@chromvoid/uikit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +96 -0
- package/dist/components/cv-accordion-item.d.ts +69 -0
- package/dist/components/cv-accordion-item.js +176 -0
- package/dist/components/cv-accordion.d.ts +79 -0
- package/dist/components/cv-accordion.js +310 -0
- package/dist/components/cv-alert-dialog.d.ts +86 -0
- package/dist/components/cv-alert-dialog.js +393 -0
- package/dist/components/cv-alert.d.ts +48 -0
- package/dist/components/cv-alert.js +156 -0
- package/dist/components/cv-badge.d.ts +56 -0
- package/dist/components/cv-badge.js +280 -0
- package/dist/components/cv-breadcrumb-item.d.ts +35 -0
- package/dist/components/cv-breadcrumb-item.js +64 -0
- package/dist/components/cv-breadcrumb.d.ts +39 -0
- package/dist/components/cv-breadcrumb.js +160 -0
- package/dist/components/cv-button.d.ts +83 -0
- package/dist/components/cv-button.js +541 -0
- package/dist/components/cv-callout.d.ts +32 -0
- package/dist/components/cv-callout.js +221 -0
- package/dist/components/cv-card.d.ts +48 -0
- package/dist/components/cv-card.js +269 -0
- package/dist/components/cv-carousel-slide.d.ts +25 -0
- package/dist/components/cv-carousel-slide.js +51 -0
- package/dist/components/cv-carousel.d.ts +96 -0
- package/dist/components/cv-carousel.js +457 -0
- package/dist/components/cv-checkbox.d.ts +84 -0
- package/dist/components/cv-checkbox.js +274 -0
- package/dist/components/cv-combobox-group.d.ts +15 -0
- package/dist/components/cv-combobox-group.js +34 -0
- package/dist/components/cv-combobox-option.d.ts +30 -0
- package/dist/components/cv-combobox-option.js +66 -0
- package/dist/components/cv-combobox.d.ts +135 -0
- package/dist/components/cv-combobox.js +834 -0
- package/dist/components/cv-command-item.d.ts +30 -0
- package/dist/components/cv-command-item.js +68 -0
- package/dist/components/cv-command-palette.d.ts +105 -0
- package/dist/components/cv-command-palette.js +578 -0
- package/dist/components/cv-context-menu.d.ts +98 -0
- package/dist/components/cv-context-menu.js +515 -0
- package/dist/components/cv-copy-button.d.ts +61 -0
- package/dist/components/cv-copy-button.js +318 -0
- package/dist/components/cv-date-picker.d.ts +161 -0
- package/dist/components/cv-date-picker.js +803 -0
- package/dist/components/cv-dialog.d.ts +89 -0
- package/dist/components/cv-dialog.js +459 -0
- package/dist/components/cv-disclosure.d.ts +57 -0
- package/dist/components/cv-disclosure.js +241 -0
- package/dist/components/cv-drawer.d.ts +102 -0
- package/dist/components/cv-drawer.js +595 -0
- package/dist/components/cv-feed-article.d.ts +26 -0
- package/dist/components/cv-feed-article.js +52 -0
- package/dist/components/cv-feed.d.ts +62 -0
- package/dist/components/cv-feed.js +310 -0
- package/dist/components/cv-grid-cell.d.ts +30 -0
- package/dist/components/cv-grid-cell.js +57 -0
- package/dist/components/cv-grid-column.d.ts +30 -0
- package/dist/components/cv-grid-column.js +43 -0
- package/dist/components/cv-grid-row.d.ts +30 -0
- package/dist/components/cv-grid-row.js +42 -0
- package/dist/components/cv-grid.d.ts +119 -0
- package/dist/components/cv-grid.js +567 -0
- package/dist/components/cv-icon.d.ts +57 -0
- package/dist/components/cv-icon.js +352 -0
- package/dist/components/cv-input.d.ts +127 -0
- package/dist/components/cv-input.js +482 -0
- package/dist/components/cv-landmark.d.ts +32 -0
- package/dist/components/cv-landmark.js +62 -0
- package/dist/components/cv-link.d.ts +22 -0
- package/dist/components/cv-link.js +99 -0
- package/dist/components/cv-listbox-group.d.ts +15 -0
- package/dist/components/cv-listbox-group.js +42 -0
- package/dist/components/cv-listbox.d.ts +81 -0
- package/dist/components/cv-listbox.js +388 -0
- package/dist/components/cv-menu-button.d.ts +118 -0
- package/dist/components/cv-menu-button.js +822 -0
- package/dist/components/cv-menu-group.d.ts +20 -0
- package/dist/components/cv-menu-group.js +48 -0
- package/dist/components/cv-menu-item.d.ts +52 -0
- package/dist/components/cv-menu-item.js +105 -0
- package/dist/components/cv-menu.d.ts +62 -0
- package/dist/components/cv-menu.js +414 -0
- package/dist/components/cv-meter.d.ts +66 -0
- package/dist/components/cv-meter.js +154 -0
- package/dist/components/cv-number.d.ts +139 -0
- package/dist/components/cv-number.js +553 -0
- package/dist/components/cv-option.d.ts +30 -0
- package/dist/components/cv-option.js +84 -0
- package/dist/components/cv-popover.d.ts +87 -0
- package/dist/components/cv-popover.js +373 -0
- package/dist/components/cv-progress-ring.d.ts +45 -0
- package/dist/components/cv-progress-ring.js +169 -0
- package/dist/components/cv-progress.d.ts +45 -0
- package/dist/components/cv-progress.js +148 -0
- package/dist/components/cv-radio-group.d.ts +79 -0
- package/dist/components/cv-radio-group.js +398 -0
- package/dist/components/cv-radio.d.ts +36 -0
- package/dist/components/cv-radio.js +123 -0
- package/dist/components/cv-select-group.d.ts +15 -0
- package/dist/components/cv-select-group.js +44 -0
- package/dist/components/cv-select-option.d.ts +30 -0
- package/dist/components/cv-select-option.js +66 -0
- package/dist/components/cv-select.d.ts +128 -0
- package/dist/components/cv-select.js +666 -0
- package/dist/components/cv-sidebar-item.d.ts +26 -0
- package/dist/components/cv-sidebar-item.js +142 -0
- package/dist/components/cv-sidebar.d.ts +171 -0
- package/dist/components/cv-sidebar.js +767 -0
- package/dist/components/cv-slider-multi-thumb.d.ts +73 -0
- package/dist/components/cv-slider-multi-thumb.js +374 -0
- package/dist/components/cv-slider.d.ts +84 -0
- package/dist/components/cv-slider.js +328 -0
- package/dist/components/cv-spinbutton.d.ts +121 -0
- package/dist/components/cv-spinbutton.js +486 -0
- package/dist/components/cv-spinner.d.ts +18 -0
- package/dist/components/cv-spinner.js +95 -0
- package/dist/components/cv-switch.d.ts +81 -0
- package/dist/components/cv-switch.js +285 -0
- package/dist/components/cv-tab-panel.d.ts +20 -0
- package/dist/components/cv-tab-panel.js +37 -0
- package/dist/components/cv-tab.d.ts +40 -0
- package/dist/components/cv-tab.js +132 -0
- package/dist/components/cv-table-cell.d.ts +31 -0
- package/dist/components/cv-table-cell.js +49 -0
- package/dist/components/cv-table-column.d.ts +37 -0
- package/dist/components/cv-table-column.js +63 -0
- package/dist/components/cv-table-row.d.ts +30 -0
- package/dist/components/cv-table-row.js +45 -0
- package/dist/components/cv-table.d.ts +147 -0
- package/dist/components/cv-table.js +607 -0
- package/dist/components/cv-tabs.d.ts +70 -0
- package/dist/components/cv-tabs.js +524 -0
- package/dist/components/cv-textarea.d.ts +108 -0
- package/dist/components/cv-textarea.js +328 -0
- package/dist/components/cv-toast-region.d.ts +39 -0
- package/dist/components/cv-toast-region.js +162 -0
- package/dist/components/cv-toast.d.ts +67 -0
- package/dist/components/cv-toast.js +315 -0
- package/dist/components/cv-toolbar-item.d.ts +25 -0
- package/dist/components/cv-toolbar-item.js +72 -0
- package/dist/components/cv-toolbar-separator.d.ts +25 -0
- package/dist/components/cv-toolbar-separator.js +45 -0
- package/dist/components/cv-toolbar.d.ts +63 -0
- package/dist/components/cv-toolbar.js +295 -0
- package/dist/components/cv-tooltip.d.ts +83 -0
- package/dist/components/cv-tooltip.js +455 -0
- package/dist/components/cv-treegrid-cell.d.ts +30 -0
- package/dist/components/cv-treegrid-cell.js +57 -0
- package/dist/components/cv-treegrid-column.d.ts +37 -0
- package/dist/components/cv-treegrid-column.js +53 -0
- package/dist/components/cv-treegrid-row.d.ts +55 -0
- package/dist/components/cv-treegrid-row.js +90 -0
- package/dist/components/cv-treegrid.d.ts +96 -0
- package/dist/components/cv-treegrid.js +632 -0
- package/dist/components/cv-treeitem.d.ts +58 -0
- package/dist/components/cv-treeitem.js +144 -0
- package/dist/components/cv-treeview.d.ts +70 -0
- package/dist/components/cv-treeview.js +396 -0
- package/dist/components/cv-window-splitter.d.ts +79 -0
- package/dist/components/cv-window-splitter.js +316 -0
- package/dist/components/index.d.ts +94 -0
- package/dist/components/index.js +79 -0
- package/dist/dialog/create-dialog-controller.d.ts +31 -0
- package/dist/dialog/create-dialog-controller.js +320 -0
- package/dist/dialog/index.d.ts +2 -0
- package/dist/dialog/index.js +1 -0
- package/dist/form-associated/FormAssociatedReatomElement.d.ts +25 -0
- package/dist/form-associated/FormAssociatedReatomElement.js +70 -0
- package/dist/form-associated/withFormAssociated.d.ts +5 -0
- package/dist/form-associated/withFormAssociated.js +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/reatom-lit/ReatomLitElement.d.ts +27 -0
- package/dist/reatom-lit/ReatomLitElement.js +118 -0
- package/dist/reatom-lit/html.d.ts +4 -0
- package/dist/reatom-lit/html.js +10 -0
- package/dist/reatom-lit/index.d.ts +4 -0
- package/dist/reatom-lit/index.js +4 -0
- package/dist/reatom-lit/watch.d.ts +15 -0
- package/dist/reatom-lit/watch.js +40 -0
- package/dist/reatom-lit/withReatomElement.d.ts +4 -0
- package/dist/reatom-lit/withReatomElement.js +57 -0
- package/dist/register.d.ts +1 -0
- package/dist/register.js +84 -0
- package/dist/styles/component-styles.d.ts +4 -0
- package/dist/styles/component-styles.js +78 -0
- package/dist/theme/cv-theme-provider.d.ts +32 -0
- package/dist/theme/cv-theme-provider.js +110 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +2 -0
- package/dist/theme/theme-engine.d.ts +4 -0
- package/dist/theme/theme-engine.js +67 -0
- package/dist/theme/tokens.css +265 -0
- package/dist/theme/types.d.ts +7 -0
- package/dist/theme/types.js +1 -0
- package/dist/toast/create-toast-controller.d.ts +12 -0
- package/dist/toast/create-toast-controller.js +12 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +1 -0
- package/package.json +146 -0
- package/specs/_template.md +110 -0
- package/specs/components/accordion.md +207 -0
- package/specs/components/alert.md +83 -0
- package/specs/components/badge.md +183 -0
- package/specs/components/breadcrumb.md +152 -0
- package/specs/components/button.md +227 -0
- package/specs/components/callout.md +153 -0
- package/specs/components/card.md +192 -0
- package/specs/components/carousel.md +232 -0
- package/specs/components/checkbox.md +141 -0
- package/specs/components/combobox.md +427 -0
- package/specs/components/context-menu.md +375 -0
- package/specs/components/copy-button.md +236 -0
- package/specs/components/date-picker.md +290 -0
- package/specs/components/dialog.md +184 -0
- package/specs/components/disclosure.md +151 -0
- package/specs/components/drawer.md +216 -0
- package/specs/components/feed.md +266 -0
- package/specs/components/grid.md +423 -0
- package/specs/components/input.md +237 -0
- package/specs/components/landmark.md +92 -0
- package/specs/components/link.md +117 -0
- package/specs/components/listbox.md +327 -0
- package/specs/components/menu.md +508 -0
- package/specs/components/meter.md +148 -0
- package/specs/components/number.md +268 -0
- package/specs/components/option.md +167 -0
- package/specs/components/popover.md +207 -0
- package/specs/components/progress-ring.md +134 -0
- package/specs/components/progress.md +110 -0
- package/specs/components/radio.md +208 -0
- package/specs/components/select.md +305 -0
- package/specs/components/sidebar.md +204 -0
- package/specs/components/spinbutton.md +157 -0
- package/specs/components/spinner.md +83 -0
- package/specs/components/switch.md +145 -0
- package/specs/components/table.md +372 -0
- package/specs/components/tabs.md +242 -0
- package/specs/components/textarea.md +166 -0
- package/specs/components/theme.md +364 -0
- package/specs/components/toast.md +198 -0
- package/specs/components/toolbar.md +258 -0
- package/specs/components/tooltip.md +152 -0
- package/specs/components/treegrid.md +363 -0
- package/specs/components/treeview.md +263 -0
- package/specs/components/window-splitter.md +225 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# cv-context-menu
|
|
2
|
+
|
|
3
|
+
Contextual menu triggered by right-click, long-press on touch, or keyboard invocation, supporting action items, checkable items (checkbox/radio), sub-menus, separators, and group labels.
|
|
4
|
+
|
|
5
|
+
**Headless:** [`createContextMenu`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/context-menu.md)
|
|
6
|
+
|
|
7
|
+
## Anatomy
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<cv-context-menu> (host)
|
|
11
|
+
├── <div part="target" tabindex="0">
|
|
12
|
+
│ └── <slot name="target">
|
|
13
|
+
└── <div part="menu" role="menu" tabindex="-1">
|
|
14
|
+
└── <slot> ← cv-context-menu-item / cv-context-menu-separator / cv-context-menu-group children
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Attributes
|
|
18
|
+
|
|
19
|
+
| Attribute | Type | Default | Description |
|
|
20
|
+
| -------------------------- | ------- | ------- | --------------------------------------------- |
|
|
21
|
+
| `value` | String | `""` | Last selected item value |
|
|
22
|
+
| `open` | Boolean | `false` | Whether the menu is currently visible |
|
|
23
|
+
| `anchor-x` | Number | `0` | X coordinate of the menu anchor point |
|
|
24
|
+
| `anchor-y` | Number | `0` | Y coordinate of the menu anchor point |
|
|
25
|
+
| `aria-label` | String | `""` | Accessible label for the menu |
|
|
26
|
+
| `close-on-select` | Boolean | `true` | Close the menu after an item is selected |
|
|
27
|
+
| `close-on-outside-pointer` | Boolean | `true` | Close the menu on pointer interaction outside |
|
|
28
|
+
|
|
29
|
+
## Slots
|
|
30
|
+
|
|
31
|
+
| Slot | Description |
|
|
32
|
+
| ----------- | ----------------------------------------------------------------------------------------- |
|
|
33
|
+
| `target` | Content that acts as the right-click/long-press target zone |
|
|
34
|
+
| `(default)` | `cv-context-menu-item`, `cv-context-menu-separator`, and `cv-context-menu-group` children |
|
|
35
|
+
|
|
36
|
+
## CSS Parts
|
|
37
|
+
|
|
38
|
+
| Part | Element | Description |
|
|
39
|
+
| -------- | ------- | ----------------------------------------------------- |
|
|
40
|
+
| `target` | `<div>` | Wrapper for the trigger/target zone |
|
|
41
|
+
| `menu` | `<div>` | Menu popup container positioned at anchor coordinates |
|
|
42
|
+
|
|
43
|
+
## CSS Custom Properties
|
|
44
|
+
|
|
45
|
+
| Property | Default | Description |
|
|
46
|
+
| ----------------------------------- | --------------------------- | ------------------------------------------------------------------------------ |
|
|
47
|
+
| `--cv-context-menu-x` | `0px` | Inline-start position of the menu popup (set programmatically from `anchor-x`) |
|
|
48
|
+
| `--cv-context-menu-y` | `0px` | Block-start position of the menu popup (set programmatically from `anchor-y`) |
|
|
49
|
+
| `--cv-context-menu-min-inline-size` | `180px` | Minimum inline size of the menu popup |
|
|
50
|
+
| `--cv-context-menu-padding` | `var(--cv-space-1, 4px)` | Padding inside the menu popup |
|
|
51
|
+
| `--cv-context-menu-gap` | `var(--cv-space-1, 4px)` | Gap between menu items |
|
|
52
|
+
| `--cv-context-menu-border-radius` | `var(--cv-radius-md, 10px)` | Border radius of the menu popup |
|
|
53
|
+
| `--cv-context-menu-z-index` | `80` | Z-index of the menu popup |
|
|
54
|
+
|
|
55
|
+
## Visual States
|
|
56
|
+
|
|
57
|
+
| Host selector | Description |
|
|
58
|
+
| --------------------- | --------------------- |
|
|
59
|
+
| `:host([open])` | Menu popup is visible |
|
|
60
|
+
| `:host(:not([open]))` | Menu popup is hidden |
|
|
61
|
+
|
|
62
|
+
## Events
|
|
63
|
+
|
|
64
|
+
| Event | Detail | Description |
|
|
65
|
+
| ----------- | ----------------------------------------------------- | ----------------------------------------------------------- |
|
|
66
|
+
| `cv-input` | `{value, activeId, open, anchorX, anchorY, openedBy}` | Fires on any state change (selection, active, open, anchor) |
|
|
67
|
+
| `cv-change` | `{value, activeId, open, anchorX, anchorY, openedBy}` | Fires only when the selected `value` changes |
|
|
68
|
+
|
|
69
|
+
Event detail type:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
interface CVContextMenuEventDetail {
|
|
73
|
+
value: string | null
|
|
74
|
+
activeId: string | null
|
|
75
|
+
open: boolean
|
|
76
|
+
anchorX: number
|
|
77
|
+
anchorY: number
|
|
78
|
+
openedBy: string | null // 'pointer' | 'keyboard' | 'programmatic' | null
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Imperative API
|
|
83
|
+
|
|
84
|
+
| Method | Signature | Description |
|
|
85
|
+
| ---------- | -------------------------------- | --------------------------------------- |
|
|
86
|
+
| `openAt` | `(x: number, y: number) => void` | Opens the menu at the given coordinates |
|
|
87
|
+
| `cv-close` | `() => void` | Closes the menu |
|
|
88
|
+
|
|
89
|
+
## Keyboard Interaction
|
|
90
|
+
|
|
91
|
+
### Target element
|
|
92
|
+
|
|
93
|
+
| Key | Action |
|
|
94
|
+
| ------------- | --------------------------------------- |
|
|
95
|
+
| `ContextMenu` | Open menu at current anchor coordinates |
|
|
96
|
+
| `Shift+F10` | Open menu at current anchor coordinates |
|
|
97
|
+
|
|
98
|
+
### Menu (when open, no sub-menu)
|
|
99
|
+
|
|
100
|
+
| Key | Action |
|
|
101
|
+
| ------------------- | --------------------------------------------------------- |
|
|
102
|
+
| `Escape` | Close menu, restore focus to target |
|
|
103
|
+
| `Tab` | Close menu, restore focus to target |
|
|
104
|
+
| `ArrowDown` | Move active to next enabled item (wrapping) |
|
|
105
|
+
| `ArrowUp` | Move active to previous enabled item (wrapping) |
|
|
106
|
+
| `ArrowRight` | If active item has a sub-menu: open it, focus first child |
|
|
107
|
+
| `Home` | Move active to first enabled item |
|
|
108
|
+
| `End` | Move active to last enabled item |
|
|
109
|
+
| `Enter` / `Space` | Select active item |
|
|
110
|
+
| Printable character | Type-ahead: move active to matching item by label prefix |
|
|
111
|
+
|
|
112
|
+
### Sub-menu (when open)
|
|
113
|
+
|
|
114
|
+
| Key | Action |
|
|
115
|
+
| ----------------- | ------------------------------------------------- |
|
|
116
|
+
| `Escape` | Close sub-menu, return to parent menu |
|
|
117
|
+
| `ArrowLeft` | Close sub-menu, return to parent menu |
|
|
118
|
+
| `ArrowDown` | Move to next enabled sub-menu item (wrapping) |
|
|
119
|
+
| `ArrowUp` | Move to previous enabled sub-menu item (wrapping) |
|
|
120
|
+
| `Home` | Move to first enabled sub-menu item |
|
|
121
|
+
| `End` | Move to last enabled sub-menu item |
|
|
122
|
+
| `Enter` / `Space` | Select active sub-menu item |
|
|
123
|
+
|
|
124
|
+
## Touch Interaction
|
|
125
|
+
|
|
126
|
+
Long-press on the target zone opens the menu at the touch coordinates after the long-press threshold (default 500ms). Touch move or touch end before the threshold cancels the long-press.
|
|
127
|
+
|
|
128
|
+
## ARIA Contract
|
|
129
|
+
|
|
130
|
+
| Element | Attribute | Value |
|
|
131
|
+
| ------- | --------------- | ------------------- |
|
|
132
|
+
| menu | `role` | `menu` |
|
|
133
|
+
| menu | `tabindex` | `-1` |
|
|
134
|
+
| menu | `aria-label` | optional label text |
|
|
135
|
+
| menu | `hidden` | reflects `!open` |
|
|
136
|
+
| menu | `data-anchor-x` | string of `anchorX` |
|
|
137
|
+
| menu | `data-anchor-y` | string of `anchorY` |
|
|
138
|
+
| target | `id` | `{idBase}-target` |
|
|
139
|
+
|
|
140
|
+
## Reactive State Mapping
|
|
141
|
+
|
|
142
|
+
`cv-context-menu` is a visual adapter over headless `createContextMenu`.
|
|
143
|
+
|
|
144
|
+
| UIKit Property | Direction | Headless Binding |
|
|
145
|
+
| -------------------------- | -------------- | ------------------------------------------------------------------------------ |
|
|
146
|
+
| `value` | attr -> action | `actions.select(value)` when value changes |
|
|
147
|
+
| `open` | attr -> action | `actions.openAt(anchorX, anchorY)` when `true`; `actions.close()` when `false` |
|
|
148
|
+
| `anchor-x` / `anchor-y` | attr -> action | passed to `actions.openAt(x, y)` |
|
|
149
|
+
| `aria-label` | attr -> option | passed as `ariaLabel` in `createContextMenu(options)` |
|
|
150
|
+
| `close-on-select` | attr -> option | passed as `closeOnSelect` in `createContextMenu(options)` |
|
|
151
|
+
| `close-on-outside-pointer` | attr -> option | passed as `closeOnOutsidePointer` in `createContextMenu(options)` |
|
|
152
|
+
|
|
153
|
+
| Headless State | Direction | DOM Reflection |
|
|
154
|
+
| ------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
155
|
+
| `state.isOpen()` | state -> attr | `[open]` host attribute, menu `[hidden]` |
|
|
156
|
+
| `state.activeId()` | state -> DOM | `[data-active]` on item elements, focus management |
|
|
157
|
+
| `state.anchorX()` / `state.anchorY()` | state -> attr | `[anchor-x]` / `[anchor-y]` host attributes, `--cv-context-menu-x` / `--cv-context-menu-y` CSS custom properties |
|
|
158
|
+
| `state.openedBy()` | state -> event | included in `cv-input`/`cv-change` event detail |
|
|
159
|
+
| `state.restoreTargetId()` | state -> DOM | focus restored to target element on close |
|
|
160
|
+
| `state.checkedIds()` | state -> DOM | `[aria-checked]` on checkbox/radio item elements |
|
|
161
|
+
| `state.openSubmenuId()` | state -> DOM | sub-menu container `[hidden]` state |
|
|
162
|
+
| `state.submenuActiveId()` | state -> DOM | `[data-active]` on sub-menu child items |
|
|
163
|
+
|
|
164
|
+
Contracts applied to DOM elements:
|
|
165
|
+
|
|
166
|
+
- `contracts.getTargetProps()` -> target wrapper (`[part="target"]`): provides `id`, `onContextMenu`, `onKeyDown`
|
|
167
|
+
- `contracts.getMenuProps()` -> menu container (`[part="menu"]`): provides `id`, `role`, `tabindex`, `hidden`, `aria-label`, `data-anchor-x`, `data-anchor-y`, `onKeyDown`
|
|
168
|
+
- `contracts.getItemProps(id)` -> each item element: provides `id`, `role`, `tabindex`, `aria-disabled`, `data-active`, `aria-checked`, `aria-haspopup`, `aria-expanded`, `onClick`
|
|
169
|
+
- `contracts.getSeparatorProps(id)` -> separator elements: provides `id`, `role`
|
|
170
|
+
- `contracts.getGroupLabelProps(id)` -> group label elements: provides `id`, `role`, `aria-label`
|
|
171
|
+
- `contracts.getSubmenuProps(id)` -> sub-menu containers: provides `id`, `role`, `tabindex`, `hidden`
|
|
172
|
+
|
|
173
|
+
UIKit does not own activation, navigation, or toggle logic; headless state is the source of truth.
|
|
174
|
+
|
|
175
|
+
## Usage
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<!-- Basic context menu -->
|
|
179
|
+
<cv-context-menu aria-label="File actions">
|
|
180
|
+
<div slot="target">Right-click here</div>
|
|
181
|
+
<cv-context-menu-item value="copy">Copy</cv-context-menu-item>
|
|
182
|
+
<cv-context-menu-item value="paste">Paste</cv-context-menu-item>
|
|
183
|
+
<cv-context-menu-item value="delete" disabled>Delete</cv-context-menu-item>
|
|
184
|
+
</cv-context-menu>
|
|
185
|
+
|
|
186
|
+
<!-- With separators and groups -->
|
|
187
|
+
<cv-context-menu aria-label="Edit actions">
|
|
188
|
+
<div slot="target">Right-click here</div>
|
|
189
|
+
<cv-context-menu-item value="cut">Cut</cv-context-menu-item>
|
|
190
|
+
<cv-context-menu-item value="copy">Copy</cv-context-menu-item>
|
|
191
|
+
<cv-context-menu-item value="paste">Paste</cv-context-menu-item>
|
|
192
|
+
<cv-context-menu-separator></cv-context-menu-separator>
|
|
193
|
+
<cv-context-menu-item value="select-all">Select All</cv-context-menu-item>
|
|
194
|
+
</cv-context-menu>
|
|
195
|
+
|
|
196
|
+
<!-- With checkbox items -->
|
|
197
|
+
<cv-context-menu aria-label="View options">
|
|
198
|
+
<div slot="target">Right-click here</div>
|
|
199
|
+
<cv-context-menu-item value="toolbar" type="checkbox" checked>Toolbar</cv-context-menu-item>
|
|
200
|
+
<cv-context-menu-item value="sidebar" type="checkbox">Sidebar</cv-context-menu-item>
|
|
201
|
+
<cv-context-menu-item value="statusbar" type="checkbox" checked>Status Bar</cv-context-menu-item>
|
|
202
|
+
</cv-context-menu>
|
|
203
|
+
|
|
204
|
+
<!-- With radio items -->
|
|
205
|
+
<cv-context-menu aria-label="Sort order">
|
|
206
|
+
<div slot="target">Right-click here</div>
|
|
207
|
+
<cv-context-menu-item value="name" type="radio" group="sort" checked>By Name</cv-context-menu-item>
|
|
208
|
+
<cv-context-menu-item value="date" type="radio" group="sort">By Date</cv-context-menu-item>
|
|
209
|
+
<cv-context-menu-item value="size" type="radio" group="sort">By Size</cv-context-menu-item>
|
|
210
|
+
</cv-context-menu>
|
|
211
|
+
|
|
212
|
+
<!-- With sub-menu -->
|
|
213
|
+
<cv-context-menu aria-label="Actions">
|
|
214
|
+
<div slot="target">Right-click here</div>
|
|
215
|
+
<cv-context-menu-item value="open">Open</cv-context-menu-item>
|
|
216
|
+
<cv-context-menu-item value="share" type="submenu">
|
|
217
|
+
Share
|
|
218
|
+
<cv-context-menu-item slot="submenu" value="email">Email</cv-context-menu-item>
|
|
219
|
+
<cv-context-menu-item slot="submenu" value="link">Copy Link</cv-context-menu-item>
|
|
220
|
+
</cv-context-menu-item>
|
|
221
|
+
</cv-context-menu>
|
|
222
|
+
|
|
223
|
+
<!-- Imperative positioning -->
|
|
224
|
+
<cv-context-menu id="my-menu" aria-label="Custom menu">
|
|
225
|
+
<div slot="target">Content area</div>
|
|
226
|
+
<cv-context-menu-item value="action1">Action 1</cv-context-menu-item>
|
|
227
|
+
</cv-context-menu>
|
|
228
|
+
<script>
|
|
229
|
+
document.getElementById('my-menu').openAt(200, 150)
|
|
230
|
+
</script>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Child Elements
|
|
234
|
+
|
|
235
|
+
### cv-context-menu-item
|
|
236
|
+
|
|
237
|
+
Actionable item within a context menu. Supports standard, checkbox, radio, and submenu types.
|
|
238
|
+
|
|
239
|
+
#### Anatomy
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
<cv-context-menu-item> (host)
|
|
243
|
+
└── <div part="base" class="item">
|
|
244
|
+
└── <slot>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Attributes
|
|
248
|
+
|
|
249
|
+
| Attribute | Type | Default | Description |
|
|
250
|
+
| ---------- | ------- | -------- | ------------------------------------------------------------------------- |
|
|
251
|
+
| `value` | String | `""` | Identifier for the item (used as selection value) |
|
|
252
|
+
| `disabled` | Boolean | `false` | Prevents selection and skips during navigation |
|
|
253
|
+
| `active` | Boolean | `false` | Reflects keyboard-active (highlighted) state (managed by parent) |
|
|
254
|
+
| `selected` | Boolean | `false` | Reflects whether this item is the last selected value (managed by parent) |
|
|
255
|
+
| `type` | String | `"item"` | Item type: `item` \| `checkbox` \| `radio` \| `submenu` |
|
|
256
|
+
| `checked` | Boolean | `false` | Initial checked state for checkbox/radio items |
|
|
257
|
+
| `group` | String | `""` | Radio group name for radio items |
|
|
258
|
+
|
|
259
|
+
#### Slots
|
|
260
|
+
|
|
261
|
+
| Slot | Description |
|
|
262
|
+
| ----------- | ------------------------------------------------------------------ |
|
|
263
|
+
| `(default)` | Item label text |
|
|
264
|
+
| `submenu` | Nested `cv-context-menu-item` children (only for `type="submenu"`) |
|
|
265
|
+
|
|
266
|
+
#### CSS Parts
|
|
267
|
+
|
|
268
|
+
| Part | Element | Description |
|
|
269
|
+
| ------ | ------- | ----------------- |
|
|
270
|
+
| `base` | `<div>` | Item root wrapper |
|
|
271
|
+
|
|
272
|
+
#### CSS Custom Properties
|
|
273
|
+
|
|
274
|
+
| Property | Default | Description |
|
|
275
|
+
| --------------------------------------- | -------------------------- | ------------------------------ |
|
|
276
|
+
| `--cv-context-menu-item-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding of the item |
|
|
277
|
+
| `--cv-context-menu-item-padding-block` | `var(--cv-space-2, 8px)` | Vertical padding of the item |
|
|
278
|
+
| `--cv-context-menu-item-border-radius` | `var(--cv-radius-sm, 6px)` | Border radius of the item |
|
|
279
|
+
|
|
280
|
+
#### Visual States
|
|
281
|
+
|
|
282
|
+
| Host selector | Description |
|
|
283
|
+
| ----------------------------------------------- | ----------------------------------------------------- |
|
|
284
|
+
| `:host([active])` | Item has keyboard focus (primary tint at 24%) |
|
|
285
|
+
| `:host([selected])` | Item is the last selected value (primary tint at 32%) |
|
|
286
|
+
| `:host([disabled])` | Item is non-selectable (opacity 0.5) |
|
|
287
|
+
| `:host([hidden])` | Item is hidden when menu is closed |
|
|
288
|
+
| `:host([type="checkbox"][aria-checked="true"])` | Checkbox item is checked |
|
|
289
|
+
| `:host([type="radio"][aria-checked="true"])` | Radio item is checked |
|
|
290
|
+
|
|
291
|
+
#### ARIA Contract
|
|
292
|
+
|
|
293
|
+
| Item type | `role` | Additional attributes |
|
|
294
|
+
| ---------------- | ------------------ | ---------------------------------------------------------------------------------------- |
|
|
295
|
+
| `item` (default) | `menuitem` | `tabindex="-1"`, `aria-disabled` (when disabled), `data-active` |
|
|
296
|
+
| `checkbox` | `menuitemcheckbox` | `tabindex="-1"`, `aria-disabled`, `data-active`, `aria-checked` |
|
|
297
|
+
| `radio` | `menuitemradio` | `tabindex="-1"`, `aria-disabled`, `data-active`, `aria-checked` |
|
|
298
|
+
| `submenu` | `menuitem` | `tabindex="-1"`, `aria-disabled`, `data-active`, `aria-haspopup="menu"`, `aria-expanded` |
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
### cv-context-menu-separator
|
|
303
|
+
|
|
304
|
+
Visual divider between groups of menu items. Not actionable, skipped during keyboard navigation.
|
|
305
|
+
|
|
306
|
+
#### Anatomy
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
<cv-context-menu-separator> (host)
|
|
310
|
+
└── <div part="base" role="separator">
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### CSS Parts
|
|
314
|
+
|
|
315
|
+
| Part | Element | Description |
|
|
316
|
+
| ------ | ------- | ---------------------- |
|
|
317
|
+
| `base` | `<div>` | Separator line element |
|
|
318
|
+
|
|
319
|
+
#### CSS Custom Properties
|
|
320
|
+
|
|
321
|
+
| Property | Default | Description |
|
|
322
|
+
| ----------------------------------- | --------------------------------- | --------------------------- |
|
|
323
|
+
| `--cv-context-menu-separator-color` | `var(--cv-color-border, #2a3245)` | Color of the separator line |
|
|
324
|
+
|
|
325
|
+
#### ARIA Contract
|
|
326
|
+
|
|
327
|
+
| Attribute | Value |
|
|
328
|
+
| --------- | ----------- |
|
|
329
|
+
| `role` | `separator` |
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### cv-context-menu-group
|
|
334
|
+
|
|
335
|
+
Labels a group of related menu items. Not actionable, skipped during keyboard navigation.
|
|
336
|
+
|
|
337
|
+
#### Anatomy
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
<cv-context-menu-group> (host)
|
|
341
|
+
├── <div part="label" role="presentation" aria-label="...">
|
|
342
|
+
└── <slot> ← cv-context-menu-item children
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Attributes
|
|
346
|
+
|
|
347
|
+
| Attribute | Type | Default | Description |
|
|
348
|
+
| --------- | ------ | ------- | ----------------------------------------------------------- |
|
|
349
|
+
| `label` | String | `""` | Group label text (set as `aria-label` on the label element) |
|
|
350
|
+
|
|
351
|
+
#### Slots
|
|
352
|
+
|
|
353
|
+
| Slot | Description |
|
|
354
|
+
| ----------- | --------------------------------------------- |
|
|
355
|
+
| `(default)` | `cv-context-menu-item` children in this group |
|
|
356
|
+
|
|
357
|
+
#### CSS Parts
|
|
358
|
+
|
|
359
|
+
| Part | Element | Description |
|
|
360
|
+
| ------- | ------- | ------------------------ |
|
|
361
|
+
| `label` | `<div>` | Group label text element |
|
|
362
|
+
|
|
363
|
+
#### CSS Custom Properties
|
|
364
|
+
|
|
365
|
+
| Property | Default | Description |
|
|
366
|
+
| ---------------------------------------------- | ------------------------- | ------------------------------------- |
|
|
367
|
+
| `--cv-context-menu-group-label-padding-inline` | `var(--cv-space-3, 12px)` | Horizontal padding of the group label |
|
|
368
|
+
| `--cv-context-menu-group-label-font-size` | `0.75em` | Font size of the group label |
|
|
369
|
+
|
|
370
|
+
#### ARIA Contract
|
|
371
|
+
|
|
372
|
+
| Attribute | Value |
|
|
373
|
+
| ------------ | ---------------- |
|
|
374
|
+
| `role` | `presentation` |
|
|
375
|
+
| `aria-label` | group label text |
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# cv-copy-button
|
|
2
|
+
|
|
3
|
+
Button that copies a value to the system clipboard with three-state visual feedback (idle, success, error).
|
|
4
|
+
|
|
5
|
+
**Headless:** [`createCopyButton`](https://github.com/chromvoid/headless-ui/blob/main/specs/components/copy-button.md)
|
|
6
|
+
|
|
7
|
+
## Anatomy
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<cv-copy-button> (host)
|
|
11
|
+
└── <div part="base" role="button">
|
|
12
|
+
├── <span part="copy-icon">
|
|
13
|
+
│ └── <slot name="copy-icon"> (default: clipboard icon)
|
|
14
|
+
├── <span part="success-icon">
|
|
15
|
+
│ └── <slot name="success-icon"> (default: check icon)
|
|
16
|
+
├── <span part="error-icon">
|
|
17
|
+
│ └── <slot name="error-icon"> (default: x icon)
|
|
18
|
+
└── <span part="status" role="status" aria-live="polite">
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Attributes
|
|
22
|
+
|
|
23
|
+
| Attribute | Type | Default | Description |
|
|
24
|
+
| ------------------- | ------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
25
|
+
| `value` | String | `''` | Text to copy. Property also accepts `(() => Promise<string>)` for lazy/sensitive values (property-only, not reflected as attribute for security) |
|
|
26
|
+
| `disabled` | Boolean | `false` | Prevents interaction |
|
|
27
|
+
| `feedback-duration` | Number | `1500` | Milliseconds to show success/error feedback before reverting to idle |
|
|
28
|
+
| `size` | String | `"medium"` | Size: `small` \| `medium` \| `large` |
|
|
29
|
+
|
|
30
|
+
## Sizes
|
|
31
|
+
|
|
32
|
+
| Size | `--cv-copy-button-size` |
|
|
33
|
+
| -------- | ----------------------- |
|
|
34
|
+
| `small` | `30px` |
|
|
35
|
+
| `medium` | `36px` |
|
|
36
|
+
| `large` | `42px` |
|
|
37
|
+
|
|
38
|
+
## Slots
|
|
39
|
+
|
|
40
|
+
| Slot | Description |
|
|
41
|
+
| -------------- | ------------------------------------------------------ |
|
|
42
|
+
| `copy-icon` | Icon shown in idle state (default: clipboard icon) |
|
|
43
|
+
| `success-icon` | Icon shown after successful copy (default: check icon) |
|
|
44
|
+
| `error-icon` | Icon shown after copy failure (default: x icon) |
|
|
45
|
+
|
|
46
|
+
## CSS Parts
|
|
47
|
+
|
|
48
|
+
| Part | Element | Description |
|
|
49
|
+
| -------------- | -------- | -------------------------------------------------- |
|
|
50
|
+
| `base` | `<div>` | Root interactive element with `role="button"` |
|
|
51
|
+
| `copy-icon` | `<span>` | Wrapper around the `copy-icon` slot |
|
|
52
|
+
| `success-icon` | `<span>` | Wrapper around the `success-icon` slot |
|
|
53
|
+
| `error-icon` | `<span>` | Wrapper around the `error-icon` slot |
|
|
54
|
+
| `status` | `<span>` | Live region for assistive technology announcements |
|
|
55
|
+
|
|
56
|
+
## CSS Custom Properties
|
|
57
|
+
|
|
58
|
+
| Property | Default | Description |
|
|
59
|
+
| -------------------------------- | ---------------------------------- | -------------------------------------- |
|
|
60
|
+
| `--cv-copy-button-size` | `36px` | Overall button size (width and height) |
|
|
61
|
+
| `--cv-copy-button-border-radius` | `var(--cv-radius-sm, 6px)` | Border radius for button shape |
|
|
62
|
+
| `--cv-copy-button-success-color` | `var(--cv-color-success, #4ade80)` | Color applied during success state |
|
|
63
|
+
| `--cv-copy-button-error-color` | `var(--cv-color-danger, #ff7d86)` | Color applied during error state |
|
|
64
|
+
|
|
65
|
+
Additionally, component styles depend on theme tokens through fallback values:
|
|
66
|
+
|
|
67
|
+
| Theme Property | Default | Description |
|
|
68
|
+
| ---------------------- | --------- | -------------------------- |
|
|
69
|
+
| `--cv-color-border` | `#2a3245` | Base border color |
|
|
70
|
+
| `--cv-color-surface` | `#141923` | Surface background color |
|
|
71
|
+
| `--cv-color-text` | `#e8ecf6` | Default text/icon color |
|
|
72
|
+
| `--cv-color-success` | `#4ade80` | Success accent color |
|
|
73
|
+
| `--cv-color-danger` | `#ff7d86` | Danger accent color |
|
|
74
|
+
| `--cv-duration-fast` | `120ms` | Transition duration |
|
|
75
|
+
| `--cv-easing-standard` | `ease` | Transition timing function |
|
|
76
|
+
| `--cv-radius-sm` | `6px` | Base radius fallback |
|
|
77
|
+
|
|
78
|
+
## Visual States
|
|
79
|
+
|
|
80
|
+
| Host selector | Description |
|
|
81
|
+
| --------------------------- | -------------------------------------------------------------------------------- |
|
|
82
|
+
| `:host([disabled])` | Reduced opacity (`0.55`), `cursor: not-allowed` |
|
|
83
|
+
| `:host([status="idle"])` | Default state; copy icon visible, success/error icons hidden |
|
|
84
|
+
| `:host([status="success"])` | Success color applied via `--cv-copy-button-success-color`; success icon visible |
|
|
85
|
+
| `:host([status="error"])` | Error color applied via `--cv-copy-button-error-color`; error icon visible |
|
|
86
|
+
| `:host([copying])` | Shown while async copy is in-flight; `cursor: progress` |
|
|
87
|
+
| `:host([size="small"])` | Small size overrides |
|
|
88
|
+
| `:host([size="large"])` | Large size overrides |
|
|
89
|
+
|
|
90
|
+
## Reactive State Mapping
|
|
91
|
+
|
|
92
|
+
`cv-copy-button` is a visual adapter over headless `createCopyButton`.
|
|
93
|
+
|
|
94
|
+
### UIKit properties to headless actions
|
|
95
|
+
|
|
96
|
+
| UIKit Property | Direction | Headless Binding |
|
|
97
|
+
| ------------------- | -------------- | ------------------------------------ |
|
|
98
|
+
| `disabled` | attr -> action | `actions.setDisabled(value)` |
|
|
99
|
+
| `feedback-duration` | attr -> action | `actions.setFeedbackDuration(value)` |
|
|
100
|
+
| `value` | prop -> action | `actions.setValue(value)` |
|
|
101
|
+
|
|
102
|
+
### Headless state to DOM reflection
|
|
103
|
+
|
|
104
|
+
| Headless State | Direction | DOM Reflection |
|
|
105
|
+
| -------------------- | ------------- | ---------------------------------------------------------------- |
|
|
106
|
+
| `state.status()` | state -> attr | `[status]` host attribute (`"idle"` \| `"success"` \| `"error"`) |
|
|
107
|
+
| `state.isDisabled()` | state -> attr | `[disabled]` host attribute |
|
|
108
|
+
| `state.isCopying()` | state -> attr | `[copying]` host attribute |
|
|
109
|
+
|
|
110
|
+
### Headless contracts to DOM elements
|
|
111
|
+
|
|
112
|
+
| Contract | Target Element | Notes |
|
|
113
|
+
| -------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
114
|
+
| `contracts.getButtonProps()` | Inner `[part="base"]` | Spread as attributes; provides `role`, `aria-disabled`, `tabindex`, `aria-label`, `onClick`, `onKeyDown`, `onKeyUp` |
|
|
115
|
+
| `contracts.getStatusProps()` | Inner `[part="status"]` | Spread as attributes; provides `role="status"`, `aria-live="polite"`, `aria-atomic="true"` |
|
|
116
|
+
| `contracts.getIconContainerProps('copy')` | Inner `[part="copy-icon"]` | Spread as attributes; provides `aria-hidden`, `hidden` |
|
|
117
|
+
| `contracts.getIconContainerProps('success')` | Inner `[part="success-icon"]` | Spread as attributes; provides `aria-hidden`, `hidden` |
|
|
118
|
+
| `contracts.getIconContainerProps('error')` | Inner `[part="error-icon"]` | Spread as attributes; provides `aria-hidden`, `hidden` |
|
|
119
|
+
|
|
120
|
+
### Headless options passed from UIKit attributes
|
|
121
|
+
|
|
122
|
+
| UIKit Attribute | Headless Option | Notes |
|
|
123
|
+
| ------------------- | ------------------ | ---------------------------------------------------------- |
|
|
124
|
+
| `value` | `value` | Property-only; accepts `string \| (() => Promise<string>)` |
|
|
125
|
+
| `feedback-duration` | `feedbackDuration` | Numeric attribute, defaults to `1500` |
|
|
126
|
+
| `disabled` | `isDisabled` | Boolean attribute |
|
|
127
|
+
| `aria-label` | `ariaLabel` | Standard ARIA labeling |
|
|
128
|
+
|
|
129
|
+
### UIKit-only concerns (not in headless)
|
|
130
|
+
|
|
131
|
+
- Icon rendering via slotted content (`copy-icon`, `success-icon`, `error-icon` slots with default SVG icons)
|
|
132
|
+
- CSS custom properties for sizing and colors (`--cv-copy-button-*`)
|
|
133
|
+
- `size` attribute controlling icon/button dimensions
|
|
134
|
+
- `cv-copy` and `cv-error` custom events dispatched on the host element
|
|
135
|
+
- Pulse/scale animation on copy activation
|
|
136
|
+
|
|
137
|
+
### Headless-owned concerns (UIKit does NOT reimplement)
|
|
138
|
+
|
|
139
|
+
- Copy cycle logic (resolve value, write to clipboard, transition status, schedule revert)
|
|
140
|
+
- Keyboard interaction (Enter on keydown, Space on keyup)
|
|
141
|
+
- Click handling
|
|
142
|
+
- ARIA attribute computation (`aria-disabled`, `tabindex`, `aria-label`)
|
|
143
|
+
- Timer management (revert timer, cancellation)
|
|
144
|
+
- `isCopying` re-entrant guard
|
|
145
|
+
|
|
146
|
+
## Events
|
|
147
|
+
|
|
148
|
+
| Event | Detail | Description |
|
|
149
|
+
| ---------- | -------------------- | -------------------------------------------------------------- |
|
|
150
|
+
| `cv-copy` | `{ value: string }` | Fired on successful clipboard write |
|
|
151
|
+
| `cv-error` | `{ error: unknown }` | Fired on clipboard write failure or async value getter failure |
|
|
152
|
+
|
|
153
|
+
Events are dispatched by the UIKit adapter by providing `onCopy` and `onError` callbacks to `createCopyButton`:
|
|
154
|
+
|
|
155
|
+
- `onCopy(value)` -> dispatches `cv-copy` with `{ detail: { value } }`
|
|
156
|
+
- `onError(error)` -> dispatches `cv-error` with `{ detail: { error } }`
|
|
157
|
+
|
|
158
|
+
## Usage
|
|
159
|
+
|
|
160
|
+
```html
|
|
161
|
+
<!-- Basic usage -->
|
|
162
|
+
<cv-copy-button value="text to copy"></cv-copy-button>
|
|
163
|
+
|
|
164
|
+
<!-- With aria-label for accessible context -->
|
|
165
|
+
<cv-copy-button value="secret123" aria-label="Copy password"></cv-copy-button>
|
|
166
|
+
|
|
167
|
+
<!-- Small size -->
|
|
168
|
+
<cv-copy-button value="hello" size="small"></cv-copy-button>
|
|
169
|
+
|
|
170
|
+
<!-- Custom feedback duration (3 seconds) -->
|
|
171
|
+
<cv-copy-button value="hello" feedback-duration="3000"></cv-copy-button>
|
|
172
|
+
|
|
173
|
+
<!-- Custom icons via slots -->
|
|
174
|
+
<cv-copy-button value="hello">
|
|
175
|
+
<svg
|
|
176
|
+
slot="copy-icon"
|
|
177
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
178
|
+
viewBox="0 0 24 24"
|
|
179
|
+
fill="none"
|
|
180
|
+
stroke="currentColor"
|
|
181
|
+
stroke-width="2"
|
|
182
|
+
stroke-linecap="round"
|
|
183
|
+
stroke-linejoin="round"
|
|
184
|
+
>
|
|
185
|
+
<path d="M12 5v14" />
|
|
186
|
+
<path d="M5 12h14" />
|
|
187
|
+
</svg>
|
|
188
|
+
<svg
|
|
189
|
+
slot="success-icon"
|
|
190
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
191
|
+
viewBox="0 0 24 24"
|
|
192
|
+
fill="none"
|
|
193
|
+
stroke="currentColor"
|
|
194
|
+
stroke-width="2"
|
|
195
|
+
stroke-linecap="round"
|
|
196
|
+
stroke-linejoin="round"
|
|
197
|
+
>
|
|
198
|
+
<circle cx="12" cy="12" r="9" />
|
|
199
|
+
<path d="m8.5 12 2.5 2.5 4.5-5" />
|
|
200
|
+
</svg>
|
|
201
|
+
<svg
|
|
202
|
+
slot="error-icon"
|
|
203
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
204
|
+
viewBox="0 0 24 24"
|
|
205
|
+
fill="none"
|
|
206
|
+
stroke="currentColor"
|
|
207
|
+
stroke-width="2"
|
|
208
|
+
stroke-linecap="round"
|
|
209
|
+
stroke-linejoin="round"
|
|
210
|
+
>
|
|
211
|
+
<path d="M12 3 3 20h18L12 3Z" />
|
|
212
|
+
<path d="M12 9v4" />
|
|
213
|
+
<path d="M12 17h.01" />
|
|
214
|
+
</svg>
|
|
215
|
+
</cv-copy-button>
|
|
216
|
+
|
|
217
|
+
<!-- Disabled -->
|
|
218
|
+
<cv-copy-button value="hello" disabled></cv-copy-button>
|
|
219
|
+
|
|
220
|
+
<!-- Async value (property-only, set via JS) -->
|
|
221
|
+
<cv-copy-button id="lazy-copy"></cv-copy-button>
|
|
222
|
+
<script>
|
|
223
|
+
document.querySelector('#lazy-copy').value = async () => {
|
|
224
|
+
const res = await fetch('/api/secret')
|
|
225
|
+
return res.text()
|
|
226
|
+
}
|
|
227
|
+
</script>
|
|
228
|
+
|
|
229
|
+
<!-- Listening for events -->
|
|
230
|
+
<cv-copy-button
|
|
231
|
+
value="hello"
|
|
232
|
+
@cv-copy="${(e) => console.log('Copied:', e.detail.value)}"
|
|
233
|
+
@cv-error="${(e) => console.error('Failed:', e.detail.error)}"
|
|
234
|
+
>
|
|
235
|
+
</cv-copy-button>
|
|
236
|
+
```
|