vectis-ui 0.15.0 → 0.17.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.
Files changed (216) hide show
  1. package/dist/components/VAccordion/VAccordion.css +1 -1
  2. package/dist/components/VAccordion/VAccordion.d.ts +0 -6
  3. package/dist/components/VAccordion/VAccordion.js.map +1 -1
  4. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js +3 -8
  5. package/dist/components/VAccordion/VAccordion.vue_vue_type_script_setup_true_lang.js.map +1 -1
  6. package/dist/components/VAccordion/context.js.map +1 -1
  7. package/dist/components/VAvatar/VAvatar.d.ts +0 -3
  8. package/dist/components/VAvatar/VAvatar.js.map +1 -1
  9. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js +21 -27
  10. package/dist/components/VAvatar/VAvatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
  11. package/dist/components/VAvatar/VAvatarGroup.d.ts +0 -6
  12. package/dist/components/VAvatar/VAvatarGroup.js.map +1 -1
  13. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js +5 -19
  14. package/dist/components/VAvatar/VAvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  15. package/dist/components/VAvatar/context.d.ts +0 -1
  16. package/dist/components/VAvatar/context.js.map +1 -1
  17. package/dist/components/VButton/VButton.d.ts +0 -6
  18. package/dist/components/VButton/VButton.js.map +1 -1
  19. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js +11 -17
  20. package/dist/components/VButton/VButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  21. package/dist/components/VButton/VButtonGroup.d.ts +0 -6
  22. package/dist/components/VButton/VButtonGroup.js.map +1 -1
  23. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js +0 -7
  24. package/dist/components/VButton/VButtonGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  25. package/dist/components/VButton/context.d.ts +0 -2
  26. package/dist/components/VButton/context.js.map +1 -1
  27. package/dist/components/VChip/VChip.d.ts +0 -3
  28. package/dist/components/VChip/VChip.js.map +1 -1
  29. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js +0 -6
  30. package/dist/components/VChip/VChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
  31. package/dist/components/VColorInput/VColorInput.d.ts +0 -3
  32. package/dist/components/VColorInput/VColorInput.js.map +1 -1
  33. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js +60 -66
  34. package/dist/components/VColorInput/VColorInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  35. package/dist/components/VColorPicker/color.js +2 -2
  36. package/dist/components/VColorPicker/color.js.map +1 -1
  37. package/dist/components/VCombobox/VCombobox.d.ts +10 -3
  38. package/dist/components/VCombobox/VCombobox.js.map +1 -1
  39. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js +170 -163
  40. package/dist/components/VCombobox/VCombobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
  41. package/dist/components/VContextMenu/VContextMenu.d.ts +0 -3
  42. package/dist/components/VContextMenu/VContextMenu.js.map +1 -1
  43. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js +0 -6
  44. package/dist/components/VContextMenu/VContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  45. package/dist/components/VDataTable/VDataTable.css +1 -1
  46. package/dist/components/VDataTable/VDataTable.d.ts +0 -2
  47. package/dist/components/VDataTable/VDataTable.js.map +1 -1
  48. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js +69 -84
  49. package/dist/components/VDataTable/VDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  50. package/dist/components/VDateInput/VDateInput.d.ts +0 -3
  51. package/dist/components/VDateInput/VDateInput.js.map +1 -1
  52. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js +32 -37
  53. package/dist/components/VDateInput/VDateInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  54. package/dist/components/VDatePicker/VDatePicker.css +1 -1
  55. package/dist/components/VDatePicker/VDatePicker.js.map +1 -1
  56. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js +3 -2
  57. package/dist/components/VDatePicker/VDatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  58. package/dist/components/VDialog/VDialog.css +1 -1
  59. package/dist/components/VDialog/VDialog.js.map +1 -1
  60. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js +4 -2
  61. package/dist/components/VDialog/VDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  62. package/dist/components/VDrawer/VDrawer.css +1 -1
  63. package/dist/components/VDrawer/VDrawer.js.map +1 -1
  64. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js +4 -2
  65. package/dist/components/VDrawer/VDrawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
  66. package/dist/components/VFileInput/VFileInput.d.ts +0 -5
  67. package/dist/components/VFileInput/VFileInput.js.map +1 -1
  68. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js +75 -86
  69. package/dist/components/VFileInput/VFileInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  70. package/dist/components/VHotkeys/VHotkeys.d.ts +0 -3
  71. package/dist/components/VHotkeys/VHotkeys.js.map +1 -1
  72. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js +0 -6
  73. package/dist/components/VHotkeys/VHotkeys.vue_vue_type_script_setup_true_lang.js.map +1 -1
  74. package/dist/components/VIconButton/VIconButton.d.ts +0 -3
  75. package/dist/components/VIconButton/VIconButton.js.map +1 -1
  76. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js +0 -6
  77. package/dist/components/VIconButton/VIconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  78. package/dist/components/VInput/VInput.css +1 -1
  79. package/dist/components/VInput/VInput.d.ts +0 -3
  80. package/dist/components/VInput/VInput.js.map +1 -1
  81. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js +18 -24
  82. package/dist/components/VInput/VInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  83. package/dist/components/VInput/VInputGroup.d.ts +0 -3
  84. package/dist/components/VInput/VInputGroup.js.map +1 -1
  85. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js +0 -7
  86. package/dist/components/VInput/VInputGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
  87. package/dist/components/VInput/context.d.ts +0 -2
  88. package/dist/components/VInput/context.js.map +1 -1
  89. package/dist/components/VInputOTP/VInputOTP.d.ts +0 -3
  90. package/dist/components/VInputOTP/VInputOTP.js.map +1 -1
  91. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js +0 -6
  92. package/dist/components/VInputOTP/VInputOTP.vue_vue_type_script_setup_true_lang.js.map +1 -1
  93. package/dist/components/VListbox/VListboxPanel.d.ts +0 -3
  94. package/dist/components/VListbox/VListboxPanel.js.map +1 -1
  95. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js +0 -6
  96. package/dist/components/VListbox/VListboxPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  97. package/dist/components/VListbox/VListboxValues.js.map +1 -1
  98. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js +4 -14
  99. package/dist/components/VListbox/VListboxValues.vue_vue_type_script_setup_true_lang.js.map +1 -1
  100. package/dist/components/VMenu/VMenu.d.ts +0 -3
  101. package/dist/components/VMenu/VMenu.js.map +1 -1
  102. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js +0 -6
  103. package/dist/components/VMenu/VMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  104. package/dist/components/VMenu/VMenuPanel.d.ts +0 -3
  105. package/dist/components/VMenu/VMenuPanel.js.map +1 -1
  106. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js +0 -6
  107. package/dist/components/VMenu/VMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
  108. package/dist/components/VMeter/VMeter.css +1 -1
  109. package/dist/components/VMeter/VMeter.d.ts +24 -8
  110. package/dist/components/VMeter/VMeter.js.map +1 -1
  111. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js +65 -48
  112. package/dist/components/VMeter/VMeter.vue_vue_type_script_setup_true_lang.js.map +1 -1
  113. package/dist/components/VMeterGroup/VMeterGroup.css +2 -0
  114. package/dist/components/VMeterGroup/VMeterGroup.d.ts +101 -0
  115. package/dist/components/VMeterGroup/VMeterGroup.js +9 -0
  116. package/dist/components/VMeterGroup/VMeterGroup.js.map +1 -0
  117. package/dist/components/VMeterGroup/VMeterGroup.vue_vue_type_script_setup_true_lang.js +117 -0
  118. package/dist/components/VMeterGroup/VMeterGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
  119. package/dist/components/VNumberInput/VNumberInput.d.ts +0 -3
  120. package/dist/components/VNumberInput/VNumberInput.js.map +1 -1
  121. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js +0 -6
  122. package/dist/components/VNumberInput/VNumberInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  123. package/dist/components/VNumberInput/number.js +2 -2
  124. package/dist/components/VNumberInput/number.js.map +1 -1
  125. package/dist/components/VPagination/VPagination.d.ts +0 -3
  126. package/dist/components/VPagination/VPagination.js.map +1 -1
  127. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js +0 -6
  128. package/dist/components/VPagination/VPagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  129. package/dist/components/VProgressCircular/VProgressCircular.d.ts +1 -1
  130. package/dist/components/VProgressLinear/VProgressLinear.css +1 -1
  131. package/dist/components/VProgressLinear/VProgressLinear.d.ts +12 -13
  132. package/dist/components/VProgressLinear/VProgressLinear.js.map +1 -1
  133. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js +65 -48
  134. package/dist/components/VProgressLinear/VProgressLinear.vue_vue_type_script_setup_true_lang.js.map +1 -1
  135. package/dist/components/VSelect/VSelect.d.ts +0 -3
  136. package/dist/components/VSelect/VSelect.js.map +1 -1
  137. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js +114 -123
  138. package/dist/components/VSelect/VSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  139. package/dist/components/VSeparator/VSeparator.css +1 -1
  140. package/dist/components/VSeparator/VSeparator.d.ts +42 -2
  141. package/dist/components/VSeparator/VSeparator.js.map +1 -1
  142. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js +50 -9
  143. package/dist/components/VSeparator/VSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -1
  144. package/dist/components/VSideNavigation/VSideNavigation.d.ts +6 -6
  145. package/dist/components/VSideNavigation/VSideNavigation.js.map +1 -1
  146. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js +7 -7
  147. package/dist/components/VSideNavigation/VSideNavigation.vue_vue_type_script_setup_true_lang.js.map +1 -1
  148. package/dist/components/VSideNavigation/VSideNavigationItem.css +1 -1
  149. package/dist/components/VSideNavigation/VSideNavigationItem.js.map +1 -1
  150. package/dist/components/VSideNavigation/VSideNavigationItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  151. package/dist/components/VSideNavigation/context.js.map +1 -1
  152. package/dist/components/VSkeletonLoader/VSkeletonLoader.d.ts +0 -3
  153. package/dist/components/VSkeletonLoader/VSkeletonLoader.js.map +1 -1
  154. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js +0 -6
  155. package/dist/components/VSkeletonLoader/VSkeletonLoader.vue_vue_type_script_setup_true_lang.js.map +1 -1
  156. package/dist/components/VSlider/VSlider.js.map +1 -1
  157. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js +115 -120
  158. package/dist/components/VSlider/VSlider.vue_vue_type_script_setup_true_lang.js.map +1 -1
  159. package/dist/components/VSnackbar/state.js +2 -3
  160. package/dist/components/VSnackbar/state.js.map +1 -1
  161. package/dist/components/VSplitButton/VSplitButton.d.ts +0 -3
  162. package/dist/components/VSplitButton/VSplitButton.js.map +1 -1
  163. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js +0 -6
  164. package/dist/components/VSplitButton/VSplitButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
  165. package/dist/components/VTabs/VTab.js.map +1 -1
  166. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js +0 -2
  167. package/dist/components/VTabs/VTab.vue_vue_type_script_setup_true_lang.js.map +1 -1
  168. package/dist/components/VTabs/VTabs.css +1 -1
  169. package/dist/components/VTabs/VTabs.d.ts +0 -3
  170. package/dist/components/VTabs/VTabs.js.map +1 -1
  171. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js +2 -15
  172. package/dist/components/VTabs/VTabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
  173. package/dist/components/VTabs/context.d.ts +0 -2
  174. package/dist/components/VTabs/context.js.map +1 -1
  175. package/dist/components/VTextarea/VTextarea.d.ts +0 -6
  176. package/dist/components/VTextarea/VTextarea.js.map +1 -1
  177. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js +0 -6
  178. package/dist/components/VTextarea/VTextarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
  179. package/dist/components/VTimeInput/VTimeInput.d.ts +0 -3
  180. package/dist/components/VTimeInput/VTimeInput.js.map +1 -1
  181. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js +211 -218
  182. package/dist/components/VTimeInput/VTimeInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
  183. package/dist/components/VToast/state.js +2 -3
  184. package/dist/components/VToast/state.js.map +1 -1
  185. package/dist/components/VToggle/VToggle.d.ts +0 -3
  186. package/dist/components/VToggle/VToggle.js.map +1 -1
  187. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js +0 -6
  188. package/dist/components/VToggle/VToggle.vue_vue_type_script_setup_true_lang.js.map +1 -1
  189. package/dist/components/VToggle/VToggleItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
  190. package/dist/composables/useAriaLabel.d.ts +5 -0
  191. package/dist/composables/useAriaLabel.js +5 -1
  192. package/dist/composables/useAriaLabel.js.map +1 -1
  193. package/dist/composables/useControlShape.d.ts +1 -5
  194. package/dist/composables/useControlShape.js +0 -1
  195. package/dist/composables/useControlShape.js.map +1 -1
  196. package/dist/composables/useListbox.d.ts +2 -1
  197. package/dist/composables/useListbox.js +72 -69
  198. package/dist/composables/useListbox.js.map +1 -1
  199. package/dist/i18n/en.js +3 -1
  200. package/dist/i18n/en.js.map +1 -1
  201. package/dist/i18n/fr.js +3 -1
  202. package/dist/i18n/fr.js.map +1 -1
  203. package/dist/i18n/types.d.ts +6 -0
  204. package/dist/index.d.ts +5 -3
  205. package/dist/index.js +21 -20
  206. package/dist/styles.css +1 -1
  207. package/dist/tokens/index.d.ts +13 -5
  208. package/dist/tokens/semantic.d.ts +13 -5
  209. package/dist/tokens/semantic.js +16 -10
  210. package/dist/tokens/semantic.js.map +1 -1
  211. package/dist/tokens.json +51 -19
  212. package/dist/types.d.ts +0 -2
  213. package/dist/utils/chip.d.ts +4 -6
  214. package/dist/utils/chip.js +8 -5
  215. package/dist/utils/chip.js.map +1 -1
  216. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"VButton.js","names":["_sfc_main","VButton_vue_vue_type_script_setup_true_lang_default","VButton_default"],"sources":["../../../src/components/VButton/VButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Native buttons own activation and disabling. Links use useInertLink because anchors have no\n * disabled state; group shape overrides preserve individual tones.\n */\nimport { computed, inject, useAttrs } from 'vue'\nimport type { ButtonHTMLAttributes } from 'vue'\n\nimport { buttonGroupKey } from './context'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useInertLink } from '../../composables/useInertLink'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport VSpinner from '../VSpinner/VSpinner.vue'\n\n/** How much visual weight the button carries. */\nexport type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft'\n/** What the action means, in colour. */\nexport type ButtonTone = 'accent' | 'neutral' | 'danger'\n/** The height of the button, from the scale every control shares. */\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\ninterface ButtonProps {\n /**\n * How much visual weight the button carries: `solid` is filled with the tone, `soft` uses a\n * tinted background, `outline` keeps only a border, and `ghost` shows nothing until it is\n * hovered.\n */\n variant?: ButtonVariant\n /**\n * What the action means: `accent` for the ordinary one, `neutral` for a secondary one,\n * `danger` for one that destroys something. Left out inside a VButtonGroup it takes the\n * group's tone, which is the point of not defaulting it here; on its own it is `accent`.\n */\n tone?: ButtonTone\n /**\n * Raises the button off the page: a shadow that grows on hover and settles back when pressed,\n * whatever the variant. Inside a VButtonGroup the group's own value wins over this one.\n */\n elevated?: boolean\n /**\n * The height of the button, taken from the size scale shared by every control.\n * Inside a VButtonGroup the group's own size wins over this one.\n */\n size?: ButtonSize\n /**\n * Takes 4px off the height, leaving the padding, the text and the icons as they\n * are. Inside a VButtonGroup the group's own value wins over this one.\n */\n compact?: boolean\n /**\n * Stretches the button to the whole inline size of its parent, instead of leaving it only as\n * wide as its content.\n */\n fullWidth?: boolean\n /**\n * Turns the button into an `<a>` pointing at this address. A disabled or loading\n * link becomes inert: the address is dropped, so it can be neither focused nor\n * followed.\n */\n href?: string\n /** The native type of the button. It is ignored as soon as `href` makes it a link. */\n type?: ButtonHTMLAttributes['type']\n /**\n * Makes the button unusable: it stops responding, leaves the tab order and greys\n * out through the colour tokens.\n */\n disabled?: boolean\n /**\n * Shows a spinner, disables the button and announces it as busy. The spinner takes\n * the place of the start slot (`iconStart` / `#start`), so an icon and a spinner\n * are never displayed side by side.\n */\n loading?: boolean\n /** An icon before the label. The `#start` slot replaces it when both are given. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it when both are given. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis).\n * It has no effect on the `#start`/`#end` slots, whose icons the consumer builds.\n */\n iconFilled?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n variant: 'solid',\n // The only one of the five with no default. `tone` is the prop a button keeps against its\n // group, so `undefined` has to stay distinguishable from an explicit `accent`, which would\n // otherwise silence the group for every button in the row.\n tone: undefined,\n elevated: false,\n size: 'md',\n compact: false,\n fullWidth: false,\n href: undefined,\n type: 'button',\n disabled: false,\n loading: false,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n})\n\ndefineSlots<{\n /** The label of the button. */\n default(): unknown\n /**\n * Content placed before the label, usually an icon. Mark it `aria-hidden` when it\n * only repeats what the label already says.\n */\n start?(): unknown\n /** Content placed after the label. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst attrs = useAttrs()\nconst group = inject(buttonGroupKey, null)\n\n// The shape of the segmented control belongs to the row: the group is read first, and a\n// group that named nothing hands the prop straight back. The tone goes the other way,\n// the button's own answer first (see context.ts).\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape<ButtonSize>(props, group)\nconst resolvedVariant = computed<ButtonVariant>(() => group?.variant ?? props.variant)\nconst resolvedElevated = computed(() => group?.elevated ?? props.elevated)\n// The tone goes the OTHER way, the button's own answer first: one action in a row can be\n// the destructive one, and the row has to be able to say so (see context.ts).\nconst resolvedTone = computed<ButtonTone>(() => props.tone ?? group?.tone ?? 'accent')\n\nconst isInert = computed(() => resolvedDisabled.value || props.loading)\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: passedAttrs,\n} = useInertLink({\n href: () => props.href,\n inert: () => isInert.value,\n attrs: () => attrs,\n})\n</script>\n\n<template>\n <component\n :is=\"isLink ? 'a' : 'button'\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"passedAttrs\"\n class=\"v-button v-control v-tone v-variant\"\n :href=\"linkHref\"\n :type=\"isLink ? undefined : type\"\n :disabled=\"isLink ? undefined : isInert\"\n :data-variant=\"resolvedVariant\"\n :data-tone=\"resolvedTone\"\n :data-elevated=\"resolvedElevated ? '' : undefined\"\n :data-size=\"resolvedSize\"\n :data-compact=\"resolvedCompact ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n :data-loading=\"loading ? '' : undefined\"\n >\n <!-- The button already carries aria-busy, so the spinner is hidden from\n assistive technology: its own role=\"status\" would announce the same state a\n second time -->\n <span v-if=\"loading\" class=\"v-button-spinner\" aria-hidden=\"true\">\n <VSpinner />\n </span>\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-interactive);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n text-decoration: none;\n cursor: pointer;\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n /* An explicit display, rather than a width alone: an inline-level box sits on a line\n box, which would add the strut's descender under a button the consumer asked to\n fill its parent. Block-level, it measures the parent's content box exactly. */\n .v-button[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * At (0,2,0) it beats this sheet's own padding by weight, and it is the only rule that may\n * touch that padding on an icon-only button. A consumer of VButton restyling the padding of\n * its own buttons at (0,2,0) from another sheet would tie with it, and the bundler would\n * decide: such a rule has to exclude `[data-icon-only]`.\n */\n .v-button[data-icon-only] {\n min-inline-size: var(--control-height);\n padding-inline: 0;\n }\n\n .v-button:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /* Tone and variant recipes are shared; component-specific interaction states remain here. */\n\n .v-button[data-variant='solid']:hover:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-button[data-variant='solid']:active:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-active);\n }\n\n /*\n * Nothing then arbitrates the background of a raised ghost by a one-step specificity margin,\n * so a declaration added to either set later cannot silently leak into the other case.\n */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: var(--tone-bg-soft);\n }\n\n /* One step of tint, reached two ways: pressing a ghost or an outline, which already sit on\n the soft surface while hovered, and hovering a soft one. A selector list keeps each\n member's own weight, so merging them moves no arbitration: the outline/ghost half is\n still (0,5,0) after its hover, the soft half still (0,4,0) before its active. */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):active:not(\n :disabled,\n [aria-disabled='true']\n ),\n .v-button[data-variant='soft']:hover:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-variant='soft']:active:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n /*\n * The `:is()` here is not decoration; it is what gives these rules a specificity of (0,3,0),\n * beating the (0,2,0) of the ghost and outline bases without depending on the order the rules\n * end up in.\n */\n .v-button[data-elevated] {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']) {\n background: var(--vectis-color-surface-raised);\n }\n\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(in oklab, var(--vectis-color-surface-raised), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):active:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(\n in oklab,\n var(--vectis-color-surface-raised),\n var(--tone-text-tinted) 12%\n );\n }\n\n .v-button:is(:disabled, [aria-disabled='true']) {\n cursor: not-allowed;\n }\n\n .v-button[data-loading] {\n opacity: 0.5;\n }\n\n /* A disabled button that is not loading greys out through the colour tokens and\n never through opacity. These three rules are (0,4,0), so they already beat the\n (0,3,0) of the raised background whatever the order they are read in. */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading]):is(\n [data-variant='solid'],\n [data-variant='soft']\n ) {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='outline'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n border-color: var(--vectis-color-border);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='ghost'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * The shadow needs a rule of its own. No variant declares a shadow, so none of the three\n * rules above cancels one, and a disabled raised ghost would otherwise go on casting its\n * shadow-sm.\n */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-elevated] {\n box-shadow: none;\n }\n\n .v-button-spinner {\n /*\n * Match the spinner box and font size to the icon it replaces so loading preserves button\n * width and visual scale.\n */\n font-size: var(--vectis-icon-size);\n /* 1em of the line above rather than the variable again: a relative icon size would\n otherwise be resolved twice, once against the button's text and once against this\n box's own enlarged font-size. */\n inline-size: 1em;\n block-size: 1em;\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VButton.js","names":["_sfc_main","VButton_vue_vue_type_script_setup_true_lang_default","VButton_default"],"sources":["../../../src/components/VButton/VButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Native buttons own activation and disabling. Links use useInertLink because anchors have no\n * disabled state; group shape overrides preserve individual tones.\n */\nimport { computed, inject, useAttrs } from 'vue'\nimport type { ButtonHTMLAttributes } from 'vue'\n\nimport { buttonGroupKey } from './context'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useInertLink } from '../../composables/useInertLink'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport VSpinner from '../VSpinner/VSpinner.vue'\n\n/** How much visual weight the button carries. */\nexport type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft'\n/** What the action means, in colour. */\nexport type ButtonTone = 'accent' | 'neutral' | 'danger'\n/** The height of the button, from the scale every control shares. */\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\ninterface ButtonProps {\n /**\n * How much visual weight the button carries: `solid` is filled with the tone, `soft` uses a\n * tinted background, `outline` keeps only a border, and `ghost` shows nothing until it is\n * hovered.\n */\n variant?: ButtonVariant\n /**\n * What the action means: `accent` for the ordinary one, `neutral` for a secondary one,\n * `danger` for one that destroys something. Left out inside a VButtonGroup it takes the\n * group's tone, which is the point of not defaulting it here; on its own it is `accent`.\n */\n tone?: ButtonTone\n /**\n * Raises the button off the page: a shadow that grows on hover and settles back when pressed,\n * whatever the variant. Inside a VButtonGroup the group's own value wins over this one.\n */\n elevated?: boolean\n /**\n * The height of the button, taken from the size scale shared by every control.\n * Inside a VButtonGroup the group's own size wins over this one.\n */\n size?: ButtonSize\n /**\n * Stretches the button to the whole inline size of its parent, instead of leaving it only as\n * wide as its content.\n */\n fullWidth?: boolean\n /**\n * Turns the button into an `<a>` pointing at this address. A disabled or loading\n * link becomes inert: the address is dropped, so it can be neither focused nor\n * followed.\n */\n href?: string\n /** The native type of the button. It is ignored as soon as `href` makes it a link. */\n type?: ButtonHTMLAttributes['type']\n /**\n * Makes the button unusable: it stops responding, leaves the tab order and greys\n * out through the colour tokens.\n */\n disabled?: boolean\n /**\n * Shows a spinner, disables the button and announces it as busy. The spinner takes\n * the place of the start slot (`iconStart` / `#start`), so an icon and a spinner\n * are never displayed side by side.\n */\n loading?: boolean\n /** An icon before the label. The `#start` slot replaces it when both are given. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it when both are given. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis).\n * It has no effect on the `#start`/`#end` slots, whose icons the consumer builds.\n */\n iconFilled?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n variant: 'solid',\n // The only one of the five with no default. `tone` is the prop a button keeps against its\n // group, so `undefined` has to stay distinguishable from an explicit `accent`, which would\n // otherwise silence the group for every button in the row.\n tone: undefined,\n elevated: false,\n size: 'md',\n fullWidth: false,\n href: undefined,\n type: 'button',\n disabled: false,\n loading: false,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n})\n\ndefineSlots<{\n /** The label of the button. */\n default(): unknown\n /**\n * Content placed before the label, usually an icon. Mark it `aria-hidden` when it\n * only repeats what the label already says.\n */\n start?(): unknown\n /** Content placed after the label. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst attrs = useAttrs()\nconst group = inject(buttonGroupKey, null)\n\n// The shape of the segmented control belongs to the row: the group is read first, and a\n// group that named nothing hands the prop straight back. The tone goes the other way,\n// the button's own answer first (see context.ts).\nconst { size: resolvedSize, disabled: resolvedDisabled } = useControlShape<ButtonSize>(props, group)\nconst resolvedVariant = computed<ButtonVariant>(() => group?.variant ?? props.variant)\nconst resolvedElevated = computed(() => group?.elevated ?? props.elevated)\n// The tone goes the OTHER way, the button's own answer first: one action in a row can be\n// the destructive one, and the row has to be able to say so (see context.ts).\nconst resolvedTone = computed<ButtonTone>(() => props.tone ?? group?.tone ?? 'accent')\n\nconst isInert = computed(() => resolvedDisabled.value || props.loading)\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: passedAttrs,\n} = useInertLink({\n href: () => props.href,\n inert: () => isInert.value,\n attrs: () => attrs,\n})\n</script>\n\n<template>\n <component\n :is=\"isLink ? 'a' : 'button'\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"passedAttrs\"\n class=\"v-button v-control v-tone v-variant\"\n :href=\"linkHref\"\n :type=\"isLink ? undefined : type\"\n :disabled=\"isLink ? undefined : isInert\"\n :data-variant=\"resolvedVariant\"\n :data-tone=\"resolvedTone\"\n :data-elevated=\"resolvedElevated ? '' : undefined\"\n :data-size=\"resolvedSize\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n :data-loading=\"loading ? '' : undefined\"\n >\n <!-- The button already carries aria-busy, so the spinner is hidden from\n assistive technology: its own role=\"status\" would announce the same state a\n second time -->\n <span v-if=\"loading\" class=\"v-button-spinner\" aria-hidden=\"true\">\n <VSpinner />\n </span>\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-interactive);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n text-decoration: none;\n cursor: pointer;\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n /* An explicit display, rather than a width alone: an inline-level box sits on a line\n box, which would add the strut's descender under a button the consumer asked to\n fill its parent. Block-level, it measures the parent's content box exactly. */\n .v-button[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * At (0,2,0) it beats this sheet's own padding by weight, and it is the only rule that may\n * touch that padding on an icon-only button. A consumer of VButton restyling the padding of\n * its own buttons at (0,2,0) from another sheet would tie with it, and the bundler would\n * decide: such a rule has to exclude `[data-icon-only]`.\n */\n .v-button[data-icon-only] {\n min-inline-size: var(--control-height);\n padding-inline: 0;\n }\n\n .v-button:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /* Tone and variant recipes are shared; component-specific interaction states remain here. */\n\n .v-button[data-variant='solid']:hover:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-button[data-variant='solid']:active:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-active);\n }\n\n /*\n * Nothing then arbitrates the background of a raised ghost by a one-step specificity margin,\n * so a declaration added to either set later cannot silently leak into the other case.\n */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: var(--tone-bg-soft);\n }\n\n /* One step of tint, reached two ways: pressing a ghost or an outline, which already sit on\n the soft surface while hovered, and hovering a soft one. A selector list keeps each\n member's own weight, so merging them moves no arbitration: the outline/ghost half is\n still (0,5,0) after its hover, the soft half still (0,4,0) before its active. */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):active:not(\n :disabled,\n [aria-disabled='true']\n ),\n .v-button[data-variant='soft']:hover:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-variant='soft']:active:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n /*\n * The `:is()` here is not decoration; it is what gives these rules a specificity of (0,3,0),\n * beating the (0,2,0) of the ghost and outline bases without depending on the order the rules\n * end up in.\n */\n .v-button[data-elevated] {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']) {\n background: var(--vectis-color-surface-raised);\n }\n\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(in oklab, var(--vectis-color-surface-raised), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):active:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(\n in oklab,\n var(--vectis-color-surface-raised),\n var(--tone-text-tinted) 12%\n );\n }\n\n .v-button:is(:disabled, [aria-disabled='true']) {\n cursor: not-allowed;\n }\n\n .v-button[data-loading] {\n opacity: 0.5;\n }\n\n /* A disabled button that is not loading greys out through the colour tokens and\n never through opacity. These three rules are (0,4,0), so they already beat the\n (0,3,0) of the raised background whatever the order they are read in. */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading]):is(\n [data-variant='solid'],\n [data-variant='soft']\n ) {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='outline'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n border-color: var(--vectis-color-border);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='ghost'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * The shadow needs a rule of its own. No variant declares a shadow, so none of the three\n * rules above cancels one, and a disabled raised ghost would otherwise go on casting its\n * shadow-sm.\n */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-elevated] {\n box-shadow: none;\n }\n\n .v-button-spinner {\n /*\n * Match the spinner box and font size to the icon it replaces so loading preserves button\n * width and visual scale.\n */\n font-size: var(--vectis-icon-size);\n /* 1em of the line above rather than the variable again: a relative icon size would\n otherwise be resolved twice, once against the button's text and once against this\n box's own enlarged font-size. */\n inline-size: 1em;\n block-size: 1em;\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -21,10 +21,6 @@ var b = {
21
21
  default: !1
22
22
  },
23
23
  size: { default: "md" },
24
- compact: {
25
- type: Boolean,
26
- default: !1
27
- },
28
24
  fullWidth: {
29
25
  type: Boolean,
30
26
  default: !1
@@ -47,24 +43,23 @@ var b = {
47
43
  }
48
44
  },
49
45
  setup(d) {
50
- let x = d, S = v(), C = f(n, null), { size: w, compact: T, disabled: E } = r(x, C), D = o(() => C?.variant ?? x.variant), O = o(() => C?.elevated ?? x.elevated), k = o(() => x.tone ?? C?.tone ?? "accent"), A = o(() => E.value || x.loading), { isLink: j, isInertLink: M, linkHref: N, attrs: P } = i({
46
+ let x = d, S = v(), C = f(n, null), { size: w, disabled: T } = r(x, C), E = o(() => C?.variant ?? x.variant), D = o(() => C?.elevated ?? x.elevated), O = o(() => x.tone ?? C?.tone ?? "accent"), k = o(() => T.value || x.loading), { isLink: A, isInertLink: j, linkHref: M, attrs: N } = i({
51
47
  href: () => x.href,
52
- inert: () => A.value,
48
+ inert: () => k.value,
53
49
  attrs: () => S
54
50
  });
55
- return (n, r) => (m(), s(g(_(j) ? "a" : "button"), p({
56
- "aria-disabled": _(M) ? "true" : void 0,
51
+ return (n, r) => (m(), s(g(_(A) ? "a" : "button"), p({
52
+ "aria-disabled": _(j) ? "true" : void 0,
57
53
  "aria-busy": d.loading || void 0
58
- }, _(P), {
54
+ }, _(N), {
59
55
  class: "v-button v-control v-tone v-variant",
60
- href: _(N),
61
- type: _(j) ? void 0 : d.type,
62
- disabled: _(j) ? void 0 : A.value,
63
- "data-variant": D.value,
64
- "data-tone": k.value,
65
- "data-elevated": O.value ? "" : void 0,
56
+ href: _(M),
57
+ type: _(A) ? void 0 : d.type,
58
+ disabled: _(A) ? void 0 : k.value,
59
+ "data-variant": E.value,
60
+ "data-tone": O.value,
61
+ "data-elevated": D.value ? "" : void 0,
66
62
  "data-size": _(w),
67
- "data-compact": _(T) ? "" : void 0,
68
63
  "data-full-width": d.fullWidth ? "" : void 0,
69
64
  "data-loading": d.loading ? "" : void 0
70
65
  }), {
@@ -84,7 +79,6 @@ var b = {
84
79
  "data-tone",
85
80
  "data-elevated",
86
81
  "data-size",
87
- "data-compact",
88
82
  "data-full-width",
89
83
  "data-loading"
90
84
  ]));
@@ -1 +1 @@
1
- {"version":3,"file":"VButton.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_resolveDynamicComponent","_unref","_mergeProps","_withCtx","_createElementBlock","_createVNode","VSpinner","_renderSlot","VIcon","_createCommentVNode","VIcon_default","VSpinner_default","_hoisted_1","VButton_vue_vue_type_script_setup_true_lang_default","props","__props","attrs","useAttrs","group","inject","buttonGroupKey","resolvedSize","resolvedCompact","resolvedDisabled","useControlShape","resolvedVariant","computed","resolvedElevated","resolvedTone","isInert","passedAttrs","useInertLink","openBlock","createBlock","resolveDynamicComponent","unref","isLink","mergeProps","isInertLink","linkHref","withCtx","createElementBlock","createVNode","renderSlot","_ctx","iconProps","createCommentVNode"],"sources":["../../../src/components/VButton/VButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Native buttons own activation and disabling. Links use useInertLink because anchors have no\n * disabled state; group shape overrides preserve individual tones.\n */\nimport { computed, inject, useAttrs } from 'vue'\nimport type { ButtonHTMLAttributes } from 'vue'\n\nimport { buttonGroupKey } from './context'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useInertLink } from '../../composables/useInertLink'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport VSpinner from '../VSpinner/VSpinner.vue'\n\n/** How much visual weight the button carries. */\nexport type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft'\n/** What the action means, in colour. */\nexport type ButtonTone = 'accent' | 'neutral' | 'danger'\n/** The height of the button, from the scale every control shares. */\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\ninterface ButtonProps {\n /**\n * How much visual weight the button carries: `solid` is filled with the tone, `soft` uses a\n * tinted background, `outline` keeps only a border, and `ghost` shows nothing until it is\n * hovered.\n */\n variant?: ButtonVariant\n /**\n * What the action means: `accent` for the ordinary one, `neutral` for a secondary one,\n * `danger` for one that destroys something. Left out inside a VButtonGroup it takes the\n * group's tone, which is the point of not defaulting it here; on its own it is `accent`.\n */\n tone?: ButtonTone\n /**\n * Raises the button off the page: a shadow that grows on hover and settles back when pressed,\n * whatever the variant. Inside a VButtonGroup the group's own value wins over this one.\n */\n elevated?: boolean\n /**\n * The height of the button, taken from the size scale shared by every control.\n * Inside a VButtonGroup the group's own size wins over this one.\n */\n size?: ButtonSize\n /**\n * Takes 4px off the height, leaving the padding, the text and the icons as they\n * are. Inside a VButtonGroup the group's own value wins over this one.\n */\n compact?: boolean\n /**\n * Stretches the button to the whole inline size of its parent, instead of leaving it only as\n * wide as its content.\n */\n fullWidth?: boolean\n /**\n * Turns the button into an `<a>` pointing at this address. A disabled or loading\n * link becomes inert: the address is dropped, so it can be neither focused nor\n * followed.\n */\n href?: string\n /** The native type of the button. It is ignored as soon as `href` makes it a link. */\n type?: ButtonHTMLAttributes['type']\n /**\n * Makes the button unusable: it stops responding, leaves the tab order and greys\n * out through the colour tokens.\n */\n disabled?: boolean\n /**\n * Shows a spinner, disables the button and announces it as busy. The spinner takes\n * the place of the start slot (`iconStart` / `#start`), so an icon and a spinner\n * are never displayed side by side.\n */\n loading?: boolean\n /** An icon before the label. The `#start` slot replaces it when both are given. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it when both are given. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis).\n * It has no effect on the `#start`/`#end` slots, whose icons the consumer builds.\n */\n iconFilled?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n variant: 'solid',\n // The only one of the five with no default. `tone` is the prop a button keeps against its\n // group, so `undefined` has to stay distinguishable from an explicit `accent`, which would\n // otherwise silence the group for every button in the row.\n tone: undefined,\n elevated: false,\n size: 'md',\n compact: false,\n fullWidth: false,\n href: undefined,\n type: 'button',\n disabled: false,\n loading: false,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n})\n\ndefineSlots<{\n /** The label of the button. */\n default(): unknown\n /**\n * Content placed before the label, usually an icon. Mark it `aria-hidden` when it\n * only repeats what the label already says.\n */\n start?(): unknown\n /** Content placed after the label. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst attrs = useAttrs()\nconst group = inject(buttonGroupKey, null)\n\n// The shape of the segmented control belongs to the row: the group is read first, and a\n// group that named nothing hands the prop straight back. The tone goes the other way,\n// the button's own answer first (see context.ts).\nconst {\n size: resolvedSize,\n compact: resolvedCompact,\n disabled: resolvedDisabled,\n} = useControlShape<ButtonSize>(props, group)\nconst resolvedVariant = computed<ButtonVariant>(() => group?.variant ?? props.variant)\nconst resolvedElevated = computed(() => group?.elevated ?? props.elevated)\n// The tone goes the OTHER way, the button's own answer first: one action in a row can be\n// the destructive one, and the row has to be able to say so (see context.ts).\nconst resolvedTone = computed<ButtonTone>(() => props.tone ?? group?.tone ?? 'accent')\n\nconst isInert = computed(() => resolvedDisabled.value || props.loading)\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: passedAttrs,\n} = useInertLink({\n href: () => props.href,\n inert: () => isInert.value,\n attrs: () => attrs,\n})\n</script>\n\n<template>\n <component\n :is=\"isLink ? 'a' : 'button'\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"passedAttrs\"\n class=\"v-button v-control v-tone v-variant\"\n :href=\"linkHref\"\n :type=\"isLink ? undefined : type\"\n :disabled=\"isLink ? undefined : isInert\"\n :data-variant=\"resolvedVariant\"\n :data-tone=\"resolvedTone\"\n :data-elevated=\"resolvedElevated ? '' : undefined\"\n :data-size=\"resolvedSize\"\n :data-compact=\"resolvedCompact ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n :data-loading=\"loading ? '' : undefined\"\n >\n <!-- The button already carries aria-busy, so the spinner is hidden from\n assistive technology: its own role=\"status\" would announce the same state a\n second time -->\n <span v-if=\"loading\" class=\"v-button-spinner\" aria-hidden=\"true\">\n <VSpinner />\n </span>\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-interactive);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n text-decoration: none;\n cursor: pointer;\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n /* An explicit display, rather than a width alone: an inline-level box sits on a line\n box, which would add the strut's descender under a button the consumer asked to\n fill its parent. Block-level, it measures the parent's content box exactly. */\n .v-button[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * At (0,2,0) it beats this sheet's own padding by weight, and it is the only rule that may\n * touch that padding on an icon-only button. A consumer of VButton restyling the padding of\n * its own buttons at (0,2,0) from another sheet would tie with it, and the bundler would\n * decide: such a rule has to exclude `[data-icon-only]`.\n */\n .v-button[data-icon-only] {\n min-inline-size: var(--control-height);\n padding-inline: 0;\n }\n\n .v-button:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /* Tone and variant recipes are shared; component-specific interaction states remain here. */\n\n .v-button[data-variant='solid']:hover:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-button[data-variant='solid']:active:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-active);\n }\n\n /*\n * Nothing then arbitrates the background of a raised ghost by a one-step specificity margin,\n * so a declaration added to either set later cannot silently leak into the other case.\n */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: var(--tone-bg-soft);\n }\n\n /* One step of tint, reached two ways: pressing a ghost or an outline, which already sit on\n the soft surface while hovered, and hovering a soft one. A selector list keeps each\n member's own weight, so merging them moves no arbitration: the outline/ghost half is\n still (0,5,0) after its hover, the soft half still (0,4,0) before its active. */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):active:not(\n :disabled,\n [aria-disabled='true']\n ),\n .v-button[data-variant='soft']:hover:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-variant='soft']:active:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n /*\n * The `:is()` here is not decoration; it is what gives these rules a specificity of (0,3,0),\n * beating the (0,2,0) of the ghost and outline bases without depending on the order the rules\n * end up in.\n */\n .v-button[data-elevated] {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']) {\n background: var(--vectis-color-surface-raised);\n }\n\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(in oklab, var(--vectis-color-surface-raised), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):active:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(\n in oklab,\n var(--vectis-color-surface-raised),\n var(--tone-text-tinted) 12%\n );\n }\n\n .v-button:is(:disabled, [aria-disabled='true']) {\n cursor: not-allowed;\n }\n\n .v-button[data-loading] {\n opacity: 0.5;\n }\n\n /* A disabled button that is not loading greys out through the colour tokens and\n never through opacity. These three rules are (0,4,0), so they already beat the\n (0,3,0) of the raised background whatever the order they are read in. */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading]):is(\n [data-variant='solid'],\n [data-variant='soft']\n ) {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='outline'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n border-color: var(--vectis-color-border);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='ghost'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * The shadow needs a rule of its own. No variant declares a shadow, so none of the three\n * rules above cancels one, and a disabled raised ghost would otherwise go on casting its\n * shadow-sm.\n */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-elevated] {\n box-shadow: none;\n }\n\n .v-button-spinner {\n /*\n * Match the spinner box and font size to the icon it replaces so loading preserves button\n * width and visual scale.\n */\n font-size: var(--vectis-icon-size);\n /* 1em of the line above rather than the variable again: a relative icon size would\n otherwise be resolved twice, once against the button's text and once against this\n box's own enlarged font-size. */\n inline-size: 1em;\n block-size: 1em;\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyFA,IAAMgB,IAAQC,GAiCRC,IAAQC,EAAS,GACjBC,IAAQC,EAAOC,GAAgB,IAAI,GAKnC,EACJ,MAAMC,GACN,SAASC,GACT,UAAUC,MACRC,EAA4BV,GAAOI,CAAK,GACtCO,IAAkBC,QAA8BR,GAAO,WAAWJ,EAAM,OAAO,GAC/Ea,IAAmBD,QAAeR,GAAO,YAAYJ,EAAM,QAAQ,GAGnEc,IAAeF,QAA2BZ,EAAM,QAAQI,GAAO,QAAQ,QAAQ,GAE/EW,IAAUH,QAAeH,EAAiB,SAAST,EAAM,OAAO,GAChE,EACJ,WACA,gBACA,aACA,OAAOgB,MACLC,EAAa;GACf,YAAYjB,EAAM;GAClB,aAAae,EAAQ;GACrB,aAAab;EACf,CAAC;oBAIClB,EAAA,GAAAC,EA8BYC,EA7BLC,EAAAmC,CAAA,IAAM,MAAA,QAAA,GADblC,EA8BY;GA5BT,iBAAeD,EAAAqC,CAAA,IAAW,SAAY,KAAA;GACtC,aAAWvB,EAAA,WAAW,KAAA;EACf,GAAAd,EAAA6B,CAAA,GAAW;GACnB,OAAM;GACL,MAAM7B,EAAAsC,CAAA;GACN,MAAMtC,EAAAmC,CAAA,IAAS,KAAA,IAAYrB,EAAA;GAC3B,UAAUd,EAAAmC,CAAA,IAAS,KAAA,IAAYP,EAAA;GAC/B,gBAAcJ,EAAA;GACd,aAAWG,EAAA;GACX,iBAAeD,EAAA,QAAgB,KAAQ,KAAA;GACvC,aAAW1B,EAAAoB,CAAA;GACX,gBAAcpB,EAAAqB,CAAA,IAAe,KAAQ,KAAA;GACrC,mBAAiBP,EAAA,YAAS,KAAQ,KAAA;GAClC,gBAAcA,EAAA,UAAO,KAAQ,KAAA;;GAK9B,SAAAZ,QAEO;IAFKY,EAAA,WAAZjB,EAAA,GAAAM,EAEO,QAFPQ,GAEO,CADLP,EAAYC,CAAA,CAAA,CAAA,KAEdC,EAEOqC,EAAA,QAAA,SAAA,CAAA,SAAA,CADQ7B,EAAA,aAAbjB,EAAA,GAAAC,EAA6ES,GAA7EN,EAA6E,EAAA,KAAA,EAAA,GAA7CD,EAAA4C,CAAA,CAAS,CAAC9B,EAAA,SAAS,GAAA,EAAI,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA+B,EAAA,IAAA,EAAA,CAAA,GAAA,KAAA,GAAA,CAAA;IAE3EvC,EAAQqC,EAAA,QAAA,SAAA;IACRrC,EAEOqC,EAAA,QAAA,OAAA,CAAA,SAAA,CADQ7B,EAAA,WAAbjB,EAAA,GAAAC,EAAyES,GAAzEN,EAAyE,EAAA,KAAA,EAAA,GAA3CD,EAAA4C,CAAA,CAAS,CAAC9B,EAAA,OAAO,GAAA,EAAI,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA+B,EAAA,IAAA,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"VButton.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_resolveDynamicComponent","_unref","_mergeProps","_withCtx","_createElementBlock","_createVNode","VSpinner","_renderSlot","VIcon","_createCommentVNode","VIcon_default","VSpinner_default","_hoisted_1","VButton_vue_vue_type_script_setup_true_lang_default","props","__props","attrs","useAttrs","group","inject","buttonGroupKey","resolvedSize","resolvedDisabled","useControlShape","resolvedVariant","computed","resolvedElevated","resolvedTone","isInert","passedAttrs","useInertLink","openBlock","createBlock","resolveDynamicComponent","unref","isLink","mergeProps","isInertLink","linkHref","withCtx","createElementBlock","createVNode","renderSlot","_ctx","iconProps","createCommentVNode"],"sources":["../../../src/components/VButton/VButton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// @a11y\n/**\n * Native buttons own activation and disabling. Links use useInertLink because anchors have no\n * disabled state; group shape overrides preserve individual tones.\n */\nimport { computed, inject, useAttrs } from 'vue'\nimport type { ButtonHTMLAttributes } from 'vue'\n\nimport { buttonGroupKey } from './context'\n\nimport { useControlShape } from '../../composables/useControlShape'\nimport { useInertLink } from '../../composables/useInertLink'\n\nimport VIcon from '../VIcon/VIcon.vue'\nimport { iconProps } from '../VIcon/iconProps'\nimport type { IconSource } from '../VIcon/types'\nimport VSpinner from '../VSpinner/VSpinner.vue'\n\n/** How much visual weight the button carries. */\nexport type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'soft'\n/** What the action means, in colour. */\nexport type ButtonTone = 'accent' | 'neutral' | 'danger'\n/** The height of the button, from the scale every control shares. */\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n\ninterface ButtonProps {\n /**\n * How much visual weight the button carries: `solid` is filled with the tone, `soft` uses a\n * tinted background, `outline` keeps only a border, and `ghost` shows nothing until it is\n * hovered.\n */\n variant?: ButtonVariant\n /**\n * What the action means: `accent` for the ordinary one, `neutral` for a secondary one,\n * `danger` for one that destroys something. Left out inside a VButtonGroup it takes the\n * group's tone, which is the point of not defaulting it here; on its own it is `accent`.\n */\n tone?: ButtonTone\n /**\n * Raises the button off the page: a shadow that grows on hover and settles back when pressed,\n * whatever the variant. Inside a VButtonGroup the group's own value wins over this one.\n */\n elevated?: boolean\n /**\n * The height of the button, taken from the size scale shared by every control.\n * Inside a VButtonGroup the group's own size wins over this one.\n */\n size?: ButtonSize\n /**\n * Stretches the button to the whole inline size of its parent, instead of leaving it only as\n * wide as its content.\n */\n fullWidth?: boolean\n /**\n * Turns the button into an `<a>` pointing at this address. A disabled or loading\n * link becomes inert: the address is dropped, so it can be neither focused nor\n * followed.\n */\n href?: string\n /** The native type of the button. It is ignored as soon as `href` makes it a link. */\n type?: ButtonHTMLAttributes['type']\n /**\n * Makes the button unusable: it stops responding, leaves the tab order and greys\n * out through the colour tokens.\n */\n disabled?: boolean\n /**\n * Shows a spinner, disables the button and announces it as busy. The spinner takes\n * the place of the start slot (`iconStart` / `#start`), so an icon and a spinner\n * are never displayed side by side.\n */\n loading?: boolean\n /** An icon before the label. The `#start` slot replaces it when both are given. */\n iconStart?: IconSource\n /** An icon after the label. The `#end` slot replaces it when both are given. */\n iconEnd?: IconSource\n /**\n * Renders `iconStart` and `iconEnd` in their filled form (the font's `FILL` axis).\n * It has no effect on the `#start`/`#end` slots, whose icons the consumer builds.\n */\n iconFilled?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n variant: 'solid',\n // The only one of the five with no default. `tone` is the prop a button keeps against its\n // group, so `undefined` has to stay distinguishable from an explicit `accent`, which would\n // otherwise silence the group for every button in the row.\n tone: undefined,\n elevated: false,\n size: 'md',\n fullWidth: false,\n href: undefined,\n type: 'button',\n disabled: false,\n loading: false,\n iconStart: undefined,\n iconEnd: undefined,\n iconFilled: false,\n})\n\ndefineSlots<{\n /** The label of the button. */\n default(): unknown\n /**\n * Content placed before the label, usually an icon. Mark it `aria-hidden` when it\n * only repeats what the label already says.\n */\n start?(): unknown\n /** Content placed after the label. */\n end?(): unknown\n}>()\n\ndefineOptions({ inheritAttrs: false })\n\nconst attrs = useAttrs()\nconst group = inject(buttonGroupKey, null)\n\n// The shape of the segmented control belongs to the row: the group is read first, and a\n// group that named nothing hands the prop straight back. The tone goes the other way,\n// the button's own answer first (see context.ts).\nconst { size: resolvedSize, disabled: resolvedDisabled } = useControlShape<ButtonSize>(props, group)\nconst resolvedVariant = computed<ButtonVariant>(() => group?.variant ?? props.variant)\nconst resolvedElevated = computed(() => group?.elevated ?? props.elevated)\n// The tone goes the OTHER way, the button's own answer first: one action in a row can be\n// the destructive one, and the row has to be able to say so (see context.ts).\nconst resolvedTone = computed<ButtonTone>(() => props.tone ?? group?.tone ?? 'accent')\n\nconst isInert = computed(() => resolvedDisabled.value || props.loading)\nconst {\n isLink,\n isInertLink,\n linkHref,\n attrs: passedAttrs,\n} = useInertLink({\n href: () => props.href,\n inert: () => isInert.value,\n attrs: () => attrs,\n})\n</script>\n\n<template>\n <component\n :is=\"isLink ? 'a' : 'button'\"\n :aria-disabled=\"isInertLink ? 'true' : undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"passedAttrs\"\n class=\"v-button v-control v-tone v-variant\"\n :href=\"linkHref\"\n :type=\"isLink ? undefined : type\"\n :disabled=\"isLink ? undefined : isInert\"\n :data-variant=\"resolvedVariant\"\n :data-tone=\"resolvedTone\"\n :data-elevated=\"resolvedElevated ? '' : undefined\"\n :data-size=\"resolvedSize\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n :data-loading=\"loading ? '' : undefined\"\n >\n <!-- The button already carries aria-busy, so the spinner is hidden from\n assistive technology: its own role=\"status\" would announce the same state a\n second time -->\n <span v-if=\"loading\" class=\"v-button-spinner\" aria-hidden=\"true\">\n <VSpinner />\n </span>\n <slot v-else name=\"start\">\n <VIcon v-if=\"iconStart\" v-bind=\"iconProps(iconStart)\" :filled=\"iconFilled\" />\n </slot>\n <slot />\n <slot name=\"end\">\n <VIcon v-if=\"iconEnd\" v-bind=\"iconProps(iconEnd)\" :filled=\"iconFilled\" />\n </slot>\n </component>\n</template>\n\n<style>\n@layer vectis.components {\n .v-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--control-gap);\n block-size: var(--control-height);\n padding-inline: var(--control-padding-inline);\n border: 1px solid transparent;\n border-radius: var(--vectis-radius-interactive);\n font-family: var(--vectis-text-family);\n font-size: var(--control-font-size);\n font-weight: var(--vectis-text-control-weight);\n line-height: var(--vectis-text-control-leading);\n text-decoration: none;\n cursor: pointer;\n transition:\n background-color var(--vectis-duration-fast) var(--vectis-ease-default),\n border-color var(--vectis-duration-fast) var(--vectis-ease-default),\n color var(--vectis-duration-fast) var(--vectis-ease-default),\n box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n /* An explicit display, rather than a width alone: an inline-level box sits on a line\n box, which would add the strut's descender under a button the consumer asked to\n fill its parent. Block-level, it measures the parent's content box exactly. */\n .v-button[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * At (0,2,0) it beats this sheet's own padding by weight, and it is the only rule that may\n * touch that padding on an icon-only button. A consumer of VButton restyling the padding of\n * its own buttons at (0,2,0) from another sheet would tie with it, and the bundler would\n * decide: such a rule has to exclude `[data-icon-only]`.\n */\n .v-button[data-icon-only] {\n min-inline-size: var(--control-height);\n padding-inline: 0;\n }\n\n .v-button:focus-visible {\n outline: var(--vectis-focus-ring-width) solid var(--vectis-focus-ring-color);\n outline-offset: var(--vectis-focus-ring-offset);\n }\n\n /* Tone and variant recipes are shared; component-specific interaction states remain here. */\n\n .v-button[data-variant='solid']:hover:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-hover);\n }\n\n .v-button[data-variant='solid']:active:not(:disabled, [aria-disabled='true']) {\n background: var(--tone-bg-solid-active);\n }\n\n /*\n * Nothing then arbitrates the background of a raised ghost by a one-step specificity margin,\n * so a declaration added to either set later cannot silently leak into the other case.\n */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: var(--tone-bg-soft);\n }\n\n /* One step of tint, reached two ways: pressing a ghost or an outline, which already sit on\n the soft surface while hovered, and hovering a soft one. A selector list keeps each\n member's own weight, so merging them moves no arbitration: the outline/ghost half is\n still (0,5,0) after its hover, the soft half still (0,4,0) before its active. */\n .v-button:is([data-variant='outline'], [data-variant='ghost']):not([data-elevated]):active:not(\n :disabled,\n [aria-disabled='true']\n ),\n .v-button[data-variant='soft']:hover:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-variant='soft']:active:not(:disabled, [aria-disabled='true']) {\n background: color-mix(in oklab, var(--tone-bg-soft), var(--tone-text-tinted) 14%);\n }\n\n /*\n * The `:is()` here is not decoration; it is what gives these rules a specificity of (0,3,0),\n * beating the (0,2,0) of the ghost and outline bases without depending on the order the rules\n * end up in.\n */\n .v-button[data-elevated] {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']) {\n background: var(--vectis-color-surface-raised);\n }\n\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):hover:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(in oklab, var(--vectis-color-surface-raised), var(--tone-text-tinted) 8%);\n }\n\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button[data-elevated]:is([data-variant='ghost'], [data-variant='outline']):active:not(\n :disabled,\n [aria-disabled='true']\n ) {\n background: color-mix(\n in oklab,\n var(--vectis-color-surface-raised),\n var(--tone-text-tinted) 12%\n );\n }\n\n .v-button:is(:disabled, [aria-disabled='true']) {\n cursor: not-allowed;\n }\n\n .v-button[data-loading] {\n opacity: 0.5;\n }\n\n /* A disabled button that is not loading greys out through the colour tokens and\n never through opacity. These three rules are (0,4,0), so they already beat the\n (0,3,0) of the raised background whatever the order they are read in. */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading]):is(\n [data-variant='solid'],\n [data-variant='soft']\n ) {\n background: var(--vectis-color-surface-muted);\n color: var(--vectis-color-text-subtle);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='outline'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n border-color: var(--vectis-color-border);\n }\n\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-variant='ghost'] {\n background: transparent;\n color: var(--vectis-color-text-subtle);\n }\n\n /*\n * The shadow needs a rule of its own. No variant declares a shadow, so none of the three\n * rules above cancels one, and a disabled raised ghost would otherwise go on casting its\n * shadow-sm.\n */\n .v-button:is(:disabled, [aria-disabled='true']):not([data-loading])[data-elevated] {\n box-shadow: none;\n }\n\n .v-button-spinner {\n /*\n * Match the spinner box and font size to the icon it replaces so loading preserves button\n * width and visual scale.\n */\n font-size: var(--vectis-icon-size);\n /* 1em of the line above rather than the variable again: a relative icon size would\n otherwise be resolved twice, once against the button's text and once against this\n box's own enlarged font-size. */\n inline-size: 1em;\n block-size: 1em;\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoFA,IAAMgB,IAAQC,GAgCRC,IAAQC,EAAS,GACjBC,IAAQC,EAAOC,GAAgB,IAAI,GAKnC,EAAE,MAAMC,GAAc,UAAUC,MAAqBC,EAA4BT,GAAOI,CAAK,GAC7FM,IAAkBC,QAA8BP,GAAO,WAAWJ,EAAM,OAAO,GAC/EY,IAAmBD,QAAeP,GAAO,YAAYJ,EAAM,QAAQ,GAGnEa,IAAeF,QAA2BX,EAAM,QAAQI,GAAO,QAAQ,QAAQ,GAE/EU,IAAUH,QAAeH,EAAiB,SAASR,EAAM,OAAO,GAChE,EACJ,WACA,gBACA,aACA,OAAOe,MACLC,EAAa;GACf,YAAYhB,EAAM;GAClB,aAAac,EAAQ;GACrB,aAAaZ;EACf,CAAC;oBAIClB,EAAA,GAAAC,EA6BYC,EA5BLC,EAAAkC,CAAA,IAAM,MAAA,QAAA,GADbjC,EA6BY;GA3BT,iBAAeD,EAAAoC,CAAA,IAAW,SAAY,KAAA;GACtC,aAAWtB,EAAA,WAAW,KAAA;EACf,GAAAd,EAAA4B,CAAA,GAAW;GACnB,OAAM;GACL,MAAM5B,EAAAqC,CAAA;GACN,MAAMrC,EAAAkC,CAAA,IAAS,KAAA,IAAYpB,EAAA;GAC3B,UAAUd,EAAAkC,CAAA,IAAS,KAAA,IAAYP,EAAA;GAC/B,gBAAcJ,EAAA;GACd,aAAWG,EAAA;GACX,iBAAeD,EAAA,QAAgB,KAAQ,KAAA;GACvC,aAAWzB,EAAAoB,CAAA;GACX,mBAAiBN,EAAA,YAAS,KAAQ,KAAA;GAClC,gBAAcA,EAAA,UAAO,KAAQ,KAAA;;GAK9B,SAAAZ,QAEO;IAFKY,EAAA,WAAZjB,EAAA,GAAAM,EAEO,QAFPQ,GAEO,CADLP,EAAYC,CAAA,CAAA,CAAA,KAEdC,EAEOoC,EAAA,QAAA,SAAA,CAAA,SAAA,CADQ5B,EAAA,aAAbjB,EAAA,GAAAC,EAA6ES,GAA7EN,EAA6E,EAAA,KAAA,EAAA,GAA7CD,EAAA2C,CAAA,CAAS,CAAC7B,EAAA,SAAS,GAAA,EAAI,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA8B,EAAA,IAAA,EAAA,CAAA,GAAA,KAAA,GAAA,CAAA;IAE3EtC,EAAQoC,EAAA,QAAA,SAAA;IACRpC,EAEOoC,EAAA,QAAA,OAAA,CAAA,SAAA,CADQ5B,EAAA,WAAbjB,EAAA,GAAAC,EAAyES,GAAzEN,EAAyE,EAAA,KAAA,EAAA,GAA3CD,EAAA2C,CAAA,CAAS,CAAC7B,EAAA,OAAO,GAAA,EAAI,QAAQA,EAAA,WAAU,CAAA,GAAA,MAAA,IAAA,CAAA,QAAA,CAAA,KAAA8B,EAAA,IAAA,EAAA,CAAA,CAAA"}
@@ -35,11 +35,6 @@ interface ButtonGroupProps {
35
35
  * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.
36
36
  */
37
37
  size?: ButtonSize;
38
- /**
39
- * Takes 4px off the height of every segment. It wins over the value a button inside
40
- * was given.
41
- */
42
- compact?: boolean;
43
38
  /**
44
39
  * Raises every segment off the page, on the terms of VButton's own `elevated`. It
45
40
  * wins over the value a button inside was given.
@@ -68,7 +63,6 @@ declare const __VLS_base: import('vue').DefineComponent<ButtonGroupProps, {
68
63
  size: ButtonSize;
69
64
  variant: ButtonVariant;
70
65
  tone: ButtonTone;
71
- compact: boolean;
72
66
  disabled: boolean;
73
67
  orientation: ButtonGroupOrientation;
74
68
  elevated: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"VButtonGroup.js","names":["_sfc_main","VButtonGroup_vue_vue_type_script_setup_true_lang_default","VButtonGroup_default"],"sources":["../../../src/components/VButton/VButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Group shape overrides individual buttons; tone is a fallback and disabled is cumulative. CSS\n * joins borders without changing native activation.\n */\nimport { provide, ref } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\nimport { buttonGroupKey } from './context'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\n\n/** Which way the row runs. */\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\ninterface ButtonGroupProps {\n /**\n * The direction the buttons are joined in: a row by default, or a column under\n * `vertical`.\n */\n orientation?: ButtonGroupOrientation\n /**\n * Leaves the buttons as separate ones, with a gap between them and each keeping its\n * own corners, instead of joining them into a segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined buttons, so the row reads as segments rather than as one\n * frame. It has no effect under `detached`.\n */\n bordered?: boolean\n /**\n * Stretches the row across the whole inline size of its parent, every segment taking an equal\n * share of that width.\n */\n fullWidth?: boolean\n /**\n * How much visual weight every segment carries, on the values of VButton's own\n * `variant`: `solid`, `outline`, `ghost` or `soft`. It wins over the variant a\n * button inside was given.\n */\n variant?: ButtonVariant\n /** The colour the segments take, among `accent`, `neutral` and `danger`. */\n tone?: ButtonTone\n /**\n * The height of the segments, from the size scale shared by every control:\n * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.\n */\n size?: ButtonSize\n /**\n * Takes 4px off the height of every segment. It wins over the value a button inside\n * was given.\n */\n compact?: boolean\n /**\n * Raises every segment off the page, on the terms of VButton's own `elevated`. It\n * wins over the value a button inside was given.\n */\n elevated?: boolean\n /**\n * Makes every segment unusable. A button that disables itself stays disabled either\n * way: the two answers add up rather than one overruling the other.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row, which is a `role=\"group\"`: \"Text formatting\",\n * \"View\". A consumer `aria-label` or `aria-labelledby` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ButtonGroupProps>(), {\n // `detached`, `bordered` and `fullWidth` are the group's own layout, and take a real\n // default like the orientation does. The six that follow travel down to the buttons,\n // and are `undefined` by default, the booleans included: it is `undefined`, and not\n // `false`, that means the group has no opinion and lets the button keep its own.\n orientation: 'horizontal',\n detached: false,\n bordered: false,\n fullWidth: false,\n variant: undefined,\n tone: undefined,\n size: undefined,\n compact: undefined,\n elevated: undefined,\n disabled: undefined,\n label: undefined,\n})\n\nconst ariaLabel = useAriaLabel(() => props.label)\n\ndefineSlots<{\n /** The VButtons and VIconButtons to join together. */\n default(): unknown\n}>()\n\n// Getters, so the group's props stay reactive on the other side of the injection.\nprovide(buttonGroupKey, {\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get compact() {\n return props.compact\n },\n get elevated() {\n return props.elevated\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst groupEl = ref<HTMLElement | null>(null)\n\n// A template ref on this component is not a safe way to its element: the comment above the\n// root makes the template a fragment in development, where `$el` is a text anchor. VToggle,\n// whose root this group is, reads the element from here.\ndefineExpose({\n /** The `role=\"group\"` element. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n `data-bordered` is withheld under `detached`, so the DOM never carries a claim the markup\n cannot honour: apart, the buttons share no edge for a line to sit on.\n -->\n <div\n ref=\"groupEl\"\n class=\"v-button-group\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-orientation=\"orientation\"\n :data-detached=\"detached ? '' : undefined\"\n :data-bordered=\"bordered && !detached ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * `.v-overlay` appears in both guards, and neither is optional. A panel must not pass for a\n * segment: VMenu renders its panel as a SIBLING of its trigger, so a group holding a menu has\n * a child that is no segment at all.\n */\n .v-button-group {\n display: inline-flex;\n /* Gives every segment the same height in a row, and the same width in a column,\n whatever each button's own content measures. */\n align-items: stretch;\n /* Declared on every group although only a raised one ever moves its shadow: that is\n what lets the reduced-motion block cancel it with a bare selector rather than\n repeating the elevation `:has()` below. */\n transition: box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-button-group[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n /* An explicit display alongside the inline size, VButton's own full-width argument: an\n inline-level box sits on a line box, and the strut's descender would show as a few\n pixels of dead space under a row asked to fill its parent. */\n .v-button-group[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * Grid tracks equalize complete border boxes; flex distributes content boxes and leaves\n * wrapped segments narrower by their missing padding.\n */\n .v-button-group[data-full-width][data-orientation='horizontal'] {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n }\n\n /*\n * The two selectors are what keeps this out of a plain horizontal row, and that is not an\n * optimization. Nothing is stretched there: each segment is as wide as its own content, so a\n * percentage on the button would resolve against a wrapper whose width depends on that very\n * button, which is circular, and a wrapped segment would stop measuring its label.\n */\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)),\n .v-button-group[data-full-width][data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)) {\n inline-size: 100%;\n }\n\n /* Detached, the buttons are simply spaced: the base rule already gives the flex box\n and the vertical direction, so a gap and the cross-axis alignment are the whole\n difference. The vertical rule restores the stretching the base declares, which the\n centring above beats by one attribute. */\n .v-button-group[data-detached] {\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-button-group[data-detached][data-orientation='vertical'] {\n align-items: stretch;\n }\n\n /*\n * The negative margin pulls each segment onto its neighbour so their two 1px borders collapse\n * into one line, over which the seam below is then laid: it draws the separation the filled\n * variants would otherwise lack (their border is transparent) and unifies the joint between\n * two outlined ones. Each block is scoped to one orientation on purpose.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-inline-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The first segment needs none of that: a panel is rendered after the trigger it belongs to,\n * so it can never take `:first-child` from a segment.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-block-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n /*\n * The seam is a box of its own, and not the neighbour's own border, because CSS joins two\n * adjacent borders at a MITRE: where a 1px border meets the transparent top and bottom ones\n * of a solid segment, its last pixel is cut on the diagonal and the bar ends in a notch at\n * each end. A box carrying that border on ONE side has no second border to join, so its ends\n * stay square.\n */\n .v-button-group > .v-button,\n .v-button-group > :not(:where(.v-overlay, .v-button-group)) .v-button:not(:where(.v-overlay *)) {\n position: relative;\n }\n\n /* Only the generating rule is guarded. The two orientation rules below place the insets\n of a pseudo-element that, unless the row is bordered, is never generated at all. */\n .v-button-group[data-bordered] > .v-button:not(:first-child)::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n content: '';\n position: absolute;\n }\n\n .v-button-group[data-orientation='horizontal'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-block: -1px;\n inset-inline-start: -1px;\n border-inline-start-width: 1px;\n border-inline-start-style: solid;\n }\n\n .v-button-group[data-orientation='vertical'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-inline: -1px;\n inset-block-start: -1px;\n border-block-start-width: 1px;\n border-block-start-style: solid;\n }\n\n /*\n * The seam takes the paint of the segment carrying it. On a filled one it is a veil, which\n * follows every tone, a custom colour and the hover and pressed backgrounds without a value of\n * its own; an outline one lends its own border, through `inherit`, and a ghost one, which has\n * none, the divider grey. The variants exclude one another, so no order arbitrates.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child):is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border-on-fill);\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='outline']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='outline']:not(:where(.v-overlay *))::before {\n border-color: inherit;\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='ghost']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='ghost']:not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border);\n }\n\n /*\n * Neutral solid inverts text and surface, so the theme's veil would vanish into it: there the\n * seam is drawn from the segment's own text colour, lightening in the light theme and\n * darkening in the dark one. One attribute heavier than the filled rule.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child)[data-variant='solid'][data-tone='neutral']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='solid'][data-tone='neutral']:not(:where(.v-overlay *))::before {\n border-color: color-mix(in oklab, var(--tone-text-solid) 25%, transparent);\n }\n\n /*\n * Beside a filled segment, its background already marks the edge, and a line between a filled\n * and an unfilled one would belong to neither. The previous segment is read through `+`, and\n * through `+ .v-overlay +` when a menu's panel sits between the two. At (0,7,0) these beat\n * every colour above.\n */\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:not([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: transparent;\n }\n\n /*\n * Unless the row is bordered, the borders on both sides of every shared edge are cleared. The\n * `:not([data-detached])` guard is also what brings these to (0,6,0), and that weight is not\n * decoration. Without it they are (0,5,0), which TIES with the compound a component overrides\n * a segment's border through\n * (VToggle's outline frame,\n * `.v-toggle[data-item-variant='outline'] > .v-toggle-item[aria-pressed='true']:is(…)`), and\n * a tie between two sheets is settled by whichever the consumer's bundler put last.\n */\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-inline-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-inline-end-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-block-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-block-end-color: transparent;\n }\n\n /*\n * Its argument is a list, so a wrapped segment answers for itself, and since a selector list\n * takes the specificity of its heaviest member, the two branches leave the weight where it\n * was. The raised BACKGROUND stays with each button.\n */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated],\n > :not(:where(.v-overlay, .v-button-group)) .v-button[data-elevated]:not(:where(.v-overlay *))\n ) {\n border-radius: var(--vectis-radius-interactive);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n /* After the hover rule and at equal specificity, the VButton order: pressing a\n raised segment settles the row back down. */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n /*\n * VButton's own hover and active shadows are (0,4,0) on this very element, so at equal\n * specificity the winner would be whichever of the two sheets the consumer's bundler happened\n * to put last.\n */\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated],\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:hover,\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:active,\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:where(.v-overlay *)) {\n box-shadow: none;\n }\n\n /*\n * Raise keyboard focus above the next segment to preserve the shared-edge ring. Do not raise\n * hover/active states, which would cover the neighbour's border seam.\n */\n .v-button-group > .v-button:focus-visible,\n .v-button-group\n > :not(:where(.v-overlay, .v-button-group))\n .v-button:focus-visible:not(:where(.v-overlay *)) {\n z-index: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button-group {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"VButtonGroup.js","names":["_sfc_main","VButtonGroup_vue_vue_type_script_setup_true_lang_default","VButtonGroup_default"],"sources":["../../../src/components/VButton/VButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Group shape overrides individual buttons; tone is a fallback and disabled is cumulative. CSS\n * joins borders without changing native activation.\n */\nimport { provide, ref } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\nimport { buttonGroupKey } from './context'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\n\n/** Which way the row runs. */\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\ninterface ButtonGroupProps {\n /**\n * The direction the buttons are joined in: a row by default, or a column under\n * `vertical`.\n */\n orientation?: ButtonGroupOrientation\n /**\n * Leaves the buttons as separate ones, with a gap between them and each keeping its\n * own corners, instead of joining them into a segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined buttons, so the row reads as segments rather than as one\n * frame. It has no effect under `detached`.\n */\n bordered?: boolean\n /**\n * Stretches the row across the whole inline size of its parent, every segment taking an equal\n * share of that width.\n */\n fullWidth?: boolean\n /**\n * How much visual weight every segment carries, on the values of VButton's own\n * `variant`: `solid`, `outline`, `ghost` or `soft`. It wins over the variant a\n * button inside was given.\n */\n variant?: ButtonVariant\n /** The colour the segments take, among `accent`, `neutral` and `danger`. */\n tone?: ButtonTone\n /**\n * The height of the segments, from the size scale shared by every control:\n * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.\n */\n size?: ButtonSize\n /**\n * Raises every segment off the page, on the terms of VButton's own `elevated`. It\n * wins over the value a button inside was given.\n */\n elevated?: boolean\n /**\n * Makes every segment unusable. A button that disables itself stays disabled either\n * way: the two answers add up rather than one overruling the other.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row, which is a `role=\"group\"`: \"Text formatting\",\n * \"View\". A consumer `aria-label` or `aria-labelledby` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ButtonGroupProps>(), {\n // `detached`, `bordered` and `fullWidth` are the group's own layout, and take a real\n // default like the orientation does. The six that follow travel down to the buttons,\n // and are `undefined` by default, the booleans included: it is `undefined`, and not\n // `false`, that means the group has no opinion and lets the button keep its own.\n orientation: 'horizontal',\n detached: false,\n bordered: false,\n fullWidth: false,\n variant: undefined,\n tone: undefined,\n size: undefined,\n elevated: undefined,\n disabled: undefined,\n label: undefined,\n})\n\nconst ariaLabel = useAriaLabel(() => props.label)\n\ndefineSlots<{\n /** The VButtons and VIconButtons to join together. */\n default(): unknown\n}>()\n\n// Getters, so the group's props stay reactive on the other side of the injection.\nprovide(buttonGroupKey, {\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get elevated() {\n return props.elevated\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst groupEl = ref<HTMLElement | null>(null)\n\n// A template ref on this component is not a safe way to its element: the comment above the\n// root makes the template a fragment in development, where `$el` is a text anchor. VToggle,\n// whose root this group is, reads the element from here.\ndefineExpose({\n /** The `role=\"group\"` element. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n `data-bordered` is withheld under `detached`, so the DOM never carries a claim the markup\n cannot honour: apart, the buttons share no edge for a line to sit on.\n -->\n <div\n ref=\"groupEl\"\n class=\"v-button-group\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-orientation=\"orientation\"\n :data-detached=\"detached ? '' : undefined\"\n :data-bordered=\"bordered && !detached ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * `.v-overlay` appears in both guards, and neither is optional. A panel must not pass for a\n * segment: VMenu renders its panel as a SIBLING of its trigger, so a group holding a menu has\n * a child that is no segment at all.\n */\n .v-button-group {\n display: inline-flex;\n /* Gives every segment the same height in a row, and the same width in a column,\n whatever each button's own content measures. */\n align-items: stretch;\n /* Declared on every group although only a raised one ever moves its shadow: that is\n what lets the reduced-motion block cancel it with a bare selector rather than\n repeating the elevation `:has()` below. */\n transition: box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-button-group[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n /* An explicit display alongside the inline size, VButton's own full-width argument: an\n inline-level box sits on a line box, and the strut's descender would show as a few\n pixels of dead space under a row asked to fill its parent. */\n .v-button-group[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * Grid tracks equalize complete border boxes; flex distributes content boxes and leaves\n * wrapped segments narrower by their missing padding.\n */\n .v-button-group[data-full-width][data-orientation='horizontal'] {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n }\n\n /*\n * The two selectors are what keeps this out of a plain horizontal row, and that is not an\n * optimization. Nothing is stretched there: each segment is as wide as its own content, so a\n * percentage on the button would resolve against a wrapper whose width depends on that very\n * button, which is circular, and a wrapped segment would stop measuring its label.\n */\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)),\n .v-button-group[data-full-width][data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)) {\n inline-size: 100%;\n }\n\n /* Detached, the buttons are simply spaced: the base rule already gives the flex box\n and the vertical direction, so a gap and the cross-axis alignment are the whole\n difference. The vertical rule restores the stretching the base declares, which the\n centring above beats by one attribute. */\n .v-button-group[data-detached] {\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-button-group[data-detached][data-orientation='vertical'] {\n align-items: stretch;\n }\n\n /*\n * The negative margin pulls each segment onto its neighbour so their two 1px borders collapse\n * into one line, over which the seam below is then laid: it draws the separation the filled\n * variants would otherwise lack (their border is transparent) and unifies the joint between\n * two outlined ones. Each block is scoped to one orientation on purpose.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-inline-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The first segment needs none of that: a panel is rendered after the trigger it belongs to,\n * so it can never take `:first-child` from a segment.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-block-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n /*\n * The seam is a box of its own, and not the neighbour's own border, because CSS joins two\n * adjacent borders at a MITRE: where a 1px border meets the transparent top and bottom ones\n * of a solid segment, its last pixel is cut on the diagonal and the bar ends in a notch at\n * each end. A box carrying that border on ONE side has no second border to join, so its ends\n * stay square.\n */\n .v-button-group > .v-button,\n .v-button-group > :not(:where(.v-overlay, .v-button-group)) .v-button:not(:where(.v-overlay *)) {\n position: relative;\n }\n\n /* Only the generating rule is guarded. The two orientation rules below place the insets\n of a pseudo-element that, unless the row is bordered, is never generated at all. */\n .v-button-group[data-bordered] > .v-button:not(:first-child)::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n content: '';\n position: absolute;\n }\n\n .v-button-group[data-orientation='horizontal'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-block: -1px;\n inset-inline-start: -1px;\n border-inline-start-width: 1px;\n border-inline-start-style: solid;\n }\n\n .v-button-group[data-orientation='vertical'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-inline: -1px;\n inset-block-start: -1px;\n border-block-start-width: 1px;\n border-block-start-style: solid;\n }\n\n /*\n * The seam takes the paint of the segment carrying it. On a filled one it is a veil, which\n * follows every tone, a custom colour and the hover and pressed backgrounds without a value of\n * its own; an outline one lends its own border, through `inherit`, and a ghost one, which has\n * none, the divider grey. The variants exclude one another, so no order arbitrates.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child):is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border-on-fill);\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='outline']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='outline']:not(:where(.v-overlay *))::before {\n border-color: inherit;\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='ghost']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='ghost']:not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border);\n }\n\n /*\n * Neutral solid inverts text and surface, so the theme's veil would vanish into it: there the\n * seam is drawn from the segment's own text colour, lightening in the light theme and\n * darkening in the dark one. One attribute heavier than the filled rule.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child)[data-variant='solid'][data-tone='neutral']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='solid'][data-tone='neutral']:not(:where(.v-overlay *))::before {\n border-color: color-mix(in oklab, var(--tone-text-solid) 25%, transparent);\n }\n\n /*\n * Beside a filled segment, its background already marks the edge, and a line between a filled\n * and an unfilled one would belong to neither. The previous segment is read through `+`, and\n * through `+ .v-overlay +` when a menu's panel sits between the two. At (0,7,0) these beat\n * every colour above.\n */\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:not([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: transparent;\n }\n\n /*\n * Unless the row is bordered, the borders on both sides of every shared edge are cleared. The\n * `:not([data-detached])` guard is also what brings these to (0,6,0), and that weight is not\n * decoration. Without it they are (0,5,0), which TIES with the compound a component overrides\n * a segment's border through\n * (VToggle's outline frame,\n * `.v-toggle[data-item-variant='outline'] > .v-toggle-item[aria-pressed='true']:is(…)`), and\n * a tie between two sheets is settled by whichever the consumer's bundler put last.\n */\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-inline-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-inline-end-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-block-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-block-end-color: transparent;\n }\n\n /*\n * Its argument is a list, so a wrapped segment answers for itself, and since a selector list\n * takes the specificity of its heaviest member, the two branches leave the weight where it\n * was. The raised BACKGROUND stays with each button.\n */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated],\n > :not(:where(.v-overlay, .v-button-group)) .v-button[data-elevated]:not(:where(.v-overlay *))\n ) {\n border-radius: var(--vectis-radius-interactive);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n /* After the hover rule and at equal specificity, the VButton order: pressing a\n raised segment settles the row back down. */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n /*\n * VButton's own hover and active shadows are (0,4,0) on this very element, so at equal\n * specificity the winner would be whichever of the two sheets the consumer's bundler happened\n * to put last.\n */\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated],\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:hover,\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:active,\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:where(.v-overlay *)) {\n box-shadow: none;\n }\n\n /*\n * Raise keyboard focus above the next segment to preserve the shared-edge ring. Do not raise\n * hover/active states, which would cover the neighbour's border seam.\n */\n .v-button-group > .v-button:focus-visible,\n .v-button-group\n > :not(:where(.v-overlay, .v-button-group))\n .v-button:focus-visible:not(:where(.v-overlay *)) {\n z-index: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button-group {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":""}
@@ -27,10 +27,6 @@ var l = [
27
27
  variant: { default: void 0 },
28
28
  tone: { default: void 0 },
29
29
  size: { default: void 0 },
30
- compact: {
31
- type: Boolean,
32
- default: void 0
33
- },
34
30
  elevated: {
35
31
  type: Boolean,
36
32
  default: void 0
@@ -53,9 +49,6 @@ var l = [
53
49
  get size() {
54
50
  return d.size;
55
51
  },
56
- get compact() {
57
- return d.compact;
58
- },
59
52
  get elevated() {
60
53
  return d.elevated;
61
54
  },
@@ -1 +1 @@
1
- {"version":3,"file":"VButtonGroup.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_unref","_renderSlot","_hoisted_1","VButtonGroup_vue_vue_type_script_setup_true_lang_default","__expose","props","__props","ariaLabel","useAriaLabel","buttonGroupKey","groupEl","ref","openBlock","createElementBlock","unref","renderSlot","_ctx"],"sources":["../../../src/components/VButton/VButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Group shape overrides individual buttons; tone is a fallback and disabled is cumulative. CSS\n * joins borders without changing native activation.\n */\nimport { provide, ref } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\nimport { buttonGroupKey } from './context'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\n\n/** Which way the row runs. */\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\ninterface ButtonGroupProps {\n /**\n * The direction the buttons are joined in: a row by default, or a column under\n * `vertical`.\n */\n orientation?: ButtonGroupOrientation\n /**\n * Leaves the buttons as separate ones, with a gap between them and each keeping its\n * own corners, instead of joining them into a segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined buttons, so the row reads as segments rather than as one\n * frame. It has no effect under `detached`.\n */\n bordered?: boolean\n /**\n * Stretches the row across the whole inline size of its parent, every segment taking an equal\n * share of that width.\n */\n fullWidth?: boolean\n /**\n * How much visual weight every segment carries, on the values of VButton's own\n * `variant`: `solid`, `outline`, `ghost` or `soft`. It wins over the variant a\n * button inside was given.\n */\n variant?: ButtonVariant\n /** The colour the segments take, among `accent`, `neutral` and `danger`. */\n tone?: ButtonTone\n /**\n * The height of the segments, from the size scale shared by every control:\n * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.\n */\n size?: ButtonSize\n /**\n * Takes 4px off the height of every segment. It wins over the value a button inside\n * was given.\n */\n compact?: boolean\n /**\n * Raises every segment off the page, on the terms of VButton's own `elevated`. It\n * wins over the value a button inside was given.\n */\n elevated?: boolean\n /**\n * Makes every segment unusable. A button that disables itself stays disabled either\n * way: the two answers add up rather than one overruling the other.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row, which is a `role=\"group\"`: \"Text formatting\",\n * \"View\". A consumer `aria-label` or `aria-labelledby` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ButtonGroupProps>(), {\n // `detached`, `bordered` and `fullWidth` are the group's own layout, and take a real\n // default like the orientation does. The six that follow travel down to the buttons,\n // and are `undefined` by default, the booleans included: it is `undefined`, and not\n // `false`, that means the group has no opinion and lets the button keep its own.\n orientation: 'horizontal',\n detached: false,\n bordered: false,\n fullWidth: false,\n variant: undefined,\n tone: undefined,\n size: undefined,\n compact: undefined,\n elevated: undefined,\n disabled: undefined,\n label: undefined,\n})\n\nconst ariaLabel = useAriaLabel(() => props.label)\n\ndefineSlots<{\n /** The VButtons and VIconButtons to join together. */\n default(): unknown\n}>()\n\n// Getters, so the group's props stay reactive on the other side of the injection.\nprovide(buttonGroupKey, {\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get compact() {\n return props.compact\n },\n get elevated() {\n return props.elevated\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst groupEl = ref<HTMLElement | null>(null)\n\n// A template ref on this component is not a safe way to its element: the comment above the\n// root makes the template a fragment in development, where `$el` is a text anchor. VToggle,\n// whose root this group is, reads the element from here.\ndefineExpose({\n /** The `role=\"group\"` element. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n `data-bordered` is withheld under `detached`, so the DOM never carries a claim the markup\n cannot honour: apart, the buttons share no edge for a line to sit on.\n -->\n <div\n ref=\"groupEl\"\n class=\"v-button-group\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-orientation=\"orientation\"\n :data-detached=\"detached ? '' : undefined\"\n :data-bordered=\"bordered && !detached ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * `.v-overlay` appears in both guards, and neither is optional. A panel must not pass for a\n * segment: VMenu renders its panel as a SIBLING of its trigger, so a group holding a menu has\n * a child that is no segment at all.\n */\n .v-button-group {\n display: inline-flex;\n /* Gives every segment the same height in a row, and the same width in a column,\n whatever each button's own content measures. */\n align-items: stretch;\n /* Declared on every group although only a raised one ever moves its shadow: that is\n what lets the reduced-motion block cancel it with a bare selector rather than\n repeating the elevation `:has()` below. */\n transition: box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-button-group[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n /* An explicit display alongside the inline size, VButton's own full-width argument: an\n inline-level box sits on a line box, and the strut's descender would show as a few\n pixels of dead space under a row asked to fill its parent. */\n .v-button-group[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * Grid tracks equalize complete border boxes; flex distributes content boxes and leaves\n * wrapped segments narrower by their missing padding.\n */\n .v-button-group[data-full-width][data-orientation='horizontal'] {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n }\n\n /*\n * The two selectors are what keeps this out of a plain horizontal row, and that is not an\n * optimization. Nothing is stretched there: each segment is as wide as its own content, so a\n * percentage on the button would resolve against a wrapper whose width depends on that very\n * button, which is circular, and a wrapped segment would stop measuring its label.\n */\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)),\n .v-button-group[data-full-width][data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)) {\n inline-size: 100%;\n }\n\n /* Detached, the buttons are simply spaced: the base rule already gives the flex box\n and the vertical direction, so a gap and the cross-axis alignment are the whole\n difference. The vertical rule restores the stretching the base declares, which the\n centring above beats by one attribute. */\n .v-button-group[data-detached] {\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-button-group[data-detached][data-orientation='vertical'] {\n align-items: stretch;\n }\n\n /*\n * The negative margin pulls each segment onto its neighbour so their two 1px borders collapse\n * into one line, over which the seam below is then laid: it draws the separation the filled\n * variants would otherwise lack (their border is transparent) and unifies the joint between\n * two outlined ones. Each block is scoped to one orientation on purpose.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-inline-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The first segment needs none of that: a panel is rendered after the trigger it belongs to,\n * so it can never take `:first-child` from a segment.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-block-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n /*\n * The seam is a box of its own, and not the neighbour's own border, because CSS joins two\n * adjacent borders at a MITRE: where a 1px border meets the transparent top and bottom ones\n * of a solid segment, its last pixel is cut on the diagonal and the bar ends in a notch at\n * each end. A box carrying that border on ONE side has no second border to join, so its ends\n * stay square.\n */\n .v-button-group > .v-button,\n .v-button-group > :not(:where(.v-overlay, .v-button-group)) .v-button:not(:where(.v-overlay *)) {\n position: relative;\n }\n\n /* Only the generating rule is guarded. The two orientation rules below place the insets\n of a pseudo-element that, unless the row is bordered, is never generated at all. */\n .v-button-group[data-bordered] > .v-button:not(:first-child)::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n content: '';\n position: absolute;\n }\n\n .v-button-group[data-orientation='horizontal'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-block: -1px;\n inset-inline-start: -1px;\n border-inline-start-width: 1px;\n border-inline-start-style: solid;\n }\n\n .v-button-group[data-orientation='vertical'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-inline: -1px;\n inset-block-start: -1px;\n border-block-start-width: 1px;\n border-block-start-style: solid;\n }\n\n /*\n * The seam takes the paint of the segment carrying it. On a filled one it is a veil, which\n * follows every tone, a custom colour and the hover and pressed backgrounds without a value of\n * its own; an outline one lends its own border, through `inherit`, and a ghost one, which has\n * none, the divider grey. The variants exclude one another, so no order arbitrates.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child):is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border-on-fill);\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='outline']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='outline']:not(:where(.v-overlay *))::before {\n border-color: inherit;\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='ghost']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='ghost']:not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border);\n }\n\n /*\n * Neutral solid inverts text and surface, so the theme's veil would vanish into it: there the\n * seam is drawn from the segment's own text colour, lightening in the light theme and\n * darkening in the dark one. One attribute heavier than the filled rule.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child)[data-variant='solid'][data-tone='neutral']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='solid'][data-tone='neutral']:not(:where(.v-overlay *))::before {\n border-color: color-mix(in oklab, var(--tone-text-solid) 25%, transparent);\n }\n\n /*\n * Beside a filled segment, its background already marks the edge, and a line between a filled\n * and an unfilled one would belong to neither. The previous segment is read through `+`, and\n * through `+ .v-overlay +` when a menu's panel sits between the two. At (0,7,0) these beat\n * every colour above.\n */\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:not([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: transparent;\n }\n\n /*\n * Unless the row is bordered, the borders on both sides of every shared edge are cleared. The\n * `:not([data-detached])` guard is also what brings these to (0,6,0), and that weight is not\n * decoration. Without it they are (0,5,0), which TIES with the compound a component overrides\n * a segment's border through\n * (VToggle's outline frame,\n * `.v-toggle[data-item-variant='outline'] > .v-toggle-item[aria-pressed='true']:is(…)`), and\n * a tie between two sheets is settled by whichever the consumer's bundler put last.\n */\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-inline-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-inline-end-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-block-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-block-end-color: transparent;\n }\n\n /*\n * Its argument is a list, so a wrapped segment answers for itself, and since a selector list\n * takes the specificity of its heaviest member, the two branches leave the weight where it\n * was. The raised BACKGROUND stays with each button.\n */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated],\n > :not(:where(.v-overlay, .v-button-group)) .v-button[data-elevated]:not(:where(.v-overlay *))\n ) {\n border-radius: var(--vectis-radius-interactive);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n /* After the hover rule and at equal specificity, the VButton order: pressing a\n raised segment settles the row back down. */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n /*\n * VButton's own hover and active shadows are (0,4,0) on this very element, so at equal\n * specificity the winner would be whichever of the two sheets the consumer's bundler happened\n * to put last.\n */\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated],\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:hover,\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:active,\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:where(.v-overlay *)) {\n box-shadow: none;\n }\n\n /*\n * Raise keyboard focus above the next segment to preserve the shared-edge ring. Do not raise\n * hover/active states, which would cover the neighbour's border seam.\n */\n .v-button-group > .v-button:focus-visible,\n .v-button-group\n > :not(:where(.v-overlay, .v-button-group))\n .v-button:focus-visible:not(:where(.v-overlay *)) {\n z-index: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button-group {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuEA,IAAMO,IAAQC,GAkBRC,IAAYC,QAAmBH,EAAM,KAAK;EAQhD,EAAQI,GAAgB;GACtB,IAAI,UAAU;IACZ,OAAOJ,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAOA,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAOA,EAAM;GACf;GACA,IAAI,UAAU;IACZ,OAAOA,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAOA,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAOA,EAAM;GACf;EACF,CAAC;EAED,IAAMK,IAAUC,EAAwB,IAAI;SAK5CP,EAAa,EAEX,IAAIM,EACN,CAAC,cAQCZ,EAAA,GAAAC,EAWM,OAAA;GAVA,SAAA;GAAJ,KAAIW;GACJ,OAAM;GACN,MAAK;GACJ,cAAYV,EAAAO,CAAA;GACZ,oBAAkBD,EAAA;GAClB,iBAAeA,EAAA,WAAQ,KAAQ,KAAA;GAC/B,iBAAeA,EAAA,YAAQ,CAAKA,EAAA,WAAQ,KAAQ,KAAA;GAC5C,mBAAiBA,EAAA,YAAS,KAAQ,KAAA;EAEnCL,GAAAA,CAAAA,EAAQe,EAAA,QAAA,SAAA,CAAA,GAAA,GAAAd,CAAA"}
1
+ {"version":3,"file":"VButtonGroup.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_unref","_renderSlot","_hoisted_1","VButtonGroup_vue_vue_type_script_setup_true_lang_default","__expose","props","__props","ariaLabel","useAriaLabel","buttonGroupKey","groupEl","ref","openBlock","createElementBlock","unref","renderSlot","_ctx"],"sources":["../../../src/components/VButton/VButtonGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Group shape overrides individual buttons; tone is a fallback and disabled is cumulative. CSS\n * joins borders without changing native activation.\n */\nimport { provide, ref } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\nimport { buttonGroupKey } from './context'\n\nimport { useAriaLabel } from '../../composables/useAriaLabel'\n\n/** Which way the row runs. */\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\ninterface ButtonGroupProps {\n /**\n * The direction the buttons are joined in: a row by default, or a column under\n * `vertical`.\n */\n orientation?: ButtonGroupOrientation\n /**\n * Leaves the buttons as separate ones, with a gap between them and each keeping its\n * own corners, instead of joining them into a segmented control.\n */\n detached?: boolean\n /**\n * Draws a line between the joined buttons, so the row reads as segments rather than as one\n * frame. It has no effect under `detached`.\n */\n bordered?: boolean\n /**\n * Stretches the row across the whole inline size of its parent, every segment taking an equal\n * share of that width.\n */\n fullWidth?: boolean\n /**\n * How much visual weight every segment carries, on the values of VButton's own\n * `variant`: `solid`, `outline`, `ghost` or `soft`. It wins over the variant a\n * button inside was given.\n */\n variant?: ButtonVariant\n /** The colour the segments take, among `accent`, `neutral` and `danger`. */\n tone?: ButtonTone\n /**\n * The height of the segments, from the size scale shared by every control:\n * `xs`, `sm`, `md`, `lg` or `xl`. It wins over the size a button inside was given.\n */\n size?: ButtonSize\n /**\n * Raises every segment off the page, on the terms of VButton's own `elevated`. It\n * wins over the value a button inside was given.\n */\n elevated?: boolean\n /**\n * Makes every segment unusable. A button that disables itself stays disabled either\n * way: the two answers add up rather than one overruling the other.\n */\n disabled?: boolean\n /**\n * What screen readers announce for the row, which is a `role=\"group\"`: \"Text formatting\",\n * \"View\". A consumer `aria-label` or `aria-labelledby` wins over it.\n */\n label?: string\n}\n\nconst props = withDefaults(defineProps<ButtonGroupProps>(), {\n // `detached`, `bordered` and `fullWidth` are the group's own layout, and take a real\n // default like the orientation does. The six that follow travel down to the buttons,\n // and are `undefined` by default, the booleans included: it is `undefined`, and not\n // `false`, that means the group has no opinion and lets the button keep its own.\n orientation: 'horizontal',\n detached: false,\n bordered: false,\n fullWidth: false,\n variant: undefined,\n tone: undefined,\n size: undefined,\n elevated: undefined,\n disabled: undefined,\n label: undefined,\n})\n\nconst ariaLabel = useAriaLabel(() => props.label)\n\ndefineSlots<{\n /** The VButtons and VIconButtons to join together. */\n default(): unknown\n}>()\n\n// Getters, so the group's props stay reactive on the other side of the injection.\nprovide(buttonGroupKey, {\n get variant() {\n return props.variant\n },\n get tone() {\n return props.tone\n },\n get size() {\n return props.size\n },\n get elevated() {\n return props.elevated\n },\n get disabled() {\n return props.disabled\n },\n})\n\nconst groupEl = ref<HTMLElement | null>(null)\n\n// A template ref on this component is not a safe way to its element: the comment above the\n// root makes the template a fragment in development, where `$el` is a text anchor. VToggle,\n// whose root this group is, reads the element from here.\ndefineExpose({\n /** The `role=\"group\"` element. */\n el: groupEl,\n})\n</script>\n\n<template>\n <!--\n `data-bordered` is withheld under `detached`, so the DOM never carries a claim the markup\n cannot honour: apart, the buttons share no edge for a line to sit on.\n -->\n <div\n ref=\"groupEl\"\n class=\"v-button-group\"\n role=\"group\"\n :aria-label=\"ariaLabel\"\n :data-orientation=\"orientation\"\n :data-detached=\"detached ? '' : undefined\"\n :data-bordered=\"bordered && !detached ? '' : undefined\"\n :data-full-width=\"fullWidth ? '' : undefined\"\n >\n <slot />\n </div>\n</template>\n\n<style>\n@layer vectis.components {\n /*\n * `.v-overlay` appears in both guards, and neither is optional. A panel must not pass for a\n * segment: VMenu renders its panel as a SIBLING of its trigger, so a group holding a menu has\n * a child that is no segment at all.\n */\n .v-button-group {\n display: inline-flex;\n /* Gives every segment the same height in a row, and the same width in a column,\n whatever each button's own content measures. */\n align-items: stretch;\n /* Declared on every group although only a raised one ever moves its shadow: that is\n what lets the reduced-motion block cancel it with a bare selector rather than\n repeating the elevation `:has()` below. */\n transition: box-shadow var(--vectis-duration-fast) var(--vectis-ease-default);\n }\n\n .v-button-group[data-orientation='vertical'] {\n flex-direction: column;\n }\n\n /* An explicit display alongside the inline size, VButton's own full-width argument: an\n inline-level box sits on a line box, and the strut's descender would show as a few\n pixels of dead space under a row asked to fill its parent. */\n .v-button-group[data-full-width] {\n display: flex;\n inline-size: 100%;\n }\n\n /*\n * Grid tracks equalize complete border boxes; flex distributes content boxes and leaves\n * wrapped segments narrower by their missing padding.\n */\n .v-button-group[data-full-width][data-orientation='horizontal'] {\n display: grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n }\n\n /*\n * The two selectors are what keeps this out of a plain horizontal row, and that is not an\n * optimization. Nothing is stretched there: each segment is as wide as its own content, so a\n * percentage on the button would resolve against a wrapper whose width depends on that very\n * button, which is circular, and a wrapped segment would stop measuring its label.\n */\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)),\n .v-button-group[data-full-width][data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *)))\n :is(.v-button, :has(.v-button)):not(:where(.v-overlay, .v-overlay *)) {\n inline-size: 100%;\n }\n\n /* Detached, the buttons are simply spaced: the base rule already gives the flex box\n and the vertical direction, so a gap and the cross-axis alignment are the whole\n difference. The vertical rule restores the stretching the base declares, which the\n centring above beats by one attribute. */\n .v-button-group[data-detached] {\n align-items: center;\n gap: var(--vectis-space-1);\n }\n\n .v-button-group[data-detached][data-orientation='vertical'] {\n align-items: stretch;\n }\n\n /*\n * The negative margin pulls each segment onto its neighbour so their two 1px borders collapse\n * into one line, over which the seam below is then laid: it draws the separation the filled\n * variants would otherwise lack (their border is transparent) and unifies the joint between\n * two outlined ones. Each block is scoped to one orientation on purpose.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-inline-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='horizontal'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n\n /*\n * The first segment needs none of that: a panel is rendered after the trigger it belongs to,\n * so it can never take `:first-child` from a segment.\n */\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(.v-button:not(:where(.v-overlay *))):not(\n :first-child\n ) {\n margin-block-start: -1px;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical'] > .v-button:not(:first-child),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-start-start-radius: 0;\n border-start-end-radius: 0;\n }\n\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-end-start-radius: 0;\n border-end-end-radius: 0;\n }\n\n /*\n * The seam is a box of its own, and not the neighbour's own border, because CSS joins two\n * adjacent borders at a MITRE: where a 1px border meets the transparent top and bottom ones\n * of a solid segment, its last pixel is cut on the diagonal and the bar ends in a notch at\n * each end. A box carrying that border on ONE side has no second border to join, so its ends\n * stay square.\n */\n .v-button-group > .v-button,\n .v-button-group > :not(:where(.v-overlay, .v-button-group)) .v-button:not(:where(.v-overlay *)) {\n position: relative;\n }\n\n /* Only the generating rule is guarded. The two orientation rules below place the insets\n of a pseudo-element that, unless the row is bordered, is never generated at all. */\n .v-button-group[data-bordered] > .v-button:not(:first-child)::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n content: '';\n position: absolute;\n }\n\n .v-button-group[data-orientation='horizontal'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-block: -1px;\n inset-inline-start: -1px;\n border-inline-start-width: 1px;\n border-inline-start-style: solid;\n }\n\n .v-button-group[data-orientation='vertical'] > .v-button:not(:first-child)::before,\n .v-button-group[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *))::before {\n inset-inline: -1px;\n inset-block-start: -1px;\n border-block-start-width: 1px;\n border-block-start-style: solid;\n }\n\n /*\n * The seam takes the paint of the segment carrying it. On a filled one it is a veil, which\n * follows every tone, a custom colour and the hover and pressed backgrounds without a value of\n * its own; an outline one lends its own border, through `inherit`, and a ghost one, which has\n * none, the divider grey. The variants exclude one another, so no order arbitrates.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child):is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border-on-fill);\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='outline']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='outline']:not(:where(.v-overlay *))::before {\n border-color: inherit;\n }\n\n .v-button-group[data-bordered] > .v-button:not(:first-child)[data-variant='ghost']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='ghost']:not(:where(.v-overlay *))::before {\n border-color: var(--vectis-color-border);\n }\n\n /*\n * Neutral solid inverts text and surface, so the theme's veil would vanish into it: there the\n * seam is drawn from the segment's own text colour, lightening in the light theme and\n * darkening in the dark one. One attribute heavier than the filled rule.\n */\n .v-button-group[data-bordered]\n > .v-button:not(:first-child)[data-variant='solid'][data-tone='neutral']::before,\n .v-button-group[data-bordered]\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button[data-variant='solid'][data-tone='neutral']:not(:where(.v-overlay *))::before {\n border-color: color-mix(in oklab, var(--tone-text-solid) 25%, transparent);\n }\n\n /*\n * Beside a filled segment, its background already marks the edge, and a line between a filled\n * and an unfilled one would belong to neither. The previous segment is read through `+`, and\n * through `+ .v-overlay +` when a menu's panel sits between the two. At (0,7,0) these beat\n * every colour above.\n */\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:is([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:not([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + .v-button:not([data-variant='solid'], [data-variant='soft'])::before,\n .v-button-group[data-bordered]\n > :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n ),\n :is(\n .v-button:is([data-variant='solid'], [data-variant='soft']),\n :not(:where(.v-overlay, .v-button-group)):has(\n .v-button:is([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))\n )\n )\n + .v-overlay\n )\n + :not(:where(.v-overlay, .v-button-group))\n .v-button:not([data-variant='solid'], [data-variant='soft']):not(:where(.v-overlay *))::before {\n border-color: transparent;\n }\n\n /*\n * Unless the row is bordered, the borders on both sides of every shared edge are cleared. The\n * `:not([data-detached])` guard is also what brings these to (0,6,0), and that weight is not\n * decoration. Without it they are (0,5,0), which TIES with the compound a component overrides\n * a segment's border through\n * (VToggle's outline frame,\n * `.v-toggle[data-item-variant='outline'] > .v-toggle-item[aria-pressed='true']:is(…)`), and\n * a tie between two sheets is settled by whichever the consumer's bundler put last.\n */\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-inline-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='horizontal']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-inline-end-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:not(:first-child),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):not(:first-child)\n .v-button:not(:where(.v-overlay *)) {\n border-block-start-color: transparent;\n }\n\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > .v-button:has(~ :not(.v-overlay)),\n .v-button-group:not([data-detached]):not([data-bordered])[data-orientation='vertical']\n > :not(:where(.v-overlay, .v-button-group)):has(~ :not(.v-overlay))\n .v-button:not(:where(.v-overlay *)) {\n border-block-end-color: transparent;\n }\n\n /*\n * Its argument is a list, so a wrapped segment answers for itself, and since a selector list\n * takes the specificity of its heaviest member, the two branches leave the weight where it\n * was. The raised BACKGROUND stays with each button.\n */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated],\n > :not(:where(.v-overlay, .v-button-group)) .v-button[data-elevated]:not(:where(.v-overlay *))\n ) {\n border-radius: var(--vectis-radius-interactive);\n box-shadow: var(--vectis-shadow-sm);\n }\n\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-md);\n }\n\n /* After the hover rule and at equal specificity, the VButton order: pressing a\n raised segment settles the row back down. */\n .v-button-group:not([data-detached]):has(\n > .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']),\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:disabled, [aria-disabled='true']):not(\n :where(.v-overlay *)\n )\n ) {\n box-shadow: var(--vectis-shadow-sm);\n }\n\n /*\n * VButton's own hover and active shadows are (0,4,0) on this very element, so at equal\n * specificity the winner would be whichever of the two sheets the consumer's bundler happened\n * to put last.\n */\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated],\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:hover,\n .v-button-group.v-button-group:not([data-detached]) > .v-button[data-elevated]:active,\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:hover:not(:where(.v-overlay *)),\n .v-button-group.v-button-group:not([data-detached])\n > :not(:where(.v-overlay, .v-button-group))\n .v-button[data-elevated]:active:not(:where(.v-overlay *)) {\n box-shadow: none;\n }\n\n /*\n * Raise keyboard focus above the next segment to preserve the shared-edge ring. Do not raise\n * hover/active states, which would cover the neighbour's border seam.\n */\n .v-button-group > .v-button:focus-visible,\n .v-button-group\n > :not(:where(.v-overlay, .v-button-group))\n .v-button:focus-visible:not(:where(.v-overlay *)) {\n z-index: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .v-button-group {\n transition: none;\n }\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkEA,IAAMO,IAAQC,GAiBRC,IAAYC,QAAmBH,EAAM,KAAK;EAQhD,EAAQI,GAAgB;GACtB,IAAI,UAAU;IACZ,OAAOJ,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAOA,EAAM;GACf;GACA,IAAI,OAAO;IACT,OAAOA,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAOA,EAAM;GACf;GACA,IAAI,WAAW;IACb,OAAOA,EAAM;GACf;EACF,CAAC;EAED,IAAMK,IAAUC,EAAwB,IAAI;SAK5CP,EAAa,EAEX,IAAIM,EACN,CAAC,cAQCZ,EAAA,GAAAC,EAWM,OAAA;GAVA,SAAA;GAAJ,KAAIW;GACJ,OAAM;GACN,MAAK;GACJ,cAAYV,EAAAO,CAAA;GACZ,oBAAkBD,EAAA;GAClB,iBAAeA,EAAA,WAAQ,KAAQ,KAAA;GAC/B,iBAAeA,EAAA,YAAQ,CAAKA,EAAA,WAAQ,KAAQ,KAAA;GAC5C,mBAAiBA,EAAA,YAAS,KAAQ,KAAA;EAEnCL,GAAAA,CAAAA,EAAQe,EAAA,QAAA,SAAA,CAAA,GAAA,GAAAd,CAAA"}
@@ -7,8 +7,6 @@ export interface ButtonGroupContext {
7
7
  readonly tone?: ButtonTone;
8
8
  /** The height of the segments. */
9
9
  readonly size?: ButtonSize;
10
- /** The reduced density. */
11
- readonly compact?: boolean;
12
10
  /** Whether the segments are raised off the page. */
13
11
  readonly elevated?: boolean;
14
12
  /** Whether the whole row is unusable, which no segment can refuse. */
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":["buttonGroupKey","NO_BUTTON_GROUP"],"sources":["../../../src/components/VButton/context.ts"],"sourcesContent":["/**\n * Group shape wins; each button may override tone. Disabled is cumulative so no child can\n * reactivate a disabled row.\n */\n\nimport type { InjectionKey } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\n\nexport interface ButtonGroupContext {\n /** How much visual weight every segment carries. */\n readonly variant?: ButtonVariant\n /** The colour a segment takes unless it names one of its own. */\n readonly tone?: ButtonTone\n /** The height of the segments. */\n readonly size?: ButtonSize\n /** The reduced density. */\n readonly compact?: boolean\n /** Whether the segments are raised off the page. */\n readonly elevated?: boolean\n /** Whether the whole row is unusable, which no segment can refuse. */\n readonly disabled?: boolean\n}\n\nexport const buttonGroupKey: InjectionKey<ButtonGroupContext> = Symbol('v-button-group')\n\n/**\n * The empty context a component provides to stop a row's at its own boundary. Every member\n * being `undefined`, `??` and `||` hand each button below it back its own prop.\n */\nexport const NO_BUTTON_GROUP: ButtonGroupContext = Object.freeze({})\n"],"mappings":";AAwBA,IAAaA,IAAmD,OAAO,gBAAgB,GAM1EC,IAAsC,OAAO,OAAO,CAAC,CAAC"}
1
+ {"version":3,"file":"context.js","names":["buttonGroupKey","NO_BUTTON_GROUP"],"sources":["../../../src/components/VButton/context.ts"],"sourcesContent":["/**\n * Group shape wins; each button may override tone. Disabled is cumulative so no child can\n * reactivate a disabled row.\n */\n\nimport type { InjectionKey } from 'vue'\n\nimport type { ButtonSize, ButtonTone, ButtonVariant } from './VButton.vue'\n\nexport interface ButtonGroupContext {\n /** How much visual weight every segment carries. */\n readonly variant?: ButtonVariant\n /** The colour a segment takes unless it names one of its own. */\n readonly tone?: ButtonTone\n /** The height of the segments. */\n readonly size?: ButtonSize\n /** Whether the segments are raised off the page. */\n readonly elevated?: boolean\n /** Whether the whole row is unusable, which no segment can refuse. */\n readonly disabled?: boolean\n}\n\nexport const buttonGroupKey: InjectionKey<ButtonGroupContext> = Symbol('v-button-group')\n\n/**\n * The empty context a component provides to stop a row's at its own boundary. Every member\n * being `undefined`, `??` and `||` hand each button below it back its own prop.\n */\nexport const NO_BUTTON_GROUP: ButtonGroupContext = Object.freeze({})\n"],"mappings":";AAsBA,IAAaA,IAAmD,OAAO,gBAAgB,GAM1EC,IAAsC,OAAO,OAAO,CAAC,CAAC"}
@@ -24,8 +24,6 @@ interface ChipProps {
24
24
  shape?: ChipShape;
25
25
  /** The height of the chip. */
26
26
  size?: ChipSize;
27
- /** Takes 4px off the height, leaving the padding, the text and the icons as they are. */
28
- compact?: boolean;
29
27
  /** Makes the chip a button that reacts to clicks, without holding a state. */
30
28
  clickable?: boolean;
31
29
  /**
@@ -106,7 +104,6 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {
106
104
  tone: ChipTone;
107
105
  color: string;
108
106
  shape: ChipShape;
109
- compact: boolean;
110
107
  clickable: boolean;
111
108
  href: string;
112
109
  selectable: boolean;