@bridge-ui/vue 0.0.6 → 0.0.8
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/ai/AGENTS.md +31 -0
- package/ai/README.md +35 -0
- package/ai/guidelines/core.md +44 -0
- package/ai/llms.txt +38 -0
- package/ai/skills/bridge-ui-components/SKILL.md +37 -0
- package/ai/skills/bridge-ui-forms/SKILL.md +38 -0
- package/ai/skills/bridge-ui-overlays/SKILL.md +37 -0
- package/ai/skills/bridge-ui-setup/SKILL.md +38 -0
- package/bin/__tests__/ai.test.ts +319 -0
- package/bin/ai.mjs +469 -0
- package/dist/Actions/Snackbar/BridgeSnackbarAction.vue_vue_type_script_setup_true_lang.js +8 -8
- package/dist/Actions/Snackbar/BridgeSnackbarHost.vue_vue_type_script_setup_true_lang.js +5 -5
- package/dist/Actions/Snackbar/createBridgeSnackbarApi.js +17 -17
- package/dist/Adapters/I18n/index.d.ts +1 -1
- package/dist/Adapters/I18n/index.js +2 -2
- package/dist/Adapters/I18n/useI18nAdapter.d.ts +7 -3
- package/dist/Adapters/I18n/useI18nAdapter.js +12 -7
- package/dist/Adapters/Icon/useIconAdapter.d.ts +1 -1
- package/dist/Adapters/index.d.ts +1 -1
- package/dist/Adapters/index.js +3 -3
- package/dist/Components/Alert/composables/useAlert.js +17 -17
- package/dist/Components/Autocomplete/Autocomplete.js +5 -0
- package/dist/Components/Autocomplete/Autocomplete.vue.d.ts +40 -0
- package/dist/Components/Autocomplete/Autocomplete.vue_vue_type_script_setup_true_lang.js +197 -0
- package/dist/Components/Autocomplete/AutocompleteOption.js +5 -0
- package/dist/Components/Autocomplete/AutocompleteOption.vue.d.ts +4 -0
- package/dist/Components/Autocomplete/AutocompleteOption.vue_vue_type_script_setup_true_lang.js +32 -0
- package/dist/Components/Autocomplete/autocomplete.types.d.ts +285 -0
- package/dist/Components/Autocomplete/autocompleteInjectionKey.d.ts +7 -0
- package/dist/Components/Autocomplete/autocompleteInjectionKey.js +4 -0
- package/dist/Components/Autocomplete/composables/useAutocomplete.d.ts +763 -0
- package/dist/Components/Autocomplete/composables/useAutocomplete.js +386 -0
- package/dist/Components/Autocomplete/index.d.ts +4 -0
- package/dist/Components/Autocomplete/index.js +4 -0
- package/dist/Components/Avatar/composables/useAvatar.d.ts +1 -1
- package/dist/Components/Avatar/composables/useAvatar.js +13 -13
- package/dist/Components/Badge/composables/useBadge.js +8 -8
- package/dist/Components/BaseField/BaseField.js +5 -0
- package/dist/Components/BaseField/BaseField.vue.d.ts +15 -0
- package/dist/Components/BaseField/BaseField.vue_vue_type_script_setup_true_lang.js +41 -0
- package/dist/Components/BaseField/BaseFieldLabel.js +5 -0
- package/dist/Components/BaseField/BaseFieldLabel.vue.d.ts +7 -0
- package/dist/Components/BaseField/BaseFieldLabel.vue_vue_type_script_setup_true_lang.js +16 -0
- package/dist/Components/BaseField/baseField.types.d.ts +222 -0
- package/dist/Components/BaseField/composables/useBaseField.d.ts +55 -0
- package/dist/Components/BaseField/composables/useBaseField.js +131 -0
- package/dist/Components/BaseField/index.d.ts +4 -0
- package/dist/Components/BaseField/index.js +3 -0
- package/dist/Components/Button/composables/useButton.d.ts +1 -1
- package/dist/Components/Button/composables/useButton.js +14 -14
- package/dist/Components/Card/composables/useCard.js +27 -27
- package/dist/Components/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/Components/Checkbox/composables/useCheckbox.d.ts +3 -3
- package/dist/Components/Checkbox/composables/useCheckbox.js +9 -9
- package/dist/Components/Chip/Chip.vue.d.ts +1 -1
- package/dist/Components/Chip/composables/useChip.d.ts +1 -1
- package/dist/Components/Chip/composables/useChip.js +6 -6
- package/dist/Components/Divider/composables/useDivider.js +5 -5
- package/dist/Components/Drawer/Drawer.vue.d.ts +2 -2
- package/dist/Components/Drawer/composables/useDrawer.d.ts +1 -1
- package/dist/Components/Drawer/composables/useDrawer.js +23 -23
- package/dist/Components/FormControl/FormControl.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/Components/FormControl/composables/useFormControl.d.ts +13 -4
- package/dist/Components/FormControl/composables/useFormControl.js +63 -59
- package/dist/Components/FormControl/formControl.types.d.ts +6 -6
- package/dist/Components/FormControl/index.d.ts +1 -1
- package/dist/Components/FormField/FilledFormField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/Components/FormField/NotchedFormField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/Components/FormField/OutlinedFormField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/Components/FormField/StackedFormField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/Components/FormField/UnderlinedFormField.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/Components/FormField/composables/useFormField.d.ts +7 -3
- package/dist/Components/FormField/composables/useFormField.js +29 -29
- package/dist/Components/FormField/formField.types.d.ts +14 -14
- package/dist/Components/Icon/composables/useIcon.js +5 -5
- package/dist/Components/Label/composables/useLabel.js +5 -5
- package/dist/Components/Link/composables/useLink.js +9 -9
- package/dist/Components/List/composables/useList.js +6 -6
- package/dist/Components/ListItem/ListItem.vue.d.ts +1 -1
- package/dist/Components/ListItem/composables/useListItem.d.ts +1 -1
- package/dist/Components/ListItem/composables/useListItem.js +20 -20
- package/dist/Components/ListSection/composables/useListSection.js +8 -8
- package/dist/Components/Listbox/Listbox.vue.d.ts +3 -3
- package/dist/Components/Listbox/Listbox.vue_vue_type_script_setup_true_lang.js +29 -28
- package/dist/Components/Listbox/composables/useListbox.d.ts +10 -1
- package/dist/Components/Listbox/composables/useListbox.js +23 -23
- package/dist/Components/Listbox/index.d.ts +1 -0
- package/dist/Components/Listbox/index.js +3 -3
- package/dist/Components/Listbox/listbox.types.d.ts +6 -0
- package/dist/Components/Menu/Menu.vue.d.ts +2 -2
- package/dist/Components/Menu/composables/useMenu.js +43 -43
- package/dist/Components/Modal/Modal.vue.d.ts +2 -2
- package/dist/Components/Modal/composables/useModal.d.ts +1 -1
- package/dist/Components/Modal/composables/useModal.js +18 -18
- package/dist/Components/NumberField/NumberField.vue.d.ts +2 -2
- package/dist/Components/NumberField/NumberField.vue_vue_type_script_setup_true_lang.js +34 -34
- package/dist/Components/NumberField/composables/useNumberField.d.ts +4 -4
- package/dist/Components/NumberField/composables/useNumberField.js +13 -10
- package/dist/Components/OtpField/OtpField.js +5 -0
- package/dist/Components/OtpField/OtpField.vue.d.ts +31 -0
- package/dist/Components/OtpField/OtpField.vue_vue_type_script_setup_true_lang.js +62 -0
- package/dist/Components/OtpField/composables/useOtpField.d.ts +287 -0
- package/dist/Components/OtpField/composables/useOtpField.js +154 -0
- package/dist/Components/OtpField/index.d.ts +4 -0
- package/dist/Components/OtpField/index.js +3 -0
- package/dist/Components/OtpField/otpField.types.d.ts +120 -0
- package/dist/Components/PasswordField/PasswordField.vue_vue_type_script_setup_true_lang.js +23 -23
- package/dist/Components/PasswordField/composables/usePasswordField.d.ts +4 -4
- package/dist/Components/PasswordField/composables/usePasswordField.js +6 -3
- package/dist/Components/Progress/composables/useProgress.js +11 -11
- package/dist/Components/Radio/Radio.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/Components/Radio/composables/useRadio.d.ts +3 -3
- package/dist/Components/Radio/composables/useRadio.js +9 -9
- package/dist/Components/Select/Select.vue.d.ts +5 -5
- package/dist/Components/Select/Select.vue_vue_type_script_setup_true_lang.js +16 -13
- package/dist/Components/Select/composables/useSelect.d.ts +5 -4
- package/dist/Components/Select/composables/useSelect.js +167 -163
- package/dist/Components/Skeleton/composables/useSkeleton.js +5 -5
- package/dist/Components/Slider/Slider.js +5 -0
- package/dist/Components/Slider/Slider.vue.d.ts +25 -0
- package/dist/Components/Slider/Slider.vue_vue_type_script_setup_true_lang.js +113 -0
- package/dist/Components/Slider/composables/useSlider.d.ts +482 -0
- package/dist/Components/Slider/composables/useSlider.js +287 -0
- package/dist/Components/Slider/index.d.ts +4 -0
- package/dist/Components/Slider/index.js +3 -0
- package/dist/Components/Slider/slider.types.d.ts +169 -0
- package/dist/Components/Snackbar/Snackbar.vue.d.ts +2 -2
- package/dist/Components/Snackbar/Snackbar.vue_vue_type_script_setup_true_lang.js +54 -54
- package/dist/Components/Snackbar/composables/useSnackbar.js +115 -115
- package/dist/Components/Spinner/composables/useSpinner.js +11 -11
- package/dist/Components/Switch/Switch.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/Components/Switch/composables/useSwitch.d.ts +3 -3
- package/dist/Components/Switch/composables/useSwitch.js +9 -9
- package/dist/Components/Tab/composables/useTab.js +11 -11
- package/dist/Components/TabList/composables/useTabList.js +7 -7
- package/dist/Components/TabPanel/composables/useTabPanel.js +7 -7
- package/dist/Components/Tabs/Tabs.vue.d.ts +2 -2
- package/dist/Components/Tabs/composables/useTabs.js +22 -22
- package/dist/Components/TextField/TextField.vue.d.ts +1 -1
- package/dist/Components/TextField/TextField.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/Components/TextField/composables/useTextField.d.ts +4 -4
- package/dist/Components/TextField/composables/useTextField.js +2 -2
- package/dist/Components/Textarea/Textarea.vue.d.ts +1 -1
- package/dist/Components/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/Components/Textarea/composables/useTextarea.d.ts +4 -4
- package/dist/Components/Textarea/composables/useTextarea.js +10 -9
- package/dist/Components/Tooltip/composables/useTooltip.js +17 -17
- package/dist/Provider/bridgeUITypes.d.ts +4 -1
- package/dist/Provider/createBridgeUIApi.js +13 -1
- package/dist/Utils/index.d.ts +3 -3
- package/dist/Utils/index.js +11 -8
- package/dist/Utils/useBreakpoint.js +7 -7
- package/dist/augments.d.ts +29 -17
- package/dist/index.d.ts +11 -3
- package/dist/index.js +88 -80
- package/docs/README.md +56 -0
- package/docs/components/Alert.md +96 -0
- package/docs/components/Autocomplete.md +198 -0
- package/docs/components/Avatar.md +60 -0
- package/docs/components/Badge.md +47 -0
- package/docs/components/BaseField.md +126 -0
- package/docs/components/BridgeUIProvider.md +151 -0
- package/docs/components/Button.md +93 -0
- package/docs/components/Card.md +287 -0
- package/docs/components/Checkbox.md +87 -0
- package/docs/components/Divider.md +56 -0
- package/docs/components/Drawer.md +137 -0
- package/docs/components/FormControl.md +66 -0
- package/docs/components/FormField.md +71 -0
- package/docs/components/I18n.md +125 -0
- package/docs/components/Icon.md +88 -0
- package/docs/components/Label.md +31 -0
- package/docs/components/Link.md +71 -0
- package/docs/components/List.md +167 -0
- package/docs/components/Menu.md +97 -0
- package/docs/components/Modal.md +154 -0
- package/docs/components/NumberField.md +80 -0
- package/docs/components/OtpField.md +133 -0
- package/docs/components/PasswordField.md +82 -0
- package/docs/components/Progress.md +68 -0
- package/docs/components/Radio.md +91 -0
- package/docs/components/Select.md +181 -0
- package/docs/components/Skeleton.md +38 -0
- package/docs/components/Slider.md +140 -0
- package/docs/components/Snackbar.md +99 -0
- package/docs/components/Spinner.md +61 -0
- package/docs/components/Switch.md +72 -0
- package/docs/components/Tabs.md +207 -0
- package/docs/components/TextField.md +72 -0
- package/docs/components/Textarea.md +80 -0
- package/docs/components/Tooltip.md +125 -0
- package/docs/components/useBreakpoint.md +100 -0
- package/docs/components/useDialogAction.md +97 -0
- package/docs/components/useDrawerAction.md +75 -0
- package/docs/components/useModalAction.md +75 -0
- package/docs/components/useSnackbarAction.md +85 -0
- package/docs/examples/i18n-dictionary.ts +48 -0
- package/docs/examples/i18n-vue-i18n.ts +34 -0
- package/docs/examples/icon-fontawesome.ts +112 -0
- package/docs/examples/icon-heroicons.ts +57 -0
- package/docs/examples/icon-lucide.ts +57 -0
- package/docs/examples/icon-phosphor.ts +57 -0
- package/docs/examples/icon-tabler.ts +57 -0
- package/package.json +13 -5
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Autocomplete
|
|
2
|
+
|
|
3
|
+
Combobox with single/multiple value, search, async data, and optional free-solo input.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Autocomplete } from "@bridge-ui/vue/Components/Autocomplete";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<Autocomplete
|
|
17
|
+
label="Country"
|
|
18
|
+
:options="countries"
|
|
19
|
+
placeholder="Choose a country"
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
<Autocomplete
|
|
23
|
+
multiple
|
|
24
|
+
searchable
|
|
25
|
+
label="Frameworks"
|
|
26
|
+
v-model="selected"
|
|
27
|
+
:options="frameworks"
|
|
28
|
+
/>
|
|
29
|
+
|
|
30
|
+
<Autocomplete
|
|
31
|
+
error
|
|
32
|
+
label="Country"
|
|
33
|
+
error-message="Please select a valid country."
|
|
34
|
+
/>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Searchable
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<Autocomplete
|
|
41
|
+
searchable
|
|
42
|
+
label="Country"
|
|
43
|
+
:options="countries"
|
|
44
|
+
placeholder="Type to filter..."
|
|
45
|
+
/>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Free solo
|
|
49
|
+
|
|
50
|
+
Enabled by default. Type a value that is not in `options` and commit with Enter, Tab, or by closing the menu. Set `:free-solo="false"` to require picking from the list.
|
|
51
|
+
|
|
52
|
+
```vue
|
|
53
|
+
<Autocomplete label="Tag" :options="tags" placeholder="Pick or type a tag" />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Async data
|
|
57
|
+
|
|
58
|
+
```vue
|
|
59
|
+
<Autocomplete
|
|
60
|
+
label="City (async)"
|
|
61
|
+
placeholder="Type to search cities..."
|
|
62
|
+
:async-data="{
|
|
63
|
+
search: asyncSearch,
|
|
64
|
+
resolve: asyncResolveSelected,
|
|
65
|
+
}"
|
|
66
|
+
/>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Custom option slot
|
|
70
|
+
|
|
71
|
+
```vue
|
|
72
|
+
<Autocomplete
|
|
73
|
+
label="Framework"
|
|
74
|
+
:options="frameworks"
|
|
75
|
+
placeholder="Custom rendered options"
|
|
76
|
+
>
|
|
77
|
+
<template #option="{ option, selected }">
|
|
78
|
+
<span class="flex w-full items-center justify-between gap-2">
|
|
79
|
+
<span class="truncate font-medium">{{ option.label }}</span>
|
|
80
|
+
|
|
81
|
+
<span v-if="selected" class="text-primary-600 shrink-0 text-xs font-semibold">
|
|
82
|
+
Selected
|
|
83
|
+
</span>
|
|
84
|
+
</span>
|
|
85
|
+
</template>
|
|
86
|
+
</Autocomplete>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Grouped options
|
|
90
|
+
|
|
91
|
+
`options` may mix standalone options and section groups (`{ title, options, sticky? }`). Search filters within sections and drops empty ones.
|
|
92
|
+
|
|
93
|
+
```vue
|
|
94
|
+
<Autocomplete
|
|
95
|
+
searchable
|
|
96
|
+
label="Produce"
|
|
97
|
+
:options="[
|
|
98
|
+
{
|
|
99
|
+
title: 'Fruits',
|
|
100
|
+
sticky: true,
|
|
101
|
+
options: [
|
|
102
|
+
{ label: 'Apple', value: 'apple' },
|
|
103
|
+
{ label: 'Banana', value: 'banana' },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
title: 'Vegetables',
|
|
108
|
+
options: [{ label: 'Carrot', value: 'carrot' }],
|
|
109
|
+
},
|
|
110
|
+
{ label: 'Other', value: 'other' },
|
|
111
|
+
]"
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Composed list (default slot)
|
|
116
|
+
|
|
117
|
+
Use the default slot with `ListSection` / `ListItem` to build the dropdown list manually. Set `value` on each `ListItem` so it registers as a selectable option. Declarative `AutocompleteOption` children still feed the `options` data path; only `ListSection` / `ListItem` (and similar) count as composed list content. The `#option` slot remains for customizing items rendered from the `options` prop.
|
|
118
|
+
|
|
119
|
+
```vue
|
|
120
|
+
<Autocomplete label="Status" v-model="status">
|
|
121
|
+
<ListSection sticky title="Workflow" />
|
|
122
|
+
<ListItem value="open" primary="Open" />
|
|
123
|
+
<ListItem value="closed" primary="Closed" />
|
|
124
|
+
</Autocomplete>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### customProps
|
|
128
|
+
|
|
129
|
+
```vue
|
|
130
|
+
<Autocomplete
|
|
131
|
+
label="Country"
|
|
132
|
+
:options="countries"
|
|
133
|
+
placeholder="Choose a country"
|
|
134
|
+
:custom-props="{
|
|
135
|
+
root: { id: 'country-field' },
|
|
136
|
+
input: { name: 'country', autocomplete: 'country' },
|
|
137
|
+
}"
|
|
138
|
+
/>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Props
|
|
142
|
+
|
|
143
|
+
### Autocomplete-specific
|
|
144
|
+
|
|
145
|
+
| Prop | Type | Default | Description |
|
|
146
|
+
| ------------------- | --------------------- | ------------- | ------------------------------------------------------------------------------------------------------- |
|
|
147
|
+
| `asyncData` | `SelectAsyncData` | — | Remote data source. Implies `searchable`. |
|
|
148
|
+
| `clearable` | `boolean` | `true` | Whether the value can be cleared. |
|
|
149
|
+
| `defaultValue` | `SelectModel` | — | Initial value when uncontrolled. |
|
|
150
|
+
| `disableMaxHeight` | `boolean` | `false` | When true, the dropdown options list is not height-limited. Forwarded to the internal `Listbox`. |
|
|
151
|
+
| `emptyMessage` | `string` | "No options" | Message when the filtered list is empty. |
|
|
152
|
+
| `flipOptions` | `boolean` | `false` | Inverts the visual order of options. |
|
|
153
|
+
| `freeSolo` | `boolean` | `true` | Allows committing typed text that is not in `options` (Enter, Tab, or closing the menu). |
|
|
154
|
+
| `hideEmptyMessage` | `boolean` | `false` | Hides the empty-state message. |
|
|
155
|
+
| `loading` | `boolean` | — | External or async loading state. |
|
|
156
|
+
| `maxHeight` | `string` | "max-h-60" | Tailwind max-height class for the dropdown options area. Forwarded to the internal `Listbox`. |
|
|
157
|
+
| `minItemsForSearch` | `number` | 11 | Minimum option count before search UI is enabled. |
|
|
158
|
+
| `multiple` | `boolean` | `false` | Whether multiple values can be selected. |
|
|
159
|
+
| `optionDescription` | `string` | "description" | Key used to read the description from option objects. |
|
|
160
|
+
| `optionLabel` | `string` | "label" | Key used to read the label from option objects. |
|
|
161
|
+
| `options` | `ListboxOptionsInput` | — | Options to display. May include section groups (`{ title, options, sticky? }`) mixed with flat options. |
|
|
162
|
+
| `optionValue` | `string` | "value" | Key used to read the value from option objects. |
|
|
163
|
+
| `placeholder` | `string` | — | Placeholder shown when no value is selected. |
|
|
164
|
+
| `searchable` | `boolean` | `true` | Whether options can be filtered via the trigger input. |
|
|
165
|
+
|
|
166
|
+
### v-model
|
|
167
|
+
|
|
168
|
+
| Prop / Event | Type | Default | Description |
|
|
169
|
+
| ------------------- | ------------------------------------------------------- | ------- | ---------------------------------------------------------------------------- |
|
|
170
|
+
| `modelValue` | `SelectValue \| SelectValue[] \| null` | — | Bound with `v-model`. |
|
|
171
|
+
| `update:modelValue` | `(value: SelectValue \| SelectValue[] \| null) => void` | — | Emitted when `v-model` should update. Listen with `v-on:update:model-value`. |
|
|
172
|
+
|
|
173
|
+
### Inherited from FormField
|
|
174
|
+
|
|
175
|
+
See [FormField](./FormField.md) (building-block chrome). Field tokens live on `components.Autocomplete` (`size`, `color`, `rounded`, `variant`, …). Dropdown option tokens are nested under `components.Autocomplete.tokens.listbox`.
|
|
176
|
+
|
|
177
|
+
## Slots
|
|
178
|
+
|
|
179
|
+
| Slot | Scope | Description |
|
|
180
|
+
| --------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
181
|
+
| `default` | — | Composed list content (`ListSection` / `ListItem` with `value`). Replaces mapped `options` in the listbox. |
|
|
182
|
+
| `option` | `{ option: SelectOption; selected: boolean }` | Custom option content when rendering from the `options` prop. |
|
|
183
|
+
|
|
184
|
+
## Events
|
|
185
|
+
|
|
186
|
+
| Event | Payload | Description |
|
|
187
|
+
| --------------- | ------------------------ | ----------------------------------------------------- |
|
|
188
|
+
| `v-on:change` | `(value: SelectModel)` | Emitted when the selection changes. |
|
|
189
|
+
| `v-on:clear` | — | Emitted when the value is cleared. |
|
|
190
|
+
| `v-on:close` | — | Emitted when the menu closes. |
|
|
191
|
+
| `v-on:deselect` | `(option: SelectOption)` | Emitted when an option is deselected (multiple mode). |
|
|
192
|
+
| `v-on:open` | — | Emitted when the menu opens. |
|
|
193
|
+
| `v-on:search` | `(query: string)` | Emitted when the search query changes. |
|
|
194
|
+
| `v-on:select` | `(option: SelectOption)` | Emitted when an option is selected. |
|
|
195
|
+
|
|
196
|
+
## Related components
|
|
197
|
+
|
|
198
|
+
Menu, List, ListItem, ListSection, FormField
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Avatar
|
|
2
|
+
|
|
3
|
+
Displays a user image, initials fallback, or icon.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Avatar } from "@bridge-ui/vue/Components/Avatar";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<Avatar fallback="JD" />
|
|
17
|
+
|
|
18
|
+
<Avatar :icon="User" color="primary" />
|
|
19
|
+
|
|
20
|
+
<Avatar alt="Jane Doe" :src="avatarSrc" />
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Classes
|
|
24
|
+
|
|
25
|
+
```vue
|
|
26
|
+
<Avatar
|
|
27
|
+
fallback="JP"
|
|
28
|
+
:classes="{
|
|
29
|
+
root: 'ring-2 ring-info-500',
|
|
30
|
+
fallback: 'font-bold tracking-widest',
|
|
31
|
+
}"
|
|
32
|
+
/>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Fallback slot
|
|
36
|
+
|
|
37
|
+
```vue
|
|
38
|
+
<Avatar>
|
|
39
|
+
<template #fallback>
|
|
40
|
+
<span class="text-xs font-semibold">Slot</span>
|
|
41
|
+
</template>
|
|
42
|
+
</Avatar>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Props
|
|
46
|
+
|
|
47
|
+
| Prop | Type | Default | Description |
|
|
48
|
+
| ---------- | --------------- | --------- | -------------------------------------------------------- |
|
|
49
|
+
| `alt` | `string` | — | The alt text for the avatar image. |
|
|
50
|
+
| `classes` | `AvatarClasses` | — | The classes to apply to the avatar. |
|
|
51
|
+
| `color` | `AvatarColor` | "primary" | The color to apply to the avatar fallback. |
|
|
52
|
+
| `fallback` | `string` | — | The fallback text to display when no image is available. |
|
|
53
|
+
| `icon` | `LucideIcon` | — | The icon to display as fallback. |
|
|
54
|
+
| `rounded` | `AvatarRounded` | "full" | The roundedness of the avatar. |
|
|
55
|
+
| `size` | `AvatarSize` | "md" | The size of the avatar. |
|
|
56
|
+
| `src` | `string` | — | The source URL for the avatar image. |
|
|
57
|
+
|
|
58
|
+
## Related components
|
|
59
|
+
|
|
60
|
+
Badge
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Badge
|
|
2
|
+
|
|
3
|
+
Small label for counts, status, or tags.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Badge } from "@bridge-ui/vue/Components/Badge";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<Badge>New</Badge>
|
|
17
|
+
|
|
18
|
+
<Badge density="mini">3</Badge>
|
|
19
|
+
|
|
20
|
+
<Badge variant="flat" color="success">
|
|
21
|
+
Active
|
|
22
|
+
</Badge>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Custom classes
|
|
26
|
+
|
|
27
|
+
```vue
|
|
28
|
+
<Badge color="primary" class="tracking-widest uppercase shadow-md">
|
|
29
|
+
Styled
|
|
30
|
+
</Badge>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
| Prop | Type | Default | Description |
|
|
36
|
+
| --------- | -------------- | --------- | ---------------------------------- |
|
|
37
|
+
| `classes` | `BadgeClasses` | — | The classes to apply to the badge. |
|
|
38
|
+
| `color` | `BadgeColor` | "primary" | The color to apply to the badge. |
|
|
39
|
+
| `density` | `BadgeDensity` | "default" | The density of the badge. |
|
|
40
|
+
| `full` | `boolean` | `false` | Whether the badge is full width. |
|
|
41
|
+
| `rounded` | `BadgeRounded` | "md" | The roundedness of the badge. |
|
|
42
|
+
| `size` | `BadgeSize` | "sm" | The size of the badge. |
|
|
43
|
+
| `variant` | `BadgeVariant` | "flat" | The variant of the badge. |
|
|
44
|
+
|
|
45
|
+
## Related components
|
|
46
|
+
|
|
47
|
+
Button
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# BaseField
|
|
2
|
+
|
|
3
|
+
> Building block for composite field chrome. Prefer `OtpField` / `Slider` in apps; BaseField remains exported for advanced composition. Not a registry key — theme chrome via `tokens.baseField` on the public parent (e.g. `components.Slider.tokens.baseField`).
|
|
4
|
+
|
|
5
|
+
Shared vertical field chrome (label, corner, start/end slots, description,
|
|
6
|
+
error message) for OtpField, Slider, and similar controls.
|
|
7
|
+
|
|
8
|
+
Control tokens stay on the parent (`tokens.size`, `tokens.invalidated`, …).
|
|
9
|
+
|
|
10
|
+
## Import
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { BaseField, useBaseField } from "@bridge-ui/vue";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Examples
|
|
17
|
+
|
|
18
|
+
### Usage
|
|
19
|
+
|
|
20
|
+
Compose with `useBaseField` and pass the returned API as `:field`:
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
import { BaseField, useBaseField } from "@bridge-ui/vue";
|
|
25
|
+
|
|
26
|
+
const field = useBaseField(() => ({
|
|
27
|
+
label: "Verification code",
|
|
28
|
+
description: "Enter the code from your email.",
|
|
29
|
+
}));
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<BaseField :field="field">
|
|
34
|
+
<input type="text" aria-label="Code" />
|
|
35
|
+
</BaseField>
|
|
36
|
+
</template>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Label and corner
|
|
40
|
+
|
|
41
|
+
```vue
|
|
42
|
+
<BaseField
|
|
43
|
+
:field="
|
|
44
|
+
useBaseField(() => ({
|
|
45
|
+
label: 'Amount',
|
|
46
|
+
corner: 'Optional',
|
|
47
|
+
}))
|
|
48
|
+
"
|
|
49
|
+
>
|
|
50
|
+
<input type="number" aria-label="Amount" />
|
|
51
|
+
</BaseField>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Adornment slots
|
|
55
|
+
|
|
56
|
+
```vue
|
|
57
|
+
<BaseField :field="useBaseField(() => ({ label: 'Code' }))">
|
|
58
|
+
<template #start>
|
|
59
|
+
<Lock aria-hidden="true" />
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<input type="text" aria-label="Code" />
|
|
63
|
+
|
|
64
|
+
<template #end>
|
|
65
|
+
<button type="button">Resend</button>
|
|
66
|
+
</template>
|
|
67
|
+
</BaseField>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Error state
|
|
71
|
+
|
|
72
|
+
```vue
|
|
73
|
+
<BaseField
|
|
74
|
+
:field="
|
|
75
|
+
useBaseField(() => ({
|
|
76
|
+
error: true,
|
|
77
|
+
label: 'Email',
|
|
78
|
+
errorMessage: 'Invalid email address.',
|
|
79
|
+
}))
|
|
80
|
+
"
|
|
81
|
+
>
|
|
82
|
+
<input type="email" aria-label="Email" />
|
|
83
|
+
</BaseField>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### customProps
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<BaseField
|
|
90
|
+
:field="
|
|
91
|
+
useBaseField(() => ({
|
|
92
|
+
label: 'Email',
|
|
93
|
+
description: 'We never share your email.',
|
|
94
|
+
customProps: {
|
|
95
|
+
group: { 'data-testid': 'email-group' },
|
|
96
|
+
},
|
|
97
|
+
}))
|
|
98
|
+
"
|
|
99
|
+
>
|
|
100
|
+
<input type="email" aria-label="Email" />
|
|
101
|
+
</BaseField>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Props
|
|
105
|
+
|
|
106
|
+
| Prop | Type | Default | Description |
|
|
107
|
+
| ------------------ | ---------------------- | ------- | -------------------------------------------------------------------------------------------- |
|
|
108
|
+
| `classes` | `BaseFieldClasses` | — | Classes for the field chrome and control group layout. |
|
|
109
|
+
| `controlId` | `string` | auto | Id for the control group and related labels. |
|
|
110
|
+
| `corner` | `string` | — | Secondary header text at the inline end. |
|
|
111
|
+
| `customProps` | `BaseFieldCustomProps` | — | Extra props for internal parts (`root`, `group`, `label`, …). |
|
|
112
|
+
| `description` | `string` | — | Helper text below the control group (hidden when invalid). |
|
|
113
|
+
| `disabled` | `boolean` | `false` | Disables the field control group. |
|
|
114
|
+
| `error` | `boolean` | `false` | Invalid styling on the label and control group. |
|
|
115
|
+
| `errorMessage` | `string` | — | Error message below the control group. |
|
|
116
|
+
| `field` | `UseBaseFieldReturn` | — | Pre-composed field chrome API from `useBaseField`. Used by `<BaseField :field="…" />`. |
|
|
117
|
+
| `hideErrorMessage` | `boolean` | `false` | Hides the error message row and reserved space. |
|
|
118
|
+
| `label` | `string` | — | Primary label above the control group. |
|
|
119
|
+
| `readonly` | `boolean` | `false` | Read-only field control group. |
|
|
120
|
+
| `required` | `boolean` | `false` | Shows a required asterisk on the label. |
|
|
121
|
+
| `size` | `BaseFieldSize` | `"md"` | Label typography and control group gap scale. |
|
|
122
|
+
| `slots` | `BaseFieldSlots` | — | `#label`, `#corner`, `#description`, `#errorMessage`, `#start`, `#end`, and default control. |
|
|
123
|
+
|
|
124
|
+
## Related components
|
|
125
|
+
|
|
126
|
+
OtpField, Slider, Label
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# BridgeUIProvider
|
|
2
|
+
|
|
3
|
+
Root provider for theme, locale, direction, breakpoints, icon/i18n adapters, and component registry defaults.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { BridgeUIProvider, useBridgeUI } from "@bridge-ui/vue";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<BridgeUIProvider
|
|
17
|
+
:global="{
|
|
18
|
+
theme: 'light',
|
|
19
|
+
locale: 'en-US',
|
|
20
|
+
breakpoints: {},
|
|
21
|
+
direction: 'ltr',
|
|
22
|
+
mobileBreakpoint: 'sm',
|
|
23
|
+
}"
|
|
24
|
+
>
|
|
25
|
+
<App />
|
|
26
|
+
</BridgeUIProvider>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Icon adapter
|
|
30
|
+
|
|
31
|
+
Provide `global.icons` when using semantic icon names (`"clear"`, `"check"`, …). Optional `normalize` converts library-native values (e.g. Font Awesome definitions) so `<Icon :icon="faCoffee" />` works. Ready samples in `packages/vue/docs/examples/` (Lucide, Heroicons, Tabler, Phosphor, Font Awesome).
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { createBridgeUI } from "@bridge-ui/vue";
|
|
35
|
+
import { createLucideIconAdapter } from "@examples/icon-lucide";
|
|
36
|
+
|
|
37
|
+
const icons = createLucideIconAdapter();
|
|
38
|
+
|
|
39
|
+
app.use(
|
|
40
|
+
createBridgeUI({
|
|
41
|
+
global: { icons },
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### i18n adapter
|
|
47
|
+
|
|
48
|
+
Provide `global.i18n` to translate Bridge chrome strings (`"Close"`, `"Hide password"`, …). Lookup is gettext-style (source English text is the key). Without an adapter, the source string is used. `setLocale` updates Bridge `locale` and calls optional `i18n.setLocale` (vue-i18n / i18next / dictionary). Persistence (localStorage, backend) stays in the app. Ready samples in `packages/vue/docs/examples/` (dictionary, vue-i18n). See [I18n](./I18n.md).
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { createBridgeUI } from "@bridge-ui/vue";
|
|
52
|
+
import { createDictionaryI18nAdapter } from "@examples/i18n-dictionary";
|
|
53
|
+
|
|
54
|
+
const i18n = createDictionaryI18nAdapter();
|
|
55
|
+
|
|
56
|
+
app.use(
|
|
57
|
+
createBridgeUI({
|
|
58
|
+
global: { i18n },
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Form density defaults
|
|
64
|
+
|
|
65
|
+
Set `global.formDefaults` to apply shared `size` / `rounded` to form controls (TextField, Select, Checkbox, Slider, OtpField, …). Does not affect Button, Progress, Modal, etc.
|
|
66
|
+
|
|
67
|
+
Merge order: instance props → `components.{Name}.defaultProps` → `formDefaults` → library defaults.
|
|
68
|
+
|
|
69
|
+
Radio and Switch receive `size` only — their `rounded` stays shape-driven (`full`) unless overridden per component.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { createBridgeUI } from "@bridge-ui/vue";
|
|
73
|
+
|
|
74
|
+
app.use(
|
|
75
|
+
createBridgeUI({
|
|
76
|
+
global: {
|
|
77
|
+
formDefaults: { size: "lg", rounded: "md" },
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Nested chrome tokens
|
|
84
|
+
|
|
85
|
+
Building blocks (`FormField`, `FormControl`, `BaseField`, `Listbox`) are not registry keys. Theme chrome under the public parent:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
app.use(
|
|
89
|
+
createBridgeUI({
|
|
90
|
+
components: {
|
|
91
|
+
Slider: {
|
|
92
|
+
tokens: {
|
|
93
|
+
baseField: {
|
|
94
|
+
size: { md: { text: "text-base", group: "gap-3" } },
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
Checkbox: {
|
|
99
|
+
tokens: {
|
|
100
|
+
formControl: {
|
|
101
|
+
invalidated: {
|
|
102
|
+
errorMessage: "text-error-700 dark:text-error-300",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
Select: {
|
|
108
|
+
tokens: {
|
|
109
|
+
listbox: {
|
|
110
|
+
size: {
|
|
111
|
+
md: {
|
|
112
|
+
option: "px-3 py-2 text-sm",
|
|
113
|
+
check: "size-4",
|
|
114
|
+
message: "text-xs",
|
|
115
|
+
primary: "font-medium",
|
|
116
|
+
secondary: "text-xs opacity-70",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Runtime updates
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const { setLocale, setTheme, setDirection, setGlobal } = useBridgeUI()!;
|
|
131
|
+
|
|
132
|
+
setTheme("dark");
|
|
133
|
+
setLocale("pt-BR"); // also calls global.i18n.setLocale when present
|
|
134
|
+
setDirection("rtl");
|
|
135
|
+
setGlobal({ mobileBreakpoint: "md" });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Props
|
|
139
|
+
|
|
140
|
+
| Prop | Type | Default | Description |
|
|
141
|
+
| ------------ | -------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| `components` | `BridgeUIComponentsConfig` | — | Per-component defaults |
|
|
143
|
+
| `global` | `Partial<BridgeUIGlobal>` | — | `theme`, `locale`, `direction`, `mobileBreakpoint`, `breakpoints`, `icons`, `i18n`, `formDefaults` |
|
|
144
|
+
|
|
145
|
+
App content is passed via the **default slot** (see Usage above).
|
|
146
|
+
|
|
147
|
+
**useBridgeUI():** `global`, `components`, `setGlobal`, `setComponents`, `setLocale`, `setTheme`, `setDirection`
|
|
148
|
+
|
|
149
|
+
## Related components
|
|
150
|
+
|
|
151
|
+
All components
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Button
|
|
2
|
+
|
|
3
|
+
Primary action control. Renders as `button`, `a`, or `span` via the `as` prop.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Button } from "@bridge-ui/vue/Components/Button";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
### Usage
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<Button color="primary">Click me</Button>
|
|
17
|
+
|
|
18
|
+
<Button variant="outline" :start-icon="Plus">
|
|
19
|
+
With icon
|
|
20
|
+
</Button>
|
|
21
|
+
|
|
22
|
+
<Button as="a" href="https://example.com">
|
|
23
|
+
External link
|
|
24
|
+
</Button>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Density
|
|
28
|
+
|
|
29
|
+
```vue
|
|
30
|
+
<Button density="mini" :icon="Settings" aria-label="Settings" />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### States
|
|
34
|
+
|
|
35
|
+
```vue
|
|
36
|
+
<Button loading>Loading</Button>
|
|
37
|
+
<Button disabled>Disabled</Button>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Slots
|
|
41
|
+
|
|
42
|
+
```vue
|
|
43
|
+
<Button>
|
|
44
|
+
<template #start>
|
|
45
|
+
<span class="text-xs opacity-80">◀</span>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
With slots
|
|
49
|
+
|
|
50
|
+
<template #end>
|
|
51
|
+
<span class="text-xs opacity-80">▶</span>
|
|
52
|
+
</template>
|
|
53
|
+
</Button>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### customProps
|
|
57
|
+
|
|
58
|
+
```vue
|
|
59
|
+
<Button
|
|
60
|
+
:icon="Plus"
|
|
61
|
+
:custom-props="{
|
|
62
|
+
startIcon: { 'aria-hidden': true },
|
|
63
|
+
root: { id: 'add-btn', type: 'button' },
|
|
64
|
+
}"
|
|
65
|
+
>
|
|
66
|
+
Add item
|
|
67
|
+
</Button>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Props
|
|
71
|
+
|
|
72
|
+
| Prop | Type | Default | Description |
|
|
73
|
+
| ------------- | --------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
74
|
+
| `as` | `"a" \| "span" \| "button"` | "button" | The element to render as. |
|
|
75
|
+
| `classes` | `ButtonClasses` | — | The classes to apply to the button. |
|
|
76
|
+
| `color` | `ButtonColor` | "primary" | The color to apply to the button. |
|
|
77
|
+
| `customProps` | `ButtonCustomProps` | — | Extra props for internal parts (`startIcon`, `endIcon`, slot wrappers, etc.). Root HTML attributes stay on the component top level. |
|
|
78
|
+
| `density` | `ButtonDensity` | "default" | The density of the button. |
|
|
79
|
+
| `disabled` | `boolean` | `false` | Whether the button is disabled. |
|
|
80
|
+
| `endIcon` | `LucideIcon` | — | Icon at the **inline end** (physical right in `ltr`, physical left in `rtl`). |
|
|
81
|
+
| `full` | `boolean` | `false` | Whether the button is full width. |
|
|
82
|
+
| `href` | `string` | — | The href to apply to the button. |
|
|
83
|
+
| `icon` | `LucideIcon` | — | Icon for mini density (replaces label and start/end icons). |
|
|
84
|
+
| `loading` | `boolean` | `false` | Whether the button is loading. |
|
|
85
|
+
| `rounded` | `ButtonRounded` | "md" | The roundedness of the button. |
|
|
86
|
+
| `size` | `ButtonSize` | "md" | The size of the button. |
|
|
87
|
+
| `startIcon` | `LucideIcon` | — | Icon at the **inline start** (physical left in `ltr`, physical right in `rtl`). |
|
|
88
|
+
| `text` | `string` | — | Label text when the default slot is not used. |
|
|
89
|
+
| `variant` | `ButtonVariant` | "solid" | The variant of the button. |
|
|
90
|
+
|
|
91
|
+
## Related components
|
|
92
|
+
|
|
93
|
+
Link, Icon
|