|
| 1 | +# ToggleGroup |
| 2 | + |
| 3 | +A group of two-state buttons that can be toggled on or off. |
| 4 | + |
| 5 | + |
| 6 | +<div class="featured-demo"> |
| 7 | + |
| 8 | +```gjs live preview no-shadow |
| 9 | +import { ToggleGroup } from 'ember-primitives'; |
| 10 | +
|
| 11 | +<template> |
| 12 | + <div class="demo"> |
| 13 | + <ToggleGroup class="toggle-group" as |t|> |
| 14 | + <t.Item @value="left" aria-label="Left align"> |
| 15 | + <AlignLeft /> |
| 16 | + </t.Item> |
| 17 | + <t.Item @value="center" aria-label="Center align"> |
| 18 | + <AlignCenter /> |
| 19 | + </t.Item> |
| 20 | + <t.Item @value="right" aria-label="Right align"> |
| 21 | + <AlignRight /> |
| 22 | + </t.Item> |
| 23 | + </ToggleGroup> |
| 24 | + </div> |
| 25 | +
|
| 26 | + <style> |
| 27 | + .demo { display: flex; justify-content: center; align-items: center;} |
| 28 | + .toggle-group { |
| 29 | + display: inline-flex; |
| 30 | + background-color: #fff; |
| 31 | + border-radius: 0.25rem; |
| 32 | + filter: drop-shadow(0 2px 2px rgba(0,0,0,0.5)); |
| 33 | + } |
| 34 | +
|
| 35 | + .toggle-group > button { |
| 36 | + background-color: white; |
| 37 | + color: #fff; |
| 38 | + height: 35px; |
| 39 | + width: 35px; |
| 40 | + display: flex; |
| 41 | + font-size: 15px; |
| 42 | + padding: 0.5rem; |
| 43 | + line-height: 1; |
| 44 | + align-items: center; |
| 45 | + justify-content: center; |
| 46 | + margin-left: 1px; |
| 47 | + border: 0; |
| 48 | + } |
| 49 | + .toggle-group > button:first-child { |
| 50 | + margin-left: 0; |
| 51 | + border-top-left-radius: 4px; |
| 52 | + border-bottom-left-radius: 4px; |
| 53 | + } |
| 54 | + .toggle-group > button:last-child { |
| 55 | + border-top-right-radius: 4px; |
| 56 | + border-bottom-right-radius: 4px; |
| 57 | + } |
| 58 | + .toggle-group > button:hover { |
| 59 | + background-color: #eee; |
| 60 | + } |
| 61 | + .toggle-group > button[aria-pressed='true'] { |
| 62 | + background-color: #ddf; |
| 63 | + color: black; |
| 64 | + } |
| 65 | + .toggle-group > button:focus { |
| 66 | + position: relative; |
| 67 | + box-shadow: 0 0 0 2px black; |
| 68 | + } |
| 69 | + </style> |
| 70 | +</template> |
| 71 | +
|
| 72 | +const AlignLeft = <template> |
| 73 | +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M288 64c0 17.7-14.3 32-32 32H32C14.3 96 0 81.7 0 64S14.3 32 32 32H256c17.7 0 32 14.3 32 32zm0 256c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H256c17.7 0 32 14.3 32 32zM0 192c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 448c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg> |
| 74 | +</template>; |
| 75 | +const AlignCenter = <template> |
| 76 | +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M352 64c0-17.7-14.3-32-32-32H128c-17.7 0-32 14.3-32 32s14.3 32 32 32H320c17.7 0 32-14.3 32-32zm96 128c0-17.7-14.3-32-32-32H32c-17.7 0-32 14.3-32 32s14.3 32 32 32H416c17.7 0 32-14.3 32-32zM0 448c0 17.7 14.3 32 32 32H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H32c-17.7 0-32 14.3-32 32zM352 320c0-17.7-14.3-32-32-32H128c-17.7 0-32 14.3-32 32s14.3 32 32 32H320c17.7 0 32-14.3 32-32z"/></svg> |
| 77 | +</template>; |
| 78 | +const AlignRight = <template> |
| 79 | +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M448 64c0 17.7-14.3 32-32 32H192c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32zm0 256c0 17.7-14.3 32-32 32H192c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32zM0 192c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 448c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg> |
| 80 | +</template>; |
| 81 | +``` |
| 82 | + |
| 83 | +</div> |
| 84 | + |
| 85 | +## Features |
| 86 | + |
| 87 | +* Full keyboard navigation |
| 88 | +* Supports horizontal / vertical orientation |
| 89 | +* Support single and multiple pressed buttons |
| 90 | +* Can be controlled or uncontrolled |
| 91 | + |
| 92 | +## Installation |
| 93 | + |
| 94 | +```bash |
| 95 | +pnpm add ember-primitives |
| 96 | +``` |
| 97 | + |
| 98 | +## Anatomy |
| 99 | + |
| 100 | +```js |
| 101 | +import { ToggleGroup } from 'ember-primitives'; |
| 102 | +``` |
| 103 | + |
| 104 | +or for non-tree-shaking environments: |
| 105 | +```js |
| 106 | +import { ToggleGroup } from 'ember-primitives/components/toggle'; |
| 107 | +``` |
| 108 | + |
| 109 | + |
| 110 | +```gjs |
| 111 | +import { ToggleGroup } from 'ember-primitives'; |
| 112 | +
|
| 113 | +<template> |
| 114 | + <ToggleGroup as |t|> |
| 115 | + <t.Item /> |
| 116 | + </ToggleGroup> |
| 117 | +</template> |
| 118 | +``` |
| 119 | + |
| 120 | +## API Reference |
| 121 | + |
| 122 | +```gjs live no-shadow |
| 123 | +import { ComponentSignature } from 'docs-app/docs-support'; |
| 124 | +
|
| 125 | +<template> |
| 126 | + <ComponentSignature @module="components/toggle-group" @name="ToggleGroup" /> |
| 127 | +</template> |
| 128 | +``` |
| 129 | + |
| 130 | +<hr> |
| 131 | + |
| 132 | +### Item |
| 133 | + |
| 134 | +```gjs live no-shadow |
| 135 | +import { ComponentSignature } from 'docs-app/docs-support'; |
| 136 | +
|
| 137 | +<template> |
| 138 | + <ComponentSignature @module="components/toggle-group" @name="ItemSignature" /> |
| 139 | +</template> |
| 140 | +``` |
| 141 | + |
| 142 | +## Accessibility |
| 143 | + |
| 144 | +Uses [roving tabindex](https://www.w3.org/TR/wai-aria-practices-1.2/examples/radio/radio.html) to manage focus movement among items using the [tabster](https://tabster.io/) library. |
| 145 | + |
| 146 | +As a caveat to using [tabster](https://tabster.io/), a "tabster-root" will need to be established separately if this component is used within a shadow-root, which escapes the parent-DOM's tabster instance. |
| 147 | + |
| 148 | +For more information, see [The Accessibility guide](/2-accessibility/intro.md). |
| 149 | + |
| 150 | +### Keyboard Interactions |
| 151 | + |
| 152 | +| key | description | |
| 153 | +| :---: | :----------- | |
| 154 | +| <kbd>Tab</kbd> | Moves focus to the first item in the group | |
| 155 | +| <kbd>Space</kbd> | Toggles the item's state | |
| 156 | +| <kbd>Enter</kbd> | Toggles the item's state | |
| 157 | +| <kbd>ArrowDown</kbd> | Moves focus to the next item in the group | |
| 158 | +| <kbd>ArrowRight</kbd> | Moves focus to the next item in the group | |
| 159 | +| <kbd>ArrowDown</kbd> | Moves focus to the previous item in the group | |
| 160 | +| <kbd>ArrowLeft</kbd> | Moves focus to the previous item in the group | |
| 161 | +| <kbd>Home</kbd> | Moves focus to the first item in the group | |
| 162 | +| <kbd>End</kbd> | Moves focus to the last item in the group | |
0 commit comments