@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,25 +1,25 @@
1
- <script setup lang="ts">
2
- /**
3
- * Floating panel on the popover attribute + anchor positioning. Trigger = `trigger` slot (scoped: { id })
4
- * or a default outline button with `label`. Default slot = panel content. Classes: .oc-popover(--sm/--lg),
5
- * -title, -text. Attributes land on the panel.
6
- */
7
- import { useAttrs } from 'vue'
8
- import { useUid } from '../composables/useUid'
9
-
10
- defineOptions({ inheritAttrs: false })
11
- withDefaults(defineProps<{ label?: string; title?: string; text?: string; size?: 'sm' | 'md' | 'lg' }>(), { size: 'md' })
12
- const attrs = useAttrs()
13
- const id = (attrs.id as string | undefined) ?? useUid('oc-popover')
14
- </script>
15
-
16
- <template>
17
- <slot name="trigger" :id="id">
18
- <button class="oc-btn oc-btn--outline" type="button" :popovertarget="id">{{ label }}</button>
19
- </slot>
20
- <div v-bind="attrs" :id="id" :class="['oc-popover', size !== 'md' && `oc-popover--${size}`]" popover>
21
- <h3 v-if="title" class="oc-popover-title">{{ title }}</h3>
22
- <p v-if="text" class="oc-popover-text">{{ text }}</p>
23
- <slot />
24
- </div>
25
- </template>
1
+ <script setup lang="ts">
2
+ /**
3
+ * Floating panel on the popover attribute + anchor positioning. Trigger = `trigger` slot (scoped: { id })
4
+ * or a default outline button with `label`. Default slot = panel content. Classes: .oc-popover(--sm/--lg),
5
+ * -title, -text. Attributes land on the panel.
6
+ */
7
+ import { useAttrs } from 'vue'
8
+ import { useUid } from '../composables/useUid'
9
+
10
+ defineOptions({ inheritAttrs: false })
11
+ withDefaults(defineProps<{ label?: string; title?: string; text?: string; size?: 'sm' | 'md' | 'lg' }>(), { size: 'md' })
12
+ const attrs = useAttrs()
13
+ const id = (attrs.id as string | undefined) ?? useUid('oc-popover')
14
+ </script>
15
+
16
+ <template>
17
+ <slot name="trigger" :id="id">
18
+ <button class="oc-btn oc-btn--outline" type="button" :popovertarget="id">{{ label }}</button>
19
+ </slot>
20
+ <div v-bind="attrs" :id="id" :class="['oc-popover', size !== 'md' && `oc-popover--${size}`]" popover>
21
+ <h3 v-if="title" class="oc-popover-title">{{ title }}</h3>
22
+ <p v-if="text" class="oc-popover-text">{{ text }}</p>
23
+ <slot />
24
+ </div>
25
+ </template>
@@ -1,69 +1,69 @@
1
- <!-- File: packages/vue/src/components/OcPriceCard.vue | Map: props -> head -> toggle (radios) -> monthly / yearly price -> features -> cta slot -->
2
- <script setup lang="ts">
3
- /**
4
- * Plan card with a CSS-only monthly / yearly switch (radio + :has). Slots: cta, features (replaces the list items).
5
- * Classes: .oc-price-card (--featured), __head, __name, __discount, __desc, __toggle, __price(--monthly/--yearly),
6
- * __amount, __period, __was, __note, __features, __feature(--excluded), __cta
7
- */
8
- import { useUid } from '../composables/useUid'
9
-
10
- export interface PriceCardFeature {
11
- label: string
12
- excluded?: boolean
13
- }
14
- export interface PriceCardPrice {
15
- amount: string
16
- period?: string
17
- was?: string
18
- note?: string
19
- }
20
- withDefaults(
21
- defineProps<{
22
- name: string
23
- description?: string
24
- discount?: string
25
- monthly: PriceCardPrice
26
- yearly?: PriceCardPrice
27
- defaultPeriod?: 'monthly' | 'yearly'
28
- features?: PriceCardFeature[]
29
- featured?: boolean
30
- monthlyLabel?: string
31
- yearlyLabel?: string
32
- }>(),
33
- { defaultPeriod: 'monthly', features: () => [], monthlyLabel: 'Monthly', yearlyLabel: 'Yearly' },
34
- )
35
- // Every card needs its own radio group name.
36
- const group = useUid('oc-price')
37
- </script>
38
-
39
- <template>
40
- <article :class="['oc-price-card', featured && 'oc-price-card--featured']">
41
- <div class="oc-price-card__head">
42
- <h3 class="oc-price-card__name">{{ name }}</h3>
43
- <span v-if="discount" class="oc-price-card__discount">{{ discount }}</span>
44
- </div>
45
- <p v-if="description" class="oc-price-card__desc">{{ description }}</p>
46
- <div v-if="yearly" class="oc-segmented oc-price-card__toggle" role="radiogroup" aria-label="Billing period">
47
- <label class="oc-segmented-item"><input type="radio" :name="group" value="monthly" :checked="defaultPeriod === 'monthly'" /> {{ monthlyLabel }}</label>
48
- <label class="oc-segmented-item"><input type="radio" :name="group" value="yearly" :checked="defaultPeriod === 'yearly'" /> {{ yearlyLabel }}</label>
49
- </div>
50
- <p class="oc-price-card__price oc-price-card__price--monthly">
51
- <span class="oc-price-card__amount">{{ monthly.amount }}</span>
52
- <span v-if="monthly.period" class="oc-price-card__period">{{ monthly.period }}</span>
53
- <s v-if="monthly.was" class="oc-price-card__was">{{ monthly.was }}</s>
54
- <span v-if="monthly.note" class="oc-price-card__note">{{ monthly.note }}</span>
55
- </p>
56
- <p v-if="yearly" class="oc-price-card__price oc-price-card__price--yearly">
57
- <span class="oc-price-card__amount">{{ yearly.amount }}</span>
58
- <span v-if="yearly.period" class="oc-price-card__period">{{ yearly.period }}</span>
59
- <s v-if="yearly.was" class="oc-price-card__was">{{ yearly.was }}</s>
60
- <span v-if="yearly.note" class="oc-price-card__note">{{ yearly.note }}</span>
61
- </p>
62
- <ul v-if="features.length || $slots.features" class="oc-price-card__features">
63
- <slot name="features">
64
- <li v-for="(f, i) in features" :key="i" :class="['oc-price-card__feature', f.excluded && 'oc-price-card__feature--excluded']">{{ f.label }}</li>
65
- </slot>
66
- </ul>
67
- <slot name="cta" />
68
- </article>
69
- </template>
1
+ <!-- File: packages/vue/src/components/OcPriceCard.vue | Map: props -> head -> toggle (radios) -> monthly / yearly price -> features -> cta slot -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Plan card with a CSS-only monthly / yearly switch (radio + :has). Slots: cta, features (replaces the list items).
5
+ * Classes: .oc-price-card (--featured), __head, __name, __discount, __desc, __toggle, __price(--monthly/--yearly),
6
+ * __amount, __period, __was, __note, __features, __feature(--excluded), __cta
7
+ */
8
+ import { useUid } from '../composables/useUid'
9
+
10
+ export interface PriceCardFeature {
11
+ label: string
12
+ excluded?: boolean
13
+ }
14
+ export interface PriceCardPrice {
15
+ amount: string
16
+ period?: string
17
+ was?: string
18
+ note?: string
19
+ }
20
+ withDefaults(
21
+ defineProps<{
22
+ name: string
23
+ description?: string
24
+ discount?: string
25
+ monthly: PriceCardPrice
26
+ yearly?: PriceCardPrice
27
+ defaultPeriod?: 'monthly' | 'yearly'
28
+ features?: PriceCardFeature[]
29
+ featured?: boolean
30
+ monthlyLabel?: string
31
+ yearlyLabel?: string
32
+ }>(),
33
+ { defaultPeriod: 'monthly', features: () => [], monthlyLabel: 'Monthly', yearlyLabel: 'Yearly' },
34
+ )
35
+ // Every card needs its own radio group name.
36
+ const group = useUid('oc-price')
37
+ </script>
38
+
39
+ <template>
40
+ <article :class="['oc-price-card', featured && 'oc-price-card--featured']">
41
+ <div class="oc-price-card__head">
42
+ <h3 class="oc-price-card__name">{{ name }}</h3>
43
+ <span v-if="discount" class="oc-price-card__discount">{{ discount }}</span>
44
+ </div>
45
+ <p v-if="description" class="oc-price-card__desc">{{ description }}</p>
46
+ <div v-if="yearly" class="oc-segmented oc-price-card__toggle" role="radiogroup" aria-label="Billing period">
47
+ <label class="oc-segmented-item"><input type="radio" :name="group" value="monthly" :checked="defaultPeriod === 'monthly'" /> {{ monthlyLabel }}</label>
48
+ <label class="oc-segmented-item"><input type="radio" :name="group" value="yearly" :checked="defaultPeriod === 'yearly'" /> {{ yearlyLabel }}</label>
49
+ </div>
50
+ <p class="oc-price-card__price oc-price-card__price--monthly">
51
+ <span class="oc-price-card__amount">{{ monthly.amount }}</span>
52
+ <span v-if="monthly.period" class="oc-price-card__period">{{ monthly.period }}</span>
53
+ <s v-if="monthly.was" class="oc-price-card__was">{{ monthly.was }}</s>
54
+ <span v-if="monthly.note" class="oc-price-card__note">{{ monthly.note }}</span>
55
+ </p>
56
+ <p v-if="yearly" class="oc-price-card__price oc-price-card__price--yearly">
57
+ <span class="oc-price-card__amount">{{ yearly.amount }}</span>
58
+ <span v-if="yearly.period" class="oc-price-card__period">{{ yearly.period }}</span>
59
+ <s v-if="yearly.was" class="oc-price-card__was">{{ yearly.was }}</s>
60
+ <span v-if="yearly.note" class="oc-price-card__note">{{ yearly.note }}</span>
61
+ </p>
62
+ <ul v-if="features.length || $slots.features" class="oc-price-card__features">
63
+ <slot name="features">
64
+ <li v-for="(f, i) in features" :key="i" :class="['oc-price-card__feature', f.excluded && 'oc-price-card__feature--excluded']">{{ f.label }}</li>
65
+ </slot>
66
+ </ul>
67
+ <slot name="cta" />
68
+ </article>
69
+ </template>
@@ -1,31 +1,31 @@
1
- <!-- File: packages/vue/src/components/OcProductMark.vue | Map: props (size, accent, soft, glow, cover) -> span.oc-product-mark -->
2
- <script setup lang="ts">
3
- /**
4
- * Squircle app-icon tile in the accent gradient. Slot: glyph, <img alt=""> or SVG. Decorative (aria-hidden).
5
- * Classes: .oc-product-mark (--sm, --lg, --xl, --soft, --glow, --cover) + .oc-accent--*
6
- */
7
- withDefaults(
8
- defineProps<{
9
- size?: 'sm' | 'default' | 'lg' | 'xl'
10
- accent?: 'blue' | 'cyan' | 'green' | 'orange' | 'red' | 'violet' | 'yellow'
11
- soft?: boolean
12
- glow?: boolean
13
- cover?: boolean
14
- }>(),
15
- { size: 'default' },
16
- )
17
- </script>
18
-
19
- <template>
20
- <span
21
- :class="[
22
- 'oc-product-mark',
23
- size !== 'default' && `oc-product-mark--${size}`,
24
- soft && 'oc-product-mark--soft',
25
- glow && 'oc-product-mark--glow',
26
- cover && 'oc-product-mark--cover',
27
- accent && `oc-accent--${accent}`,
28
- ]"
29
- aria-hidden="true"
30
- ><slot /></span>
31
- </template>
1
+ <!-- File: packages/vue/src/components/OcProductMark.vue | Map: props (size, accent, soft, glow, cover) -> span.oc-product-mark -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Squircle app-icon tile in the accent gradient. Slot: glyph, <img alt=""> or SVG. Decorative (aria-hidden).
5
+ * Classes: .oc-product-mark (--sm, --lg, --xl, --soft, --glow, --cover) + .oc-accent--*
6
+ */
7
+ withDefaults(
8
+ defineProps<{
9
+ size?: 'sm' | 'default' | 'lg' | 'xl'
10
+ accent?: 'blue' | 'cyan' | 'green' | 'orange' | 'red' | 'violet' | 'yellow'
11
+ soft?: boolean
12
+ glow?: boolean
13
+ cover?: boolean
14
+ }>(),
15
+ { size: 'default' },
16
+ )
17
+ </script>
18
+
19
+ <template>
20
+ <span
21
+ :class="[
22
+ 'oc-product-mark',
23
+ size !== 'default' && `oc-product-mark--${size}`,
24
+ soft && 'oc-product-mark--soft',
25
+ glow && 'oc-product-mark--glow',
26
+ cover && 'oc-product-mark--cover',
27
+ accent && `oc-accent--${accent}`,
28
+ ]"
29
+ aria-hidden="true"
30
+ ><slot /></span>
31
+ </template>
@@ -1,21 +1,21 @@
1
- <script setup lang="ts">
2
- /** Progress bar. `value` of `max` (default 100). Classes: .oc-progress-bar--success/warning/danger */
3
- import { computed } from 'vue'
4
-
5
- const props = withDefaults(
6
- defineProps<{
7
- value: number
8
- max?: number
9
- variant?: 'default' | 'success' | 'warning' | 'danger'
10
- label?: string
11
- }>(),
12
- { max: 100, variant: 'default' },
13
- )
14
- const percent = computed(() => Math.min(100, Math.max(0, Math.round((props.value / props.max) * 100))))
15
- </script>
16
-
17
- <template>
18
- <div class="oc-progress" role="progressbar" :aria-valuenow="value" aria-valuemin="0" :aria-valuemax="max" :aria-label="label">
19
- <div :class="['oc-progress-bar', variant !== 'default' && `oc-progress-bar--${variant}`]" :style="{ width: `${percent}%` }"></div>
20
- </div>
21
- </template>
1
+ <script setup lang="ts">
2
+ /** Progress bar. `value` of `max` (default 100). Classes: .oc-progress-bar--success/warning/danger */
3
+ import { computed } from 'vue'
4
+
5
+ const props = withDefaults(
6
+ defineProps<{
7
+ value: number
8
+ max?: number
9
+ variant?: 'default' | 'success' | 'warning' | 'danger'
10
+ label?: string
11
+ }>(),
12
+ { max: 100, variant: 'default' },
13
+ )
14
+ const percent = computed(() => Math.min(100, Math.max(0, Math.round((props.value / props.max) * 100))))
15
+ </script>
16
+
17
+ <template>
18
+ <div class="oc-progress" role="progressbar" :aria-valuenow="value" aria-valuemin="0" :aria-valuemax="max" :aria-label="label">
19
+ <div :class="['oc-progress-bar', variant !== 'default' && `oc-progress-bar--${variant}`]" :style="{ width: `${percent}%` }"></div>
20
+ </div>
21
+ </template>
@@ -1,36 +1,36 @@
1
- <!-- File: packages/vue/src/components/OcProjectCard.vue | Map: props -> a.oc-project-card -> frame (bar + shot) -> body -->
2
- <script setup lang="ts">
3
- /**
4
- * Portfolio card with a browser-like preview. Without `image` the preview is an accent gradient.
5
- * Slots: type, name, meta, more. Classes: .oc-project-card (--flat), __frame, __bar, __shot, __body, __type, __name, __meta, __more
6
- */
7
- withDefaults(
8
- defineProps<{
9
- href: string
10
- name?: string
11
- type?: string
12
- meta?: string
13
- more?: string
14
- image?: string
15
- imageAlt?: string
16
- flat?: boolean
17
- }>(),
18
- { more: 'View project', imageAlt: '' },
19
- )
20
- </script>
21
-
22
- <template>
23
- <a :class="['oc-project-card', flat && 'oc-project-card--flat']" :href="href">
24
- <span class="oc-project-card__frame">
25
- <span class="oc-project-card__bar" aria-hidden="true"><i></i><i></i><i></i></span>
26
- <img v-if="image" class="oc-project-card__shot" :src="image" :alt="imageAlt" loading="lazy" />
27
- <span v-else class="oc-project-card__shot" role="img" :aria-label="imageAlt || undefined" :aria-hidden="imageAlt ? undefined : 'true'"></span>
28
- </span>
29
- <span class="oc-project-card__body">
30
- <span v-if="type || $slots.type" class="oc-project-card__type"><slot name="type">{{ type }}</slot></span>
31
- <span class="oc-project-card__name"><slot name="name">{{ name }}</slot></span>
32
- <span v-if="meta || $slots.meta" class="oc-project-card__meta"><slot name="meta">{{ meta }}</slot></span>
33
- <span v-if="more" class="oc-project-card__more"><slot name="more">{{ more }}</slot></span>
34
- </span>
35
- </a>
36
- </template>
1
+ <!-- File: packages/vue/src/components/OcProjectCard.vue | Map: props -> a.oc-project-card -> frame (bar + shot) -> body -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Portfolio card with a browser-like preview. Without `image` the preview is an accent gradient.
5
+ * Slots: type, name, meta, more. Classes: .oc-project-card (--flat), __frame, __bar, __shot, __body, __type, __name, __meta, __more
6
+ */
7
+ withDefaults(
8
+ defineProps<{
9
+ href: string
10
+ name?: string
11
+ type?: string
12
+ meta?: string
13
+ more?: string
14
+ image?: string
15
+ imageAlt?: string
16
+ flat?: boolean
17
+ }>(),
18
+ { more: 'View project', imageAlt: '' },
19
+ )
20
+ </script>
21
+
22
+ <template>
23
+ <a :class="['oc-project-card', flat && 'oc-project-card--flat']" :href="href">
24
+ <span class="oc-project-card__frame">
25
+ <span class="oc-project-card__bar" aria-hidden="true"><i></i><i></i><i></i></span>
26
+ <img v-if="image" class="oc-project-card__shot" :src="image" :alt="imageAlt" loading="lazy" />
27
+ <span v-else class="oc-project-card__shot" role="img" :aria-label="imageAlt || undefined" :aria-hidden="imageAlt ? undefined : 'true'"></span>
28
+ </span>
29
+ <span class="oc-project-card__body">
30
+ <span v-if="type || $slots.type" class="oc-project-card__type"><slot name="type">{{ type }}</slot></span>
31
+ <span class="oc-project-card__name"><slot name="name">{{ name }}</slot></span>
32
+ <span v-if="meta || $slots.meta" class="oc-project-card__meta"><slot name="meta">{{ meta }}</slot></span>
33
+ <span v-if="more" class="oc-project-card__more"><slot name="more">{{ more }}</slot></span>
34
+ </span>
35
+ </a>
36
+ </template>
@@ -1,57 +1,57 @@
1
- <!-- File: packages/vue/src/components/OcProse.vue | Map: props -> wrapper with size modifiers -> default slot (demo article when empty) -->
2
- <script setup lang="ts">
3
- /**
4
- * Typography for long-form text written without classes (same markup as snippets/prose.html). Slot default = the text
5
- * (put .oc-prose-lead on the intro paragraph); the snippet's demo article when empty. size: sm | md | lg, full = no max width.
6
- * Classes: .oc-prose(--sm/--lg/--full), .oc-prose-lead
7
- */
8
- withDefaults(defineProps<{ size?: 'sm' | 'md' | 'lg'; full?: boolean; as?: 'article' | 'div' | 'section' }>(), { size: 'md', as: 'article' })
9
- </script>
10
-
11
- <template>
12
- <component :is="as" :class="['oc-prose', size !== 'md' && `oc-prose--${size}`, full && 'oc-prose--full']">
13
- <slot>
14
- <h1>Planning a kitchen renovation</h1>
15
- <p class="oc-prose-lead">A new kitchen takes eight to twelve weeks from the first visit to the last cabinet handle. This guide explains every step, so you know what to expect and when we need you.</p>
16
- <h2>Before the first visit</h2>
17
- <p>Collect a few photos of kitchens you like and measure the room roughly. Exact measurements are not needed yet: our site manager does that during the visit. Read the <a href="#checklist">preparation checklist</a> if you want to be fully ready.</p>
18
- <ul>
19
- <li>The <strong>budget range</strong> you have in mind, including appliances</li>
20
- <li>Which walls, doors and windows can move, and which cannot
21
- <ul><li>Load-bearing walls need a structural engineer</li><li>Moving a window needs a permit in most towns</li></ul>
22
- </li>
23
- <li>Appliances you want to keep</li>
24
- </ul>
25
- <figure>
26
- <div class="oc-media-art oc-media-art--6" role="img" aria-label="Kitchen with oak fronts and a light stone worktop"></div>
27
- <figcaption>Villa Dewitte: oak fronts with a Calacatta matt worktop, finished in October 2026.</figcaption>
28
- </figure>
29
- <h3>From design to installation</h3>
30
- <ol>
31
- <li>Site visit and measurements (week 1)</li>
32
- <li>Design and quote, with two revision rounds (weeks 2–3)</li>
33
- <li>Order and production of the cabinets (weeks 4–9)</li>
34
- <li>Installation, worktop and finishing (weeks 10–11)</li>
35
- </ol>
36
- <blockquote>
37
- <p>The planning was clear from day one. We always knew who was coming and when, and the kitchen was ready a week early.</p>
38
- <footer>— Ann Dewitte, client in Ghent</footer>
39
- </blockquote>
40
- <h2>Worktop materials compared</h2>
41
- <table>
42
- <caption>Prices per running metre, 60 cm deep, installed.</caption>
43
- <thead><tr><th>Material</th><th>Thickness</th><th>Heat resistant</th><th>Price</th></tr></thead>
44
- <tbody>
45
- <tr><td>Compact laminate</td><td>12 mm</td><td>Up to 180 °C</td><td>€ 290</td></tr>
46
- <tr><td>Quartz composite</td><td>20 mm</td><td>Up to 150 °C</td><td>€ 540</td></tr>
47
- <tr><td>Natural stone</td><td>20 mm</td><td>Yes</td><td>€ 720</td></tr>
48
- </tbody>
49
- </table>
50
- <h3>Sharing your floor plan</h3>
51
- <p>Upload a PDF or a photo in the client portal, or send it by e-mail. Press <kbd>Ctrl</kbd> + <kbd>V</kbd> in the chat to paste a screenshot. Files are stored as <code>project-id/plans/</code> and only your project team can see them.</p>
52
- <pre><code>{{ 'Project: 2026-114 Villa Dewitte\nRoom: Kitchen, ground floor\nWall A: 412 cm (window at 160 cm)\nWall B: 298 cm (door at the left)' }}</code></pre>
53
- <hr />
54
- <p><small>Lead times are an estimate. <mark>Natural stone</mark> can take two weeks longer in summer.</small></p>
55
- </slot>
56
- </component>
57
- </template>
1
+ <!-- File: packages/vue/src/components/OcProse.vue | Map: props -> wrapper with size modifiers -> default slot (demo article when empty) -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Typography for long-form text written without classes (same markup as snippets/prose.html). Slot default = the text
5
+ * (put .oc-prose-lead on the intro paragraph); the snippet's demo article when empty. size: sm | md | lg, full = no max width.
6
+ * Classes: .oc-prose(--sm/--lg/--full), .oc-prose-lead
7
+ */
8
+ withDefaults(defineProps<{ size?: 'sm' | 'md' | 'lg'; full?: boolean; as?: 'article' | 'div' | 'section' }>(), { size: 'md', as: 'article' })
9
+ </script>
10
+
11
+ <template>
12
+ <component :is="as" :class="['oc-prose', size !== 'md' && `oc-prose--${size}`, full && 'oc-prose--full']">
13
+ <slot>
14
+ <h1>Planning a kitchen renovation</h1>
15
+ <p class="oc-prose-lead">A new kitchen takes eight to twelve weeks from the first visit to the last cabinet handle. This guide explains every step, so you know what to expect and when we need you.</p>
16
+ <h2>Before the first visit</h2>
17
+ <p>Collect a few photos of kitchens you like and measure the room roughly. Exact measurements are not needed yet: our site manager does that during the visit. Read the <a href="#checklist">preparation checklist</a> if you want to be fully ready.</p>
18
+ <ul>
19
+ <li>The <strong>budget range</strong> you have in mind, including appliances</li>
20
+ <li>Which walls, doors and windows can move, and which cannot
21
+ <ul><li>Load-bearing walls need a structural engineer</li><li>Moving a window needs a permit in most towns</li></ul>
22
+ </li>
23
+ <li>Appliances you want to keep</li>
24
+ </ul>
25
+ <figure>
26
+ <div class="oc-media-art oc-media-art--6" role="img" aria-label="Kitchen with oak fronts and a light stone worktop"></div>
27
+ <figcaption>Villa Dewitte: oak fronts with a Calacatta matt worktop, finished in October 2026.</figcaption>
28
+ </figure>
29
+ <h3>From design to installation</h3>
30
+ <ol>
31
+ <li>Site visit and measurements (week 1)</li>
32
+ <li>Design and quote, with two revision rounds (weeks 2–3)</li>
33
+ <li>Order and production of the cabinets (weeks 4–9)</li>
34
+ <li>Installation, worktop and finishing (weeks 10–11)</li>
35
+ </ol>
36
+ <blockquote>
37
+ <p>The planning was clear from day one. We always knew who was coming and when, and the kitchen was ready a week early.</p>
38
+ <footer>— Ann Dewitte, client in Ghent</footer>
39
+ </blockquote>
40
+ <h2>Worktop materials compared</h2>
41
+ <table>
42
+ <caption>Prices per running metre, 60 cm deep, installed.</caption>
43
+ <thead><tr><th>Material</th><th>Thickness</th><th>Heat resistant</th><th>Price</th></tr></thead>
44
+ <tbody>
45
+ <tr><td>Compact laminate</td><td>12 mm</td><td>Up to 180 °C</td><td>€ 290</td></tr>
46
+ <tr><td>Quartz composite</td><td>20 mm</td><td>Up to 150 °C</td><td>€ 540</td></tr>
47
+ <tr><td>Natural stone</td><td>20 mm</td><td>Yes</td><td>€ 720</td></tr>
48
+ </tbody>
49
+ </table>
50
+ <h3>Sharing your floor plan</h3>
51
+ <p>Upload a PDF or a photo in the client portal, or send it by e-mail. Press <kbd>Ctrl</kbd> + <kbd>V</kbd> in the chat to paste a screenshot. Files are stored as <code>project-id/plans/</code> and only your project team can see them.</p>
52
+ <pre><code>{{ 'Project: 2026-114 Villa Dewitte\nRoom: Kitchen, ground floor\nWall A: 412 cm (window at 160 cm)\nWall B: 298 cm (door at the left)' }}</code></pre>
53
+ <hr />
54
+ <p><small>Lead times are an estimate. <mark>Natural stone</mark> can take two weeks longer in summer.</small></p>
55
+ </slot>
56
+ </component>
57
+ </template>
@@ -1,40 +1,40 @@
1
- <script setup lang="ts">
2
- /**
3
- * One radio. Inside OcRadioGroup it reads and writes the group's v-model; standalone it has its own v-model
4
- * and needs a `name` attribute. `description` (or `card`, or the group's `card`) renders the bordered card form.
5
- * Classes: .oc-radio, .oc-radio-item(--card)
6
- */
7
- import { computed, inject } from 'vue'
8
- import { useAttrsSplit } from '../composables/useAttrsSplit'
9
- import { radioGroupKey } from '../composables/keys'
10
-
11
- defineOptions({ inheritAttrs: false })
12
- const props = defineProps<{ value: string | number; description?: string; card?: boolean; disabled?: boolean }>()
13
- const own = defineModel<string | number>()
14
- const group = inject(radioGroupKey, null)
15
- const { root, control } = useAttrsSplit()
16
-
17
- const checked = computed(() => (group ? group.model.value : own.value) === props.value)
18
- const isCard = computed(() => props.card || !!group?.card.value || !!props.description)
19
- function onChange() {
20
- if (group) group.model.value = props.value
21
- else own.value = props.value
22
- }
23
- </script>
24
-
25
- <template>
26
- <label v-bind="root" :class="['oc-radio-item', isCard && 'oc-radio-item--card']">
27
- <input
28
- v-bind="control"
29
- class="oc-radio"
30
- type="radio"
31
- :name="group ? group.name.value : (control.name as string | undefined)"
32
- :value="value"
33
- :checked="checked"
34
- :disabled="disabled"
35
- @change="onChange"
36
- />
37
- <span v-if="description"><slot /><small>{{ description }}</small></span>
38
- <slot v-else />
39
- </label>
40
- </template>
1
+ <script setup lang="ts">
2
+ /**
3
+ * One radio. Inside OcRadioGroup it reads and writes the group's v-model; standalone it has its own v-model
4
+ * and needs a `name` attribute. `description` (or `card`, or the group's `card`) renders the bordered card form.
5
+ * Classes: .oc-radio, .oc-radio-item(--card)
6
+ */
7
+ import { computed, inject } from 'vue'
8
+ import { useAttrsSplit } from '../composables/useAttrsSplit'
9
+ import { radioGroupKey } from '../composables/keys'
10
+
11
+ defineOptions({ inheritAttrs: false })
12
+ const props = defineProps<{ value: string | number; description?: string; card?: boolean; disabled?: boolean }>()
13
+ const own = defineModel<string | number>()
14
+ const group = inject(radioGroupKey, null)
15
+ const { root, control } = useAttrsSplit()
16
+
17
+ const checked = computed(() => (group ? group.model.value : own.value) === props.value)
18
+ const isCard = computed(() => props.card || !!group?.card.value || !!props.description)
19
+ function onChange() {
20
+ if (group) group.model.value = props.value
21
+ else own.value = props.value
22
+ }
23
+ </script>
24
+
25
+ <template>
26
+ <label v-bind="root" :class="['oc-radio-item', isCard && 'oc-radio-item--card']">
27
+ <input
28
+ v-bind="control"
29
+ class="oc-radio"
30
+ type="radio"
31
+ :name="group ? group.name.value : (control.name as string | undefined)"
32
+ :value="value"
33
+ :checked="checked"
34
+ :disabled="disabled"
35
+ @change="onChange"
36
+ />
37
+ <span v-if="description"><slot /><small>{{ description }}</small></span>
38
+ <slot v-else />
39
+ </label>
40
+ </template>