@birdycom/omniaui-vue 0.11.0 → 0.11.1

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 (178) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +49 -49
  3. package/dist/omniaui-vue.js.map +1 -1
  4. package/package.json +69 -69
  5. package/src/blocks/OcBlockAboutTimeline.vue +44 -44
  6. package/src/blocks/OcBlockAppHeader.vue +36 -36
  7. package/src/blocks/OcBlockAppShell.vue +29 -29
  8. package/src/blocks/OcBlockAuth.vue +24 -24
  9. package/src/blocks/OcBlockAuthSplit.vue +47 -47
  10. package/src/blocks/OcBlockContactGrid.vue +53 -53
  11. package/src/blocks/OcBlockCta.vue +12 -12
  12. package/src/blocks/OcBlockDataTable.vue +27 -27
  13. package/src/blocks/OcBlockFaq.vue +49 -49
  14. package/src/blocks/OcBlockFeatures.vue +23 -23
  15. package/src/blocks/OcBlockFooter.vue +25 -25
  16. package/src/blocks/OcBlockFormPage.vue +24 -24
  17. package/src/blocks/OcBlockHero.vue +13 -13
  18. package/src/blocks/OcBlockHeroCode.vue +42 -42
  19. package/src/blocks/OcBlockHeroOrbit.vue +69 -69
  20. package/src/blocks/OcBlockHeroSplit.vue +15 -15
  21. package/src/blocks/OcBlockModuleHeader.vue +21 -21
  22. package/src/blocks/OcBlockPageHero.vue +13 -13
  23. package/src/blocks/OcBlockPortfolioGrid.vue +63 -63
  24. package/src/blocks/OcBlockPricing.vue +39 -39
  25. package/src/blocks/OcBlockProcess.vue +39 -39
  26. package/src/blocks/OcBlockProductGrid.vue +41 -41
  27. package/src/blocks/OcBlockProfileHeader.vue +50 -50
  28. package/src/blocks/OcBlockShowcase.vue +43 -43
  29. package/src/blocks/OcBlockSiteFooter.vue +54 -54
  30. package/src/blocks/OcBlockSiteHeader.vue +34 -34
  31. package/src/blocks/OcBlockSpotlight.vue +47 -47
  32. package/src/blocks/OcBlockStats.vue +17 -17
  33. package/src/blocks/OcBlockStatsCounter.vue +20 -20
  34. package/src/blocks/OcBlockTeamList.vue +53 -53
  35. package/src/blocks/OcBlockTwoFactor.vue +61 -61
  36. package/src/blocks/OcBlockWizardDialog.vue +67 -67
  37. package/src/components/OcAccordion.vue +12 -12
  38. package/src/components/OcAccordionAccent.vue +27 -27
  39. package/src/components/OcAccordionItem.vue +19 -19
  40. package/src/components/OcAffixInput.vue +40 -40
  41. package/src/components/OcAlert.vue +17 -17
  42. package/src/components/OcAlertDialog.vue +66 -66
  43. package/src/components/OcAlphaTabs.vue +54 -54
  44. package/src/components/OcAspectRatio.vue +21 -21
  45. package/src/components/OcAttachment.vue +59 -59
  46. package/src/components/OcAttachmentImage.vue +13 -13
  47. package/src/components/OcAttachmentList.vue +31 -31
  48. package/src/components/OcAttachmentThumb.vue +14 -14
  49. package/src/components/OcAvatar.vue +18 -18
  50. package/src/components/OcAvatarGroup.vue +7 -7
  51. package/src/components/OcBackLink.vue +8 -8
  52. package/src/components/OcBadge.vue +13 -13
  53. package/src/components/OcBarcodeInput.vue +51 -51
  54. package/src/components/OcBreadcrumb.vue +22 -22
  55. package/src/components/OcBrowserFrame.vue +33 -33
  56. package/src/components/OcBusyOverlay.vue +32 -32
  57. package/src/components/OcButton.vue +47 -47
  58. package/src/components/OcButtonGroup.vue +10 -10
  59. package/src/components/OcButtonGroupText.vue +7 -7
  60. package/src/components/OcCard.vue +20 -20
  61. package/src/components/OcChartLine.vue +80 -80
  62. package/src/components/OcChatComposer.vue +42 -42
  63. package/src/components/OcCheckbox.vue +26 -26
  64. package/src/components/OcChecklist.vue +29 -29
  65. package/src/components/OcCollapsible.vue +19 -19
  66. package/src/components/OcColorField.vue +15 -15
  67. package/src/components/OcColorSelect.vue +33 -33
  68. package/src/components/OcColumnMenu.vue +50 -50
  69. package/src/components/OcCommand.vue +119 -119
  70. package/src/components/OcConfirmDialog.vue +65 -65
  71. package/src/components/OcCookieConsent.vue +119 -119
  72. package/src/components/OcCopyField.vue +37 -37
  73. package/src/components/OcCounter.vue +15 -15
  74. package/src/components/OcDataTable.vue +144 -144
  75. package/src/components/OcDefinitionList.vue +35 -35
  76. package/src/components/OcDropdown.vue +23 -23
  77. package/src/components/OcDropdownItem.vue +9 -9
  78. package/src/components/OcDropdownLabel.vue +7 -7
  79. package/src/components/OcDropdownSeparator.vue +7 -7
  80. package/src/components/OcDropzone.vue +36 -36
  81. package/src/components/OcEmojiPicker.vue +45 -45
  82. package/src/components/OcEmptyState.vue +13 -13
  83. package/src/components/OcEntityCard.vue +47 -47
  84. package/src/components/OcFactList.vue +37 -37
  85. package/src/components/OcFilterCounts.vue +38 -38
  86. package/src/components/OcFlipCard.vue +36 -36
  87. package/src/components/OcFormField.vue +36 -36
  88. package/src/components/OcGreeting.vue +52 -52
  89. package/src/components/OcHeading.vue +19 -19
  90. package/src/components/OcHoverCard.vue +25 -25
  91. package/src/components/OcImageZoom.vue +39 -39
  92. package/src/components/OcInput.vue +9 -9
  93. package/src/components/OcInputGroup.vue +12 -12
  94. package/src/components/OcItem.vue +40 -40
  95. package/src/components/OcItemGroup.vue +7 -7
  96. package/src/components/OcItemSeparator.vue +7 -7
  97. package/src/components/OcKbd.vue +7 -7
  98. package/src/components/OcKbdGroup.vue +15 -15
  99. package/src/components/OcLabel.vue +10 -10
  100. package/src/components/OcLanguageSelect.vue +40 -40
  101. package/src/components/OcLauncherCard.vue +34 -34
  102. package/src/components/OcLightbox.vue +63 -63
  103. package/src/components/OcLinkArrow.vue +25 -25
  104. package/src/components/OcListToolbar.vue +44 -44
  105. package/src/components/OcMarquee.vue +36 -36
  106. package/src/components/OcMediaCard.vue +28 -28
  107. package/src/components/OcMediaRow.vue +28 -28
  108. package/src/components/OcMediaThumb.vue +38 -38
  109. package/src/components/OcMessage.vue +91 -91
  110. package/src/components/OcMessageList.vue +43 -43
  111. package/src/components/OcMessageThread.vue +155 -155
  112. package/src/components/OcModal.vue +42 -42
  113. package/src/components/OcNavmenu.vue +18 -18
  114. package/src/components/OcNavmenuList.vue +38 -38
  115. package/src/components/OcNextTeaser.vue +25 -25
  116. package/src/components/OcNumberInput.vue +47 -47
  117. package/src/components/OcOtpInput.vue +34 -34
  118. package/src/components/OcPageHeader.vue +34 -34
  119. package/src/components/OcPagination.vue +33 -33
  120. package/src/components/OcPasswordInput.vue +27 -27
  121. package/src/components/OcPasswordRequirements.vue +28 -28
  122. package/src/components/OcPhoneInput.vue +72 -72
  123. package/src/components/OcPopover.vue +25 -25
  124. package/src/components/OcPriceCard.vue +69 -69
  125. package/src/components/OcProductMark.vue +31 -31
  126. package/src/components/OcProgress.vue +21 -21
  127. package/src/components/OcProjectCard.vue +36 -36
  128. package/src/components/OcProse.vue +57 -57
  129. package/src/components/OcRadio.vue +40 -40
  130. package/src/components/OcRadioGroup.vue +32 -32
  131. package/src/components/OcRecoveryCodes.vue +55 -55
  132. package/src/components/OcScrollArea.vue +15 -15
  133. package/src/components/OcSegmented.vue +28 -28
  134. package/src/components/OcSelect.vue +21 -21
  135. package/src/components/OcSeparator.vue +18 -18
  136. package/src/components/OcSeverityCard.vue +46 -46
  137. package/src/components/OcSheet.vue +49 -49
  138. package/src/components/OcSidebarCollapsible.vue +19 -19
  139. package/src/components/OcSidebarGroup.vue +11 -11
  140. package/src/components/OcSidebarMenu.vue +7 -7
  141. package/src/components/OcSidebarMenuButton.vue +31 -31
  142. package/src/components/OcSidebarSeparator.vue +7 -7
  143. package/src/components/OcSkeleton.vue +8 -8
  144. package/src/components/OcSlider.vue +27 -27
  145. package/src/components/OcSortableRow.vue +40 -40
  146. package/src/components/OcSpinner.vue +13 -13
  147. package/src/components/OcStackCard.vue +24 -24
  148. package/src/components/OcStackList.vue +31 -31
  149. package/src/components/OcStatCard.vue +39 -39
  150. package/src/components/OcStepper.vue +34 -34
  151. package/src/components/OcSwitch.vue +15 -15
  152. package/src/components/OcTable.vue +115 -115
  153. package/src/components/OcTableActions.vue +26 -26
  154. package/src/components/OcTableCompact.vue +46 -46
  155. package/src/components/OcTabs.vue +37 -37
  156. package/src/components/OcTagInput.vue +50 -50
  157. package/src/components/OcTerminalLog.vue +50 -50
  158. package/src/components/OcTextarea.vue +9 -9
  159. package/src/components/OcThemeSwitch.vue +31 -31
  160. package/src/components/OcTimeInput.vue +50 -50
  161. package/src/components/OcTimeline.vue +36 -36
  162. package/src/components/OcToast.vue +31 -31
  163. package/src/components/OcToastContainer.vue +21 -21
  164. package/src/components/OcToggle.vue +63 -63
  165. package/src/components/OcToggleField.vue +28 -28
  166. package/src/components/OcToggleGroup.vue +40 -40
  167. package/src/components/OcTooltip.vue +8 -8
  168. package/src/components/OcTopProgress.vue +54 -54
  169. package/src/components/OcTree.vue +70 -70
  170. package/src/components/OcUpgradeBanner.vue +42 -42
  171. package/src/components/OcUser.vue +25 -25
  172. package/src/components/OcVersionBanner.vue +34 -34
  173. package/src/composables/chartLine.ts +28 -28
  174. package/src/composables/keys.ts +22 -22
  175. package/src/composables/useAttrsSplit.ts +16 -16
  176. package/src/composables/useDialog.ts +21 -21
  177. package/src/composables/useToast.ts +66 -66
  178. package/src/composables/useUid.ts +12 -12
@@ -1,39 +1,39 @@
1
- <!-- File: packages/vue/src/components/OcImageZoom.vue | Map: props + v-model:zoom -> toolbar (radio levels) -> viewport -> canvas -->
2
- <script setup lang="ts">
3
- /**
4
- * Zoom / pan viewport (same markup as snippets/image-zoom.html). v-model:zoom = current level (default 1).
5
- * levels: toolbar values (default [1, 1.5, 2, 3]; [] = no toolbar). ratio: square | photo | CSS ratio.
6
- * Default slot replaces the <img>. Classes: .oc-image-zoom(--square/--photo), __toolbar, __label, __level, __viewport, __canvas
7
- */
8
- import { computed } from 'vue'
9
- import { useUid } from '../composables/useUid'
10
-
11
- const props = withDefaults(defineProps<{ src?: string; alt?: string; levels?: number[]; ratio?: string }>(), {
12
- alt: '',
13
- levels: () => [1, 1.5, 2, 3],
14
- })
15
- const zoom = defineModel<number>('zoom', { default: 1 })
16
- const name = useUid('oc-zoom')
17
- const preset = computed(() => (props.ratio === 'square' || props.ratio === 'photo' ? props.ratio : undefined))
18
- // Writing --oc-zoom inline means any level works, not only the ones the CSS :has() rules know.
19
- const vars = computed(() => ({ '--oc-zoom': String(zoom.value), ...(props.ratio && !preset.value ? { '--oc-image-zoom-ratio': props.ratio } : {}) }))
20
- </script>
21
-
22
- <template>
23
- <div :class="['oc-image-zoom', preset && `oc-image-zoom--${preset}`]" :style="vars">
24
- <div v-if="levels.length" class="oc-image-zoom__toolbar" role="radiogroup" aria-label="Zoom level">
25
- <span class="oc-image-zoom__label">Zoom</span>
26
- <label v-for="l in levels" :key="l" class="oc-image-zoom__level">
27
- <input v-model="zoom" type="radio" :name="name" :value="l" />{{ Math.round(l * 100) }}%
28
- </label>
29
- </div>
30
- <div class="oc-image-zoom__viewport" tabindex="0" role="region" :aria-label="alt ? `${alt}, scroll to pan` : 'Scroll to pan'">
31
- <div class="oc-image-zoom__canvas">
32
- <slot>
33
- <img v-if="src" :src="src" :alt="alt" />
34
- <div v-else class="oc-media-art" role="img" :aria-label="alt" />
35
- </slot>
36
- </div>
37
- </div>
38
- </div>
39
- </template>
1
+ <!-- File: packages/vue/src/components/OcImageZoom.vue | Map: props + v-model:zoom -> toolbar (radio levels) -> viewport -> canvas -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Zoom / pan viewport (same markup as snippets/image-zoom.html). v-model:zoom = current level (default 1).
5
+ * levels: toolbar values (default [1, 1.5, 2, 3]; [] = no toolbar). ratio: square | photo | CSS ratio.
6
+ * Default slot replaces the <img>. Classes: .oc-image-zoom(--square/--photo), __toolbar, __label, __level, __viewport, __canvas
7
+ */
8
+ import { computed } from 'vue'
9
+ import { useUid } from '../composables/useUid'
10
+
11
+ const props = withDefaults(defineProps<{ src?: string; alt?: string; levels?: number[]; ratio?: string }>(), {
12
+ alt: '',
13
+ levels: () => [1, 1.5, 2, 3],
14
+ })
15
+ const zoom = defineModel<number>('zoom', { default: 1 })
16
+ const name = useUid('oc-zoom')
17
+ const preset = computed(() => (props.ratio === 'square' || props.ratio === 'photo' ? props.ratio : undefined))
18
+ // Writing --oc-zoom inline means any level works, not only the ones the CSS :has() rules know.
19
+ const vars = computed(() => ({ '--oc-zoom': String(zoom.value), ...(props.ratio && !preset.value ? { '--oc-image-zoom-ratio': props.ratio } : {}) }))
20
+ </script>
21
+
22
+ <template>
23
+ <div :class="['oc-image-zoom', preset && `oc-image-zoom--${preset}`]" :style="vars">
24
+ <div v-if="levels.length" class="oc-image-zoom__toolbar" role="radiogroup" aria-label="Zoom level">
25
+ <span class="oc-image-zoom__label">Zoom</span>
26
+ <label v-for="l in levels" :key="l" class="oc-image-zoom__level">
27
+ <input v-model="zoom" type="radio" :name="name" :value="l" />{{ Math.round(l * 100) }}%
28
+ </label>
29
+ </div>
30
+ <div class="oc-image-zoom__viewport" tabindex="0" role="region" :aria-label="alt ? `${alt}, scroll to pan` : 'Scroll to pan'">
31
+ <div class="oc-image-zoom__canvas">
32
+ <slot>
33
+ <img v-if="src" :src="src" :alt="alt" />
34
+ <div v-else class="oc-media-art" role="img" :aria-label="alt" />
35
+ </slot>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </template>
@@ -1,9 +1,9 @@
1
- <script setup lang="ts">
2
- /** Text input. v-model. `error` sets .oc-input--error + aria-invalid. Any native attribute falls through. */
3
- defineProps<{ error?: boolean }>()
4
- const model = defineModel<string | number>()
5
- </script>
6
-
7
- <template>
8
- <input v-model="model" :class="['oc-input', error && 'oc-input--error']" :aria-invalid="error ? 'true' : undefined" />
9
- </template>
1
+ <script setup lang="ts">
2
+ /** Text input. v-model. `error` sets .oc-input--error + aria-invalid. Any native attribute falls through. */
3
+ defineProps<{ error?: boolean }>()
4
+ const model = defineModel<string | number>()
5
+ </script>
6
+
7
+ <template>
8
+ <input v-model="model" :class="['oc-input', error && 'oc-input--error']" :aria-invalid="error ? 'true' : undefined" />
9
+ </template>
@@ -1,12 +1,12 @@
1
- <script setup lang="ts">
2
- /** Input with prefix/suffix addon. Default slot = the input(s); `prefix` / `suffix` are text props or slots. */
3
- defineProps<{ prefix?: string; suffix?: string }>()
4
- </script>
5
-
6
- <template>
7
- <div class="oc-input-group">
8
- <span v-if="prefix || $slots.prefix" class="oc-input-addon"><slot name="prefix">{{ prefix }}</slot></span>
9
- <slot />
10
- <span v-if="suffix || $slots.suffix" class="oc-input-addon"><slot name="suffix">{{ suffix }}</slot></span>
11
- </div>
12
- </template>
1
+ <script setup lang="ts">
2
+ /** Input with prefix/suffix addon. Default slot = the input(s); `prefix` / `suffix` are text props or slots. */
3
+ defineProps<{ prefix?: string; suffix?: string }>()
4
+ </script>
5
+
6
+ <template>
7
+ <div class="oc-input-group">
8
+ <span v-if="prefix || $slots.prefix" class="oc-input-addon"><slot name="prefix">{{ prefix }}</slot></span>
9
+ <slot />
10
+ <span v-if="suffix || $slots.suffix" class="oc-input-addon"><slot name="suffix">{{ suffix }}</slot></span>
11
+ </div>
12
+ </template>
@@ -1,40 +1,40 @@
1
- <script setup lang="ts">
2
- /**
3
- * List row. Renders <a> when `href` is set. Slots: media (left; `icon` boxes it, `image` makes it an image),
4
- * default (replaces title + description), actions (right), header / footer (full-width rows).
5
- * Classes: .oc-item(--outline/--muted/--sm/--interactive), -media(--icon/--image), -content, -title, -description,
6
- * -actions, -header, -footer
7
- */
8
- withDefaults(
9
- defineProps<{
10
- variant?: 'default' | 'outline' | 'muted'
11
- size?: 'sm' | 'md'
12
- interactive?: boolean
13
- href?: string
14
- title?: string
15
- description?: string
16
- icon?: boolean
17
- image?: boolean
18
- }>(),
19
- { variant: 'default', size: 'md' },
20
- )
21
- </script>
22
-
23
- <template>
24
- <component
25
- :is="href ? 'a' : 'div'"
26
- :href="href"
27
- :class="['oc-item', variant !== 'default' && `oc-item--${variant}`, size === 'sm' && 'oc-item--sm', interactive && 'oc-item--interactive']"
28
- >
29
- <div v-if="$slots.header" class="oc-item-header"><slot name="header" /></div>
30
- <div v-if="$slots.media" :class="['oc-item-media', icon && 'oc-item-media--icon', image && 'oc-item-media--image']"><slot name="media" /></div>
31
- <div class="oc-item-content">
32
- <slot>
33
- <p v-if="title" class="oc-item-title">{{ title }}</p>
34
- <p v-if="description" class="oc-item-description">{{ description }}</p>
35
- </slot>
36
- </div>
37
- <div v-if="$slots.actions" class="oc-item-actions"><slot name="actions" /></div>
38
- <div v-if="$slots.footer" class="oc-item-footer"><slot name="footer" /></div>
39
- </component>
40
- </template>
1
+ <script setup lang="ts">
2
+ /**
3
+ * List row. Renders <a> when `href` is set. Slots: media (left; `icon` boxes it, `image` makes it an image),
4
+ * default (replaces title + description), actions (right), header / footer (full-width rows).
5
+ * Classes: .oc-item(--outline/--muted/--sm/--interactive), -media(--icon/--image), -content, -title, -description,
6
+ * -actions, -header, -footer
7
+ */
8
+ withDefaults(
9
+ defineProps<{
10
+ variant?: 'default' | 'outline' | 'muted'
11
+ size?: 'sm' | 'md'
12
+ interactive?: boolean
13
+ href?: string
14
+ title?: string
15
+ description?: string
16
+ icon?: boolean
17
+ image?: boolean
18
+ }>(),
19
+ { variant: 'default', size: 'md' },
20
+ )
21
+ </script>
22
+
23
+ <template>
24
+ <component
25
+ :is="href ? 'a' : 'div'"
26
+ :href="href"
27
+ :class="['oc-item', variant !== 'default' && `oc-item--${variant}`, size === 'sm' && 'oc-item--sm', interactive && 'oc-item--interactive']"
28
+ >
29
+ <div v-if="$slots.header" class="oc-item-header"><slot name="header" /></div>
30
+ <div v-if="$slots.media" :class="['oc-item-media', icon && 'oc-item-media--icon', image && 'oc-item-media--image']"><slot name="media" /></div>
31
+ <div class="oc-item-content">
32
+ <slot>
33
+ <p v-if="title" class="oc-item-title">{{ title }}</p>
34
+ <p v-if="description" class="oc-item-description">{{ description }}</p>
35
+ </slot>
36
+ </div>
37
+ <div v-if="$slots.actions" class="oc-item-actions"><slot name="actions" /></div>
38
+ <div v-if="$slots.footer" class="oc-item-footer"><slot name="footer" /></div>
39
+ </component>
40
+ </template>
@@ -1,7 +1,7 @@
1
- <script setup lang="ts">
2
- /** Vertical list of OcItem. `separated` puts a separator between the direct children is left to OcItemSeparator. Class: .oc-item-group */
3
- </script>
4
-
5
- <template>
6
- <div class="oc-item-group"><slot /></div>
7
- </template>
1
+ <script setup lang="ts">
2
+ /** Vertical list of OcItem. `separated` puts a separator between the direct children is left to OcItemSeparator. Class: .oc-item-group */
3
+ </script>
4
+
5
+ <template>
6
+ <div class="oc-item-group"><slot /></div>
7
+ </template>
@@ -1,7 +1,7 @@
1
- <script setup lang="ts">
2
- /** Divider between items in an OcItemGroup. Class: .oc-item-separator */
3
- </script>
4
-
5
- <template>
6
- <hr class="oc-item-separator" />
7
- </template>
1
+ <script setup lang="ts">
2
+ /** Divider between items in an OcItemGroup. Class: .oc-item-separator */
3
+ </script>
4
+
5
+ <template>
6
+ <hr class="oc-item-separator" />
7
+ </template>
@@ -1,7 +1,7 @@
1
- <script setup lang="ts">
2
- /** One key. Class: .oc-kbd */
3
- </script>
4
-
5
- <template>
6
- <kbd class="oc-kbd"><slot /></kbd>
7
- </template>
1
+ <script setup lang="ts">
2
+ /** One key. Class: .oc-kbd */
3
+ </script>
4
+
5
+ <template>
6
+ <kbd class="oc-kbd"><slot /></kbd>
7
+ </template>
@@ -1,15 +1,15 @@
1
- <script setup lang="ts">
2
- /** Shortcut: `keys` ["Ctrl", "K"] renders one key per entry, with a "+" separator when `separator` is set. Or pass OcKbd in the slot. Classes: .oc-kbd-group, .oc-kbd-sep */
3
- defineProps<{ keys?: string[]; separator?: boolean }>()
4
- </script>
5
-
6
- <template>
7
- <span class="oc-kbd-group">
8
- <slot>
9
- <template v-for="(key, i) in keys" :key="key + i">
10
- <span v-if="separator && i > 0" class="oc-kbd-sep">+</span>
11
- <kbd class="oc-kbd">{{ key }}</kbd>
12
- </template>
13
- </slot>
14
- </span>
15
- </template>
1
+ <script setup lang="ts">
2
+ /** Shortcut: `keys` ["Ctrl", "K"] renders one key per entry, with a "+" separator when `separator` is set. Or pass OcKbd in the slot. Classes: .oc-kbd-group, .oc-kbd-sep */
3
+ defineProps<{ keys?: string[]; separator?: boolean }>()
4
+ </script>
5
+
6
+ <template>
7
+ <span class="oc-kbd-group">
8
+ <slot>
9
+ <template v-for="(key, i) in keys" :key="key + i">
10
+ <span v-if="separator && i > 0" class="oc-kbd-sep">+</span>
11
+ <kbd class="oc-kbd">{{ key }}</kbd>
12
+ </template>
13
+ </slot>
14
+ </span>
15
+ </template>
@@ -1,10 +1,10 @@
1
- <script setup lang="ts">
2
- /** Form label. `for` links it to a control. Classes: .oc-label(--required/--inline/--muted) */
3
- defineProps<{ for?: string; required?: boolean; inline?: boolean; muted?: boolean }>()
4
- </script>
5
-
6
- <template>
7
- <label :class="['oc-label', required && 'oc-label--required', inline && 'oc-label--inline', muted && 'oc-label--muted']" :for="$props.for">
8
- <slot />
9
- </label>
10
- </template>
1
+ <script setup lang="ts">
2
+ /** Form label. `for` links it to a control. Classes: .oc-label(--required/--inline/--muted) */
3
+ defineProps<{ for?: string; required?: boolean; inline?: boolean; muted?: boolean }>()
4
+ </script>
5
+
6
+ <template>
7
+ <label :class="['oc-label', required && 'oc-label--required', inline && 'oc-label--inline', muted && 'oc-label--muted']" :for="$props.for">
8
+ <slot />
9
+ </label>
10
+ </template>
@@ -1,40 +1,40 @@
1
- <!-- File: packages/vue/src/components/OcLanguageSelect.vue | Map: types + props -> template (label pill: icon slot + native select) -->
2
- <script setup lang="ts">
3
- /**
4
- * Compact language / currency switcher. v-model = selected value. Slot `icon` replaces the globe / symbol.
5
- * Same markup as packages/ui/snippets/language-select.html. class/style on the pill, other attributes on the select.
6
- */
7
- import { useAttrsSplit } from '../composables/useAttrsSplit'
8
-
9
- export interface LanguageSelectItem {
10
- value: string
11
- label: string
12
- }
13
-
14
- defineOptions({ inheritAttrs: false })
15
- defineProps<{
16
- options: LanguageSelectItem[]
17
- /** aria-label of the select (default "Language" / "Currency") */
18
- label?: string
19
- currency?: boolean
20
- ghost?: boolean
21
- /** Text icon, e.g. "€" (default: globe, or € with `currency`) */
22
- icon?: string
23
- }>()
24
- const model = defineModel<string>()
25
- const { root, control } = useAttrsSplit()
26
- </script>
27
-
28
- <template>
29
- <label v-bind="root" :class="['oc-language-select', ghost && 'oc-language-select--ghost', currency && 'oc-language-select--currency']">
30
- <span class="oc-language-select-icon" aria-hidden="true">
31
- <slot name="icon">
32
- <template v-if="icon || currency">{{ icon ?? '€' }}</template>
33
- <svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9" /><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" /></svg>
34
- </slot>
35
- </span>
36
- <select v-bind="control" v-model="model" class="oc-language-select-control" :aria-label="label ?? (currency ? 'Currency' : 'Language')">
37
- <option v-for="option in options" :key="option.value" :value="option.value">{{ option.label }}</option>
38
- </select>
39
- </label>
40
- </template>
1
+ <!-- File: packages/vue/src/components/OcLanguageSelect.vue | Map: types + props -> template (label pill: icon slot + native select) -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Compact language / currency switcher. v-model = selected value. Slot `icon` replaces the globe / symbol.
5
+ * Same markup as packages/ui/snippets/language-select.html. class/style on the pill, other attributes on the select.
6
+ */
7
+ import { useAttrsSplit } from '../composables/useAttrsSplit'
8
+
9
+ export interface LanguageSelectItem {
10
+ value: string
11
+ label: string
12
+ }
13
+
14
+ defineOptions({ inheritAttrs: false })
15
+ defineProps<{
16
+ options: LanguageSelectItem[]
17
+ /** aria-label of the select (default "Language" / "Currency") */
18
+ label?: string
19
+ currency?: boolean
20
+ ghost?: boolean
21
+ /** Text icon, e.g. "€" (default: globe, or € with `currency`) */
22
+ icon?: string
23
+ }>()
24
+ const model = defineModel<string>()
25
+ const { root, control } = useAttrsSplit()
26
+ </script>
27
+
28
+ <template>
29
+ <label v-bind="root" :class="['oc-language-select', ghost && 'oc-language-select--ghost', currency && 'oc-language-select--currency']">
30
+ <span class="oc-language-select-icon" aria-hidden="true">
31
+ <slot name="icon">
32
+ <template v-if="icon || currency">{{ icon ?? '€' }}</template>
33
+ <svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9" /><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" /></svg>
34
+ </slot>
35
+ </span>
36
+ <select v-bind="control" v-model="model" class="oc-language-select-control" :aria-label="label ?? (currency ? 'Currency' : 'Language')">
37
+ <option v-for="option in options" :key="option.value" :value="option.value">{{ option.label }}</option>
38
+ </select>
39
+ </label>
40
+ </template>
@@ -1,34 +1,34 @@
1
- <script setup lang="ts">
2
- /**
3
- * Large clickable card with icon, title and description. Renders <a> with `href`, <span> when disabled,
4
- * otherwise <button>. State: `current` (aria-current), `disabled` (aria-disabled).
5
- * Slots: icon, title (+ trailing badge), description, footer.
6
- */
7
- import { computed } from 'vue'
8
-
9
- const props = defineProps<{
10
- href?: string
11
- title?: string
12
- description?: string
13
- footer?: string
14
- current?: boolean
15
- disabled?: boolean
16
- }>()
17
- const tag = computed(() => (props.disabled ? 'span' : props.href ? 'a' : 'button'))
18
- </script>
19
-
20
- <template>
21
- <component
22
- :is="tag"
23
- class="oc-launcher"
24
- :href="tag === 'a' ? href : undefined"
25
- :type="tag === 'button' ? 'button' : undefined"
26
- :aria-current="current ? 'true' : undefined"
27
- :aria-disabled="disabled ? 'true' : undefined"
28
- >
29
- <span v-if="$slots.icon" class="oc-launcher-icon" aria-hidden="true"><slot name="icon" /></span>
30
- <h3 class="oc-launcher-title"><slot name="title">{{ title }}</slot></h3>
31
- <p v-if="description || $slots.description" class="oc-launcher-description"><slot name="description">{{ description }}</slot></p>
32
- <span v-if="footer || $slots.footer" class="oc-launcher-footer"><slot name="footer">{{ footer }}</slot></span>
33
- </component>
34
- </template>
1
+ <script setup lang="ts">
2
+ /**
3
+ * Large clickable card with icon, title and description. Renders <a> with `href`, <span> when disabled,
4
+ * otherwise <button>. State: `current` (aria-current), `disabled` (aria-disabled).
5
+ * Slots: icon, title (+ trailing badge), description, footer.
6
+ */
7
+ import { computed } from 'vue'
8
+
9
+ const props = defineProps<{
10
+ href?: string
11
+ title?: string
12
+ description?: string
13
+ footer?: string
14
+ current?: boolean
15
+ disabled?: boolean
16
+ }>()
17
+ const tag = computed(() => (props.disabled ? 'span' : props.href ? 'a' : 'button'))
18
+ </script>
19
+
20
+ <template>
21
+ <component
22
+ :is="tag"
23
+ class="oc-launcher"
24
+ :href="tag === 'a' ? href : undefined"
25
+ :type="tag === 'button' ? 'button' : undefined"
26
+ :aria-current="current ? 'true' : undefined"
27
+ :aria-disabled="disabled ? 'true' : undefined"
28
+ >
29
+ <span v-if="$slots.icon" class="oc-launcher-icon" aria-hidden="true"><slot name="icon" /></span>
30
+ <h3 class="oc-launcher-title"><slot name="title">{{ title }}</slot></h3>
31
+ <p v-if="description || $slots.description" class="oc-launcher-description"><slot name="description">{{ description }}</slot></p>
32
+ <span v-if="footer || $slots.footer" class="oc-launcher-footer"><slot name="footer">{{ footer }}</slot></span>
33
+ </component>
34
+ </template>
@@ -1,63 +1,63 @@
1
- <!-- File: packages/vue/src/components/OcLightbox.vue | Map: props + model -> open at photo -> thumbnails -> <dialog> (bar, track of slides) -->
2
- <script setup lang="ts">
3
- /**
4
- * Lightbox on a native <dialog> (same markup as snippets/lightbox.html). v-model:open controls it.
5
- * images: { src?, alt, caption?, thumb? }[] (no src = placeholder art). Prev / next are anchor links.
6
- * Slot: caption (scoped: { image, index }). Classes: .oc-lightbox(--single), __bar, __title, __close, __track,
7
- * __slide, __media, __nav(--prev/--next), __caption, __counter; .oc-lightbox-thumbs / -thumb.
8
- */
9
- import { nextTick, ref } from 'vue'
10
- import { useDialog } from '../composables/useDialog'
11
- import { useUid } from '../composables/useUid'
12
-
13
- const props = withDefaults(
14
- defineProps<{ images: { src?: string; alt: string; caption?: string; thumb?: string }[]; title?: string; label?: string; thumbnails?: boolean }>(),
15
- { label: 'Photo viewer', thumbnails: true },
16
- )
17
- const open = defineModel<boolean>('open', { default: false })
18
- const el = ref<HTMLDialogElement | null>(null)
19
- const { onClose } = useDialog(open, el)
20
- const base = useUid('oc-lightbox')
21
- const n = () => props.images.length
22
- const slideId = (i: number) => `${base}-${((i % n()) + n()) % n() + 1}`
23
-
24
- // Open the viewer and jump (without animation) to photo i.
25
- async function show(i: number) {
26
- open.value = true
27
- await nextTick()
28
- document.getElementById(slideId(i))?.scrollIntoView?.({ behavior: 'instant' as ScrollBehavior, block: 'nearest', inline: 'center' })
29
- }
30
- defineExpose({ show, close: () => (open.value = false) })
31
- </script>
32
-
33
- <template>
34
- <div v-if="thumbnails" class="oc-lightbox-thumbs">
35
- <button v-for="(img, i) in images" :key="i" type="button" class="oc-lightbox-thumb" :aria-label="`Open photo ${i + 1}: ${img.alt}`" @click="show(i)">
36
- <img v-if="img.thumb || img.src" :src="img.thumb || img.src" :alt="img.alt" />
37
- <span v-else class="oc-media-art" />
38
- </button>
39
- </div>
40
- <dialog ref="el" :class="['oc-lightbox', images.length === 1 && 'oc-lightbox--single']" closedby="any" :aria-label="label" @close="onClose">
41
- <div class="oc-lightbox__bar">
42
- <h2 v-if="title" class="oc-lightbox__title">{{ title }}</h2>
43
- <span v-else />
44
- <button type="button" class="oc-lightbox__close" aria-label="Close" @click="open = false">&#10005;</button>
45
- </div>
46
- <div class="oc-lightbox__track" tabindex="0" aria-label="Photos, use the arrow keys">
47
- <figure v-for="(img, i) in images" :id="slideId(i)" :key="i" class="oc-lightbox__slide">
48
- <div class="oc-lightbox__media">
49
- <img v-if="img.src" :src="img.src" :alt="img.alt" />
50
- <span v-else class="oc-media-art" role="img" :aria-label="img.alt" />
51
- </div>
52
- <template v-if="images.length > 1">
53
- <a class="oc-lightbox__nav oc-lightbox__nav--prev" :href="`#${slideId(i - 1)}`" aria-label="Previous photo">&#8592;</a>
54
- <a class="oc-lightbox__nav oc-lightbox__nav--next" :href="`#${slideId(i + 1)}`" aria-label="Next photo">&#8594;</a>
55
- </template>
56
- <figcaption class="oc-lightbox__caption">
57
- <span v-if="images.length > 1" class="oc-lightbox__counter">{{ i + 1 }} / {{ images.length }}</span>
58
- <slot name="caption" :image="img" :index="i">{{ img.caption ?? img.alt }}</slot>
59
- </figcaption>
60
- </figure>
61
- </div>
62
- </dialog>
63
- </template>
1
+ <!-- File: packages/vue/src/components/OcLightbox.vue | Map: props + model -> open at photo -> thumbnails -> <dialog> (bar, track of slides) -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Lightbox on a native <dialog> (same markup as snippets/lightbox.html). v-model:open controls it.
5
+ * images: { src?, alt, caption?, thumb? }[] (no src = placeholder art). Prev / next are anchor links.
6
+ * Slot: caption (scoped: { image, index }). Classes: .oc-lightbox(--single), __bar, __title, __close, __track,
7
+ * __slide, __media, __nav(--prev/--next), __caption, __counter; .oc-lightbox-thumbs / -thumb.
8
+ */
9
+ import { nextTick, ref } from 'vue'
10
+ import { useDialog } from '../composables/useDialog'
11
+ import { useUid } from '../composables/useUid'
12
+
13
+ const props = withDefaults(
14
+ defineProps<{ images: { src?: string; alt: string; caption?: string; thumb?: string }[]; title?: string; label?: string; thumbnails?: boolean }>(),
15
+ { label: 'Photo viewer', thumbnails: true },
16
+ )
17
+ const open = defineModel<boolean>('open', { default: false })
18
+ const el = ref<HTMLDialogElement | null>(null)
19
+ const { onClose } = useDialog(open, el)
20
+ const base = useUid('oc-lightbox')
21
+ const n = () => props.images.length
22
+ const slideId = (i: number) => `${base}-${((i % n()) + n()) % n() + 1}`
23
+
24
+ // Open the viewer and jump (without animation) to photo i.
25
+ async function show(i: number) {
26
+ open.value = true
27
+ await nextTick()
28
+ document.getElementById(slideId(i))?.scrollIntoView?.({ behavior: 'instant' as ScrollBehavior, block: 'nearest', inline: 'center' })
29
+ }
30
+ defineExpose({ show, close: () => (open.value = false) })
31
+ </script>
32
+
33
+ <template>
34
+ <div v-if="thumbnails" class="oc-lightbox-thumbs">
35
+ <button v-for="(img, i) in images" :key="i" type="button" class="oc-lightbox-thumb" :aria-label="`Open photo ${i + 1}: ${img.alt}`" @click="show(i)">
36
+ <img v-if="img.thumb || img.src" :src="img.thumb || img.src" :alt="img.alt" />
37
+ <span v-else class="oc-media-art" />
38
+ </button>
39
+ </div>
40
+ <dialog ref="el" :class="['oc-lightbox', images.length === 1 && 'oc-lightbox--single']" closedby="any" :aria-label="label" @close="onClose">
41
+ <div class="oc-lightbox__bar">
42
+ <h2 v-if="title" class="oc-lightbox__title">{{ title }}</h2>
43
+ <span v-else />
44
+ <button type="button" class="oc-lightbox__close" aria-label="Close" @click="open = false">&#10005;</button>
45
+ </div>
46
+ <div class="oc-lightbox__track" tabindex="0" aria-label="Photos, use the arrow keys">
47
+ <figure v-for="(img, i) in images" :id="slideId(i)" :key="i" class="oc-lightbox__slide">
48
+ <div class="oc-lightbox__media">
49
+ <img v-if="img.src" :src="img.src" :alt="img.alt" />
50
+ <span v-else class="oc-media-art" role="img" :aria-label="img.alt" />
51
+ </div>
52
+ <template v-if="images.length > 1">
53
+ <a class="oc-lightbox__nav oc-lightbox__nav--prev" :href="`#${slideId(i - 1)}`" aria-label="Previous photo">&#8592;</a>
54
+ <a class="oc-lightbox__nav oc-lightbox__nav--next" :href="`#${slideId(i + 1)}`" aria-label="Next photo">&#8594;</a>
55
+ </template>
56
+ <figcaption class="oc-lightbox__caption">
57
+ <span v-if="images.length > 1" class="oc-lightbox__counter">{{ i + 1 }} / {{ images.length }}</span>
58
+ <slot name="caption" :image="img" :index="i">{{ img.caption ?? img.alt }}</slot>
59
+ </figcaption>
60
+ </figure>
61
+ </div>
62
+ </dialog>
63
+ </template>