@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,50 +1,50 @@
1
- <!-- File: packages/vue/src/components/OcTerminalLog.vue | Map: props -> frame -> bar (dots, title, status) -> body (lines + prompt) -> auto-scroll -->
2
- <script setup lang="ts">
3
- /**
4
- * Dark console window (same markup as snippets/terminal-log.html).
5
- * Props: lines ({ level: info|success|warn|error|debug, message, time?, label? }[]), title, status ("Live" + pulsing dot),
6
- * prompt (text, or true for an empty prompt with cursor), height (max body height), autoScroll (default true), label.
7
- * Classes: .oc-terminal-log, __bar, __title, __status, __body, __line(--info/--success/--warn/--error/--debug), __time, __level, __msg, __prompt
8
- */
9
- import { nextTick, ref, watch } from 'vue'
10
-
11
- const props = withDefaults(
12
- defineProps<{
13
- lines: { level: 'info' | 'success' | 'warn' | 'error' | 'debug'; message: string; time?: string; label?: string }[]
14
- title?: string
15
- status?: string
16
- prompt?: string | boolean
17
- height?: string
18
- autoScroll?: boolean
19
- label?: string
20
- }>(),
21
- { autoScroll: true, label: 'Log output', prompt: false },
22
- )
23
- const body = ref<HTMLDivElement | null>(null)
24
- // Keep the newest line in view.
25
- watch(
26
- () => props.lines.length,
27
- async () => {
28
- await nextTick()
29
- if (props.autoScroll && body.value) body.value.scrollTop = body.value.scrollHeight
30
- },
31
- )
32
- </script>
33
-
34
- <template>
35
- <div class="oc-terminal-log" :style="height ? { '--oc-terminal-log-height': height } : undefined">
36
- <div class="oc-terminal-log__bar">
37
- <i /><i /><i />
38
- <span v-if="title" class="oc-terminal-log__title">{{ title }}</span>
39
- <span v-if="status" class="oc-terminal-log__status">{{ status }}</span>
40
- </div>
41
- <div ref="body" class="oc-terminal-log__body" role="log" aria-live="polite" :aria-label="label" tabindex="0">
42
- <p v-for="(l, i) in lines" :key="i" :class="['oc-terminal-log__line', `oc-terminal-log__line--${l.level}`]">
43
- <time v-if="l.time" class="oc-terminal-log__time">{{ l.time }}</time>
44
- <span class="oc-terminal-log__level">{{ l.label ?? (l.level === 'success' ? 'ok' : l.level) }}</span>
45
- <span class="oc-terminal-log__msg">{{ l.message }}</span>
46
- </p>
47
- <p v-if="prompt !== false" class="oc-terminal-log__prompt">{{ prompt === true ? '' : prompt }}</p>
48
- </div>
49
- </div>
50
- </template>
1
+ <!-- File: packages/vue/src/components/OcTerminalLog.vue | Map: props -> frame -> bar (dots, title, status) -> body (lines + prompt) -> auto-scroll -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Dark console window (same markup as snippets/terminal-log.html).
5
+ * Props: lines ({ level: info|success|warn|error|debug, message, time?, label? }[]), title, status ("Live" + pulsing dot),
6
+ * prompt (text, or true for an empty prompt with cursor), height (max body height), autoScroll (default true), label.
7
+ * Classes: .oc-terminal-log, __bar, __title, __status, __body, __line(--info/--success/--warn/--error/--debug), __time, __level, __msg, __prompt
8
+ */
9
+ import { nextTick, ref, watch } from 'vue'
10
+
11
+ const props = withDefaults(
12
+ defineProps<{
13
+ lines: { level: 'info' | 'success' | 'warn' | 'error' | 'debug'; message: string; time?: string; label?: string }[]
14
+ title?: string
15
+ status?: string
16
+ prompt?: string | boolean
17
+ height?: string
18
+ autoScroll?: boolean
19
+ label?: string
20
+ }>(),
21
+ { autoScroll: true, label: 'Log output', prompt: false },
22
+ )
23
+ const body = ref<HTMLDivElement | null>(null)
24
+ // Keep the newest line in view.
25
+ watch(
26
+ () => props.lines.length,
27
+ async () => {
28
+ await nextTick()
29
+ if (props.autoScroll && body.value) body.value.scrollTop = body.value.scrollHeight
30
+ },
31
+ )
32
+ </script>
33
+
34
+ <template>
35
+ <div class="oc-terminal-log" :style="height ? { '--oc-terminal-log-height': height } : undefined">
36
+ <div class="oc-terminal-log__bar">
37
+ <i /><i /><i />
38
+ <span v-if="title" class="oc-terminal-log__title">{{ title }}</span>
39
+ <span v-if="status" class="oc-terminal-log__status">{{ status }}</span>
40
+ </div>
41
+ <div ref="body" class="oc-terminal-log__body" role="log" aria-live="polite" :aria-label="label" tabindex="0">
42
+ <p v-for="(l, i) in lines" :key="i" :class="['oc-terminal-log__line', `oc-terminal-log__line--${l.level}`]">
43
+ <time v-if="l.time" class="oc-terminal-log__time">{{ l.time }}</time>
44
+ <span class="oc-terminal-log__level">{{ l.label ?? (l.level === 'success' ? 'ok' : l.level) }}</span>
45
+ <span class="oc-terminal-log__msg">{{ l.message }}</span>
46
+ </p>
47
+ <p v-if="prompt !== false" class="oc-terminal-log__prompt">{{ prompt === true ? '' : prompt }}</p>
48
+ </div>
49
+ </div>
50
+ </template>
@@ -1,9 +1,9 @@
1
- <script setup lang="ts">
2
- /** Multi-line input (uses .oc-input). v-model. */
3
- defineProps<{ error?: boolean }>()
4
- const model = defineModel<string>()
5
- </script>
6
-
7
- <template>
8
- <textarea v-model="model" :class="['oc-input', error && 'oc-input--error']" :aria-invalid="error ? 'true' : undefined"></textarea>
9
- </template>
1
+ <script setup lang="ts">
2
+ /** Multi-line input (uses .oc-input). v-model. */
3
+ defineProps<{ error?: boolean }>()
4
+ const model = defineModel<string>()
5
+ </script>
6
+
7
+ <template>
8
+ <textarea v-model="model" :class="['oc-input', error && 'oc-input--error']" :aria-invalid="error ? 'true' : undefined"></textarea>
9
+ </template>
@@ -1,31 +1,31 @@
1
- <script setup lang="ts">
2
- /**
3
- * System / light / dark switch (CSS only: color-scheme + :has()). v-model: 'system' | 'light' | 'dark'.
4
- * `options` limits the choices; `as="div"` renders a role="radiogroup" div instead of a fieldset.
5
- */
6
- import { useUid } from '../composables/useUid'
7
-
8
- type Theme = 'system' | 'light' | 'dark'
9
- const labels: Record<Theme, string> = { system: 'System', light: 'Light', dark: 'Dark' }
10
-
11
- withDefaults(
12
- defineProps<{
13
- options?: Theme[]
14
- as?: 'fieldset' | 'div'
15
- label?: string
16
- name?: string
17
- }>(),
18
- { options: () => ['system', 'light', 'dark'], as: 'fieldset', label: 'Color theme' },
19
- )
20
- const model = defineModel<Theme>({ default: 'system' })
21
- const uid = useUid('oc-theme')
22
- </script>
23
-
24
- <template>
25
- <component :is="as" class="oc-theme-switch" :role="as === 'div' ? 'radiogroup' : undefined" :aria-label="label">
26
- <label v-for="o in options" :key="o">
27
- <input v-model="model" type="radio" :name="name ?? uid" :value="o" />
28
- {{ labels[o] }}
29
- </label>
30
- </component>
31
- </template>
1
+ <script setup lang="ts">
2
+ /**
3
+ * System / light / dark switch (CSS only: color-scheme + :has()). v-model: 'system' | 'light' | 'dark'.
4
+ * `options` limits the choices; `as="div"` renders a role="radiogroup" div instead of a fieldset.
5
+ */
6
+ import { useUid } from '../composables/useUid'
7
+
8
+ type Theme = 'system' | 'light' | 'dark'
9
+ const labels: Record<Theme, string> = { system: 'System', light: 'Light', dark: 'Dark' }
10
+
11
+ withDefaults(
12
+ defineProps<{
13
+ options?: Theme[]
14
+ as?: 'fieldset' | 'div'
15
+ label?: string
16
+ name?: string
17
+ }>(),
18
+ { options: () => ['system', 'light', 'dark'], as: 'fieldset', label: 'Color theme' },
19
+ )
20
+ const model = defineModel<Theme>({ default: 'system' })
21
+ const uid = useUid('oc-theme')
22
+ </script>
23
+
24
+ <template>
25
+ <component :is="as" class="oc-theme-switch" :role="as === 'div' ? 'radiogroup' : undefined" :aria-label="label">
26
+ <label v-for="o in options" :key="o">
27
+ <input v-model="model" type="radio" :name="name ?? uid" :value="o" />
28
+ {{ labels[o] }}
29
+ </label>
30
+ </component>
31
+ </template>
@@ -1,50 +1,50 @@
1
- <!-- File: packages/vue/src/components/OcTimeInput.vue | Map: props + model -> format helpers -> template (text field + datalist) -->
2
- <script setup lang="ts">
3
- /**
4
- * 24h time field (HH:mm). v-model: "HH:mm" or "". `slots` fills a datalist with suggestions.
5
- * Same markup as packages/ui/snippets/time-input.html. JS only adds the colon ("0930" -> "09:30").
6
- * All attributes go to the input.
7
- */
8
- import { useUid } from '../composables/useUid'
9
-
10
- defineOptions({ inheritAttrs: false })
11
- defineProps<{ slots?: string[]; error?: boolean; placeholder?: string }>()
12
- const model = defineModel<string>({ default: '' })
13
- const listId = useUid('oc-time')
14
-
15
- /** Up to 4 digits with a colon after the first two. A picked datalist option is kept as is. */
16
- function onInput(e: Event) {
17
- const input = e.target as HTMLInputElement
18
- const v = input.value
19
- const digits = v.replace(/\D/g, '').slice(0, 4)
20
- model.value = /^\d{2}:\d{2}$/.test(v) ? v : digits.length <= 2 ? digits : `${digits.slice(0, 2)}:${digits.slice(2)}`
21
- input.value = model.value
22
- }
23
-
24
- /** On blur: "9" -> "09:00", "930" -> "09:30". */
25
- function onBlur() {
26
- const d = model.value.replace(/\D/g, '')
27
- if (d.length === 1 || d.length === 2) model.value = `${d.padStart(2, '0')}:00`
28
- else if (d.length === 3) model.value = `0${d[0]}:${d.slice(1)}`
29
- }
30
- </script>
31
-
32
- <template>
33
- <input
34
- v-bind="$attrs"
35
- class="oc-input oc-time-input"
36
- type="text"
37
- inputmode="numeric"
38
- maxlength="5"
39
- pattern="([01][0-9]|2[0-3]):[0-5][0-9]"
40
- :placeholder="placeholder ?? 'hh:mm'"
41
- :list="slots?.length ? listId : undefined"
42
- :aria-invalid="error ? 'true' : undefined"
43
- :value="model"
44
- @input="onInput"
45
- @blur="onBlur"
46
- />
47
- <datalist v-if="slots?.length" :id="listId">
48
- <option v-for="s in slots" :key="s" :value="s"></option>
49
- </datalist>
50
- </template>
1
+ <!-- File: packages/vue/src/components/OcTimeInput.vue | Map: props + model -> format helpers -> template (text field + datalist) -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * 24h time field (HH:mm). v-model: "HH:mm" or "". `slots` fills a datalist with suggestions.
5
+ * Same markup as packages/ui/snippets/time-input.html. JS only adds the colon ("0930" -> "09:30").
6
+ * All attributes go to the input.
7
+ */
8
+ import { useUid } from '../composables/useUid'
9
+
10
+ defineOptions({ inheritAttrs: false })
11
+ defineProps<{ slots?: string[]; error?: boolean; placeholder?: string }>()
12
+ const model = defineModel<string>({ default: '' })
13
+ const listId = useUid('oc-time')
14
+
15
+ /** Up to 4 digits with a colon after the first two. A picked datalist option is kept as is. */
16
+ function onInput(e: Event) {
17
+ const input = e.target as HTMLInputElement
18
+ const v = input.value
19
+ const digits = v.replace(/\D/g, '').slice(0, 4)
20
+ model.value = /^\d{2}:\d{2}$/.test(v) ? v : digits.length <= 2 ? digits : `${digits.slice(0, 2)}:${digits.slice(2)}`
21
+ input.value = model.value
22
+ }
23
+
24
+ /** On blur: "9" -> "09:00", "930" -> "09:30". */
25
+ function onBlur() {
26
+ const d = model.value.replace(/\D/g, '')
27
+ if (d.length === 1 || d.length === 2) model.value = `${d.padStart(2, '0')}:00`
28
+ else if (d.length === 3) model.value = `0${d[0]}:${d.slice(1)}`
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <input
34
+ v-bind="$attrs"
35
+ class="oc-input oc-time-input"
36
+ type="text"
37
+ inputmode="numeric"
38
+ maxlength="5"
39
+ pattern="([01][0-9]|2[0-3]):[0-5][0-9]"
40
+ :placeholder="placeholder ?? 'hh:mm'"
41
+ :list="slots?.length ? listId : undefined"
42
+ :aria-invalid="error ? 'true' : undefined"
43
+ :value="model"
44
+ @input="onInput"
45
+ @blur="onBlur"
46
+ />
47
+ <datalist v-if="slots?.length" :id="listId">
48
+ <option v-for="s in slots" :key="s" :value="s"></option>
49
+ </datalist>
50
+ </template>
@@ -1,36 +1,36 @@
1
- <!-- File: packages/vue/src/components/OcTimeline.vue | Map: props (items, size, accent) -> ol.oc-timeline -> li.oc-timeline__item -->
2
- <script setup lang="ts">
3
- /**
4
- * Vertical timeline. Pass `items`, or put your own li.oc-timeline__item elements in the default slot.
5
- * Classes: .oc-timeline (--lg, --accent), .oc-timeline__item (--current), __date, __title, __text
6
- */
7
- export interface TimelineEntry {
8
- date: string
9
- /** Renders a <time datetime> when set. */
10
- dateTime?: string
11
- title?: string
12
- text?: string
13
- current?: boolean
14
- }
15
- withDefaults(
16
- defineProps<{
17
- items?: TimelineEntry[]
18
- size?: 'default' | 'lg'
19
- accent?: boolean
20
- }>(),
21
- { items: () => [], size: 'default', accent: false },
22
- )
23
- </script>
24
-
25
- <template>
26
- <ol :class="['oc-timeline', size === 'lg' && 'oc-timeline--lg', accent && 'oc-timeline--accent']">
27
- <slot>
28
- <li v-for="(item, i) in items" :key="i" :class="['oc-timeline__item', item.current && 'oc-timeline__item--current']">
29
- <time v-if="item.dateTime" class="oc-timeline__date" :datetime="item.dateTime">{{ item.date }}</time>
30
- <span v-else class="oc-timeline__date">{{ item.date }}</span>
31
- <h3 v-if="item.title" class="oc-timeline__title">{{ item.title }}</h3>
32
- <p v-if="item.text" class="oc-timeline__text">{{ item.text }}</p>
33
- </li>
34
- </slot>
35
- </ol>
36
- </template>
1
+ <!-- File: packages/vue/src/components/OcTimeline.vue | Map: props (items, size, accent) -> ol.oc-timeline -> li.oc-timeline__item -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Vertical timeline. Pass `items`, or put your own li.oc-timeline__item elements in the default slot.
5
+ * Classes: .oc-timeline (--lg, --accent), .oc-timeline__item (--current), __date, __title, __text
6
+ */
7
+ export interface TimelineEntry {
8
+ date: string
9
+ /** Renders a <time datetime> when set. */
10
+ dateTime?: string
11
+ title?: string
12
+ text?: string
13
+ current?: boolean
14
+ }
15
+ withDefaults(
16
+ defineProps<{
17
+ items?: TimelineEntry[]
18
+ size?: 'default' | 'lg'
19
+ accent?: boolean
20
+ }>(),
21
+ { items: () => [], size: 'default', accent: false },
22
+ )
23
+ </script>
24
+
25
+ <template>
26
+ <ol :class="['oc-timeline', size === 'lg' && 'oc-timeline--lg', accent && 'oc-timeline--accent']">
27
+ <slot>
28
+ <li v-for="(item, i) in items" :key="i" :class="['oc-timeline__item', item.current && 'oc-timeline__item--current']">
29
+ <time v-if="item.dateTime" class="oc-timeline__date" :datetime="item.dateTime">{{ item.date }}</time>
30
+ <span v-else class="oc-timeline__date">{{ item.date }}</span>
31
+ <h3 v-if="item.title" class="oc-timeline__title">{{ item.title }}</h3>
32
+ <p v-if="item.text" class="oc-timeline__text">{{ item.text }}</p>
33
+ </li>
34
+ </slot>
35
+ </ol>
36
+ </template>
@@ -1,31 +1,31 @@
1
- <script setup lang="ts">
2
- /**
3
- * One notification. Emits `dismiss`. role="alert" for errors, role="status" otherwise.
4
- * Slots: icon, title, default (description). Classes: .oc-toast--success/error/warning/info, -icon, -body,
5
- * -title, -description, -close
6
- */
7
- const glyphs = { success: '✓', error: '!', warning: '!', info: 'i' } as const
8
-
9
- withDefaults(
10
- defineProps<{
11
- variant?: 'success' | 'error' | 'warning' | 'info'
12
- title?: string
13
- description?: string
14
- dismissible?: boolean
15
- dismissLabel?: string
16
- }>(),
17
- { variant: 'info', dismissible: true, dismissLabel: 'Dismiss' },
18
- )
19
- defineEmits<{ dismiss: [] }>()
20
- </script>
21
-
22
- <template>
23
- <div :class="['oc-toast', `oc-toast--${variant}`]" :role="variant === 'error' ? 'alert' : 'status'">
24
- <span class="oc-toast-icon" aria-hidden="true"><slot name="icon">{{ glyphs[variant] }}</slot></span>
25
- <div class="oc-toast-body">
26
- <p class="oc-toast-title"><slot name="title">{{ title }}</slot></p>
27
- <p v-if="description || $slots.default" class="oc-toast-description"><slot>{{ description }}</slot></p>
28
- </div>
29
- <button v-if="dismissible" class="oc-toast-close" type="button" :aria-label="dismissLabel" @click="$emit('dismiss')">&times;</button>
30
- </div>
31
- </template>
1
+ <script setup lang="ts">
2
+ /**
3
+ * One notification. Emits `dismiss`. role="alert" for errors, role="status" otherwise.
4
+ * Slots: icon, title, default (description). Classes: .oc-toast--success/error/warning/info, -icon, -body,
5
+ * -title, -description, -close
6
+ */
7
+ const glyphs = { success: '✓', error: '!', warning: '!', info: 'i' } as const
8
+
9
+ withDefaults(
10
+ defineProps<{
11
+ variant?: 'success' | 'error' | 'warning' | 'info'
12
+ title?: string
13
+ description?: string
14
+ dismissible?: boolean
15
+ dismissLabel?: string
16
+ }>(),
17
+ { variant: 'info', dismissible: true, dismissLabel: 'Dismiss' },
18
+ )
19
+ defineEmits<{ dismiss: [] }>()
20
+ </script>
21
+
22
+ <template>
23
+ <div :class="['oc-toast', `oc-toast--${variant}`]" :role="variant === 'error' ? 'alert' : 'status'">
24
+ <span class="oc-toast-icon" aria-hidden="true"><slot name="icon">{{ glyphs[variant] }}</slot></span>
25
+ <div class="oc-toast-body">
26
+ <p class="oc-toast-title"><slot name="title">{{ title }}</slot></p>
27
+ <p v-if="description || $slots.default" class="oc-toast-description"><slot>{{ description }}</slot></p>
28
+ </div>
29
+ <button v-if="dismissible" class="oc-toast-close" type="button" :aria-label="dismissLabel" @click="$emit('dismiss')">&times;</button>
30
+ </div>
31
+ </template>
@@ -1,21 +1,21 @@
1
- <script setup lang="ts">
2
- /** Fixed stack that renders the useToast() queue. Mount once near the app root. Classes: .oc-toast-container(--top/--static) */
3
- import { useToast } from '../composables/useToast'
4
- import OcToast from './OcToast.vue'
5
-
6
- withDefaults(defineProps<{ position?: 'bottom' | 'top'; static?: boolean }>(), { position: 'bottom' })
7
- const { toasts, dismiss } = useToast()
8
- </script>
9
-
10
- <template>
11
- <div :class="['oc-toast-container', position === 'top' && 'oc-toast-container--top', static && 'oc-toast-container--static']">
12
- <OcToast
13
- v-for="t in toasts"
14
- :key="t.id"
15
- :variant="t.variant"
16
- :title="t.title"
17
- :description="t.description"
18
- @dismiss="dismiss(t.id)"
19
- />
20
- </div>
21
- </template>
1
+ <script setup lang="ts">
2
+ /** Fixed stack that renders the useToast() queue. Mount once near the app root. Classes: .oc-toast-container(--top/--static) */
3
+ import { useToast } from '../composables/useToast'
4
+ import OcToast from './OcToast.vue'
5
+
6
+ withDefaults(defineProps<{ position?: 'bottom' | 'top'; static?: boolean }>(), { position: 'bottom' })
7
+ const { toasts, dismiss } = useToast()
8
+ </script>
9
+
10
+ <template>
11
+ <div :class="['oc-toast-container', position === 'top' && 'oc-toast-container--top', static && 'oc-toast-container--static']">
12
+ <OcToast
13
+ v-for="t in toasts"
14
+ :key="t.id"
15
+ :variant="t.variant"
16
+ :title="t.title"
17
+ :description="t.description"
18
+ @dismiss="dismiss(t.id)"
19
+ />
20
+ </div>
21
+ </template>
@@ -1,63 +1,63 @@
1
- <script setup lang="ts">
2
- /**
3
- * Toggle. Inside OcToggleGroup (give it a `value`) it is a label around a radio/checkbox bound to the group's v-model.
4
- * Standalone: v-model boolean on a label + checkbox, or `button` for a <button aria-pressed>.
5
- * Pass an aria-label attribute for icon-only toggles (it lands on the input/button).
6
- * Classes: .oc-toggle(--outline/--sm/--lg/--icon)
7
- */
8
- import { computed, inject } from 'vue'
9
- import { useAttrsSplit } from '../composables/useAttrsSplit'
10
- import { toggleGroupKey } from '../composables/keys'
11
-
12
- defineOptions({ inheritAttrs: false })
13
- const props = defineProps<{
14
- value?: string
15
- variant?: 'default' | 'outline'
16
- size?: 'sm' | 'md' | 'lg'
17
- icon?: boolean
18
- button?: boolean
19
- disabled?: boolean
20
- }>()
21
- const pressed = defineModel<boolean>({ default: false })
22
- const group = inject(toggleGroupKey, null)
23
- const { root, control } = useAttrsSplit()
24
-
25
- const classes = computed(() => [
26
- 'oc-toggle',
27
- props.variant === 'outline' && 'oc-toggle--outline',
28
- props.size && props.size !== 'md' && `oc-toggle--${props.size}`,
29
- props.icon && 'oc-toggle--icon',
30
- ])
31
- const inGroup = computed(() => !!group && props.value !== undefined)
32
- const isOn = computed(() => (inGroup.value ? group!.values.value.includes(props.value!) : pressed.value))
33
- function onChange() {
34
- if (inGroup.value) group!.toggle(props.value!)
35
- else pressed.value = !pressed.value
36
- }
37
- </script>
38
-
39
- <template>
40
- <button
41
- v-if="button && !inGroup"
42
- v-bind="{ ...root, ...control }"
43
- type="button"
44
- :class="classes"
45
- :aria-pressed="pressed ? 'true' : 'false'"
46
- :disabled="disabled"
47
- @click="pressed = !pressed"
48
- >
49
- <slot />
50
- </button>
51
- <label v-else v-bind="root" :class="classes">
52
- <input
53
- v-bind="control"
54
- :type="inGroup && group!.type.value === 'single' ? 'radio' : 'checkbox'"
55
- :name="inGroup ? group!.name.value : (control.name as string | undefined)"
56
- :value="value"
57
- :checked="isOn"
58
- :disabled="disabled"
59
- @change="onChange"
60
- />
61
- <slot />
62
- </label>
63
- </template>
1
+ <script setup lang="ts">
2
+ /**
3
+ * Toggle. Inside OcToggleGroup (give it a `value`) it is a label around a radio/checkbox bound to the group's v-model.
4
+ * Standalone: v-model boolean on a label + checkbox, or `button` for a <button aria-pressed>.
5
+ * Pass an aria-label attribute for icon-only toggles (it lands on the input/button).
6
+ * Classes: .oc-toggle(--outline/--sm/--lg/--icon)
7
+ */
8
+ import { computed, inject } from 'vue'
9
+ import { useAttrsSplit } from '../composables/useAttrsSplit'
10
+ import { toggleGroupKey } from '../composables/keys'
11
+
12
+ defineOptions({ inheritAttrs: false })
13
+ const props = defineProps<{
14
+ value?: string
15
+ variant?: 'default' | 'outline'
16
+ size?: 'sm' | 'md' | 'lg'
17
+ icon?: boolean
18
+ button?: boolean
19
+ disabled?: boolean
20
+ }>()
21
+ const pressed = defineModel<boolean>({ default: false })
22
+ const group = inject(toggleGroupKey, null)
23
+ const { root, control } = useAttrsSplit()
24
+
25
+ const classes = computed(() => [
26
+ 'oc-toggle',
27
+ props.variant === 'outline' && 'oc-toggle--outline',
28
+ props.size && props.size !== 'md' && `oc-toggle--${props.size}`,
29
+ props.icon && 'oc-toggle--icon',
30
+ ])
31
+ const inGroup = computed(() => !!group && props.value !== undefined)
32
+ const isOn = computed(() => (inGroup.value ? group!.values.value.includes(props.value!) : pressed.value))
33
+ function onChange() {
34
+ if (inGroup.value) group!.toggle(props.value!)
35
+ else pressed.value = !pressed.value
36
+ }
37
+ </script>
38
+
39
+ <template>
40
+ <button
41
+ v-if="button && !inGroup"
42
+ v-bind="{ ...root, ...control }"
43
+ type="button"
44
+ :class="classes"
45
+ :aria-pressed="pressed ? 'true' : 'false'"
46
+ :disabled="disabled"
47
+ @click="pressed = !pressed"
48
+ >
49
+ <slot />
50
+ </button>
51
+ <label v-else v-bind="root" :class="classes">
52
+ <input
53
+ v-bind="control"
54
+ :type="inGroup && group!.type.value === 'single' ? 'radio' : 'checkbox'"
55
+ :name="inGroup ? group!.name.value : (control.name as string | undefined)"
56
+ :value="value"
57
+ :checked="isOn"
58
+ :disabled="disabled"
59
+ @change="onChange"
60
+ />
61
+ <slot />
62
+ </label>
63
+ </template>