@birdycom/omniaui-vue 0.12.6 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/manifest.json CHANGED
@@ -114,6 +114,12 @@
114
114
  "file": "src/components/OcBarcodeInput.vue",
115
115
  "snippet": "@birdycom/omniaui/snippets/barcode-input.html"
116
116
  },
117
+ {
118
+ "name": "OcBrandMark",
119
+ "kind": "component",
120
+ "file": "src/components/OcBrandMark.vue",
121
+ "snippet": "@birdycom/omniaui/snippets/brand-mark.html"
122
+ },
117
123
  {
118
124
  "name": "OcBreadcrumb",
119
125
  "kind": "component",
@@ -324,6 +330,12 @@
324
330
  "file": "src/components/OcFactList.vue",
325
331
  "snippet": "@birdycom/omniaui/snippets/fact-list.html"
326
332
  },
333
+ {
334
+ "name": "OcFeatureCard",
335
+ "kind": "component",
336
+ "file": "src/components/OcFeatureCard.vue",
337
+ "snippet": "@birdycom/omniaui/snippets/feature-card.html"
338
+ },
327
339
  {
328
340
  "name": "OcFilterCounts",
329
341
  "kind": "component",
@@ -474,6 +486,12 @@
474
486
  "file": "src/components/OcLinkArrow.vue",
475
487
  "snippet": "@birdycom/omniaui/snippets/link-arrow.html"
476
488
  },
489
+ {
490
+ "name": "OcLinkCard",
491
+ "kind": "component",
492
+ "file": "src/components/OcLinkCard.vue",
493
+ "snippet": null
494
+ },
477
495
  {
478
496
  "name": "OcLinkRow",
479
497
  "kind": "component",
@@ -594,6 +612,12 @@
594
612
  "file": "src/components/OcPagination.vue",
595
613
  "snippet": "@birdycom/omniaui/snippets/pagination.html"
596
614
  },
615
+ {
616
+ "name": "OcPanel",
617
+ "kind": "component",
618
+ "file": "src/components/OcPanel.vue",
619
+ "snippet": "@birdycom/omniaui/snippets/panel.html"
620
+ },
597
621
  {
598
622
  "name": "OcPasswordInput",
599
623
  "kind": "component",
@@ -690,6 +714,12 @@
690
714
  "file": "src/components/OcRecoveryCodes.vue",
691
715
  "snippet": "@birdycom/omniaui/snippets/recovery-codes.html"
692
716
  },
717
+ {
718
+ "name": "OcResult",
719
+ "kind": "component",
720
+ "file": "src/components/OcResult.vue",
721
+ "snippet": "@birdycom/omniaui/snippets/result.html"
722
+ },
693
723
  {
694
724
  "name": "OcRibbon",
695
725
  "kind": "component",
@@ -1032,6 +1062,12 @@
1032
1062
  "file": "src/blocks/OcBlockAuthSplit.vue",
1033
1063
  "snippet": "@birdycom/omniaui-blocks/blocks/auth-split.html"
1034
1064
  },
1065
+ {
1066
+ "name": "OcBlockContactForm",
1067
+ "kind": "block",
1068
+ "file": "src/blocks/OcBlockContactForm.vue",
1069
+ "snippet": "@birdycom/omniaui-blocks/blocks/contact-form.html"
1070
+ },
1035
1071
  {
1036
1072
  "name": "OcBlockContactGrid",
1037
1073
  "kind": "block",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@birdycom/omniaui-vue",
3
- "version": "0.12.6",
3
+ "version": "0.13.0",
4
4
  "description": "OmniaUI for Vue 3: one single-file component per OmniaUI component and block, rendering the exact markup of the CSS packages.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,146 @@
1
+ <!-- File: packages/vue/src/blocks/OcBlockContactForm.vue | Map: props -> panel (result | title, intro, alert, form: honeypot, fields, actions) -> cards -->
2
+ <script setup lang="ts">
3
+ /**
4
+ * Contact form in a panel next to a column of contact cards. Server errors come in as `errors` (field name -> text,
5
+ * `form` = message above the form); `sent` shows the result, `busy` sets aria-busy and disables the button.
6
+ * Slots: fields (own fields instead of name, e-mail, phone, subject, message), note, success, card-icon ({ card }).
7
+ * Emits submit (the native event; call preventDefault for Inertia) and again (back to the form).
8
+ * Classes: .oc-block-contact-form, -layout, -title, -intro, -cards; .oc-panel--glow, .oc-form--filled, .oc-result
9
+ */
10
+ import { computed } from 'vue'
11
+ import { useUid } from '../composables/useUid'
12
+
13
+ export interface ContactFormLine {
14
+ text: string
15
+ href?: string
16
+ }
17
+ export interface ContactFormCard {
18
+ title: string
19
+ lines: ContactFormLine[]
20
+ /** Webicon name (mdi:email-outline …), drawn with <iconify-icon>. */
21
+ icon?: string
22
+ }
23
+ export interface ContactFormSubject {
24
+ value: string
25
+ label: string
26
+ }
27
+ const props = withDefaults(
28
+ defineProps<{
29
+ title?: string
30
+ intro?: string
31
+ action?: string
32
+ method?: string
33
+ subjects?: ContactFormSubject[]
34
+ values?: Record<string, string>
35
+ errors?: Record<string, string>
36
+ sent?: boolean
37
+ busy?: boolean
38
+ submitLabel?: string
39
+ busyLabel?: string
40
+ note?: string
41
+ successTitle?: string
42
+ successText?: string
43
+ againLabel?: string
44
+ errorText?: string
45
+ cards?: ContactFormCard[]
46
+ }>(),
47
+ {
48
+ title: 'Send us a message',
49
+ method: 'post',
50
+ subjects: () => [],
51
+ values: () => ({}),
52
+ errors: () => ({}),
53
+ submitLabel: 'Send message',
54
+ busyLabel: 'Sending…',
55
+ successTitle: 'Thank you, your message was sent',
56
+ againLabel: 'Send another message',
57
+ errorText: 'Please check the fields in red.',
58
+ cards: () => [],
59
+ },
60
+ )
61
+ const emit = defineEmits<{ submit: [event: Event]; again: [] }>()
62
+ const uid = useUid('oc-contact')
63
+ const fid = (name: string) => `${uid}-${name}`
64
+ const invalid = (name: string) => (props.errors[name] ? { 'aria-invalid': true as const, 'aria-describedby': `err-${name}` } : {})
65
+ const alertText = computed(() => props.errors.form ?? (Object.keys(props.errors).some((k) => k !== 'form') ? props.errorText : null))
66
+ </script>
67
+
68
+ <template>
69
+ <section class="oc-block-contact-form" aria-label="Contact">
70
+ <div class="oc-block-contact-form-layout">
71
+ <div :id="uid" class="oc-panel oc-panel--glow">
72
+ <div v-if="sent" class="oc-result" role="status" tabindex="-1">
73
+ <slot name="success">
74
+ <span class="oc-icon-tile oc-icon-tile--sm oc-accent--green" aria-hidden="true"><iconify-icon icon="mdi:check" /></span>
75
+ <h2 class="oc-result__title">{{ successTitle }}</h2>
76
+ <p v-if="successText" class="oc-result__text">{{ successText }}</p>
77
+ <div class="oc-result__actions">
78
+ <button type="button" class="oc-btn oc-btn--outline" @click="emit('again')">{{ againLabel }}</button>
79
+ </div>
80
+ </slot>
81
+ </div>
82
+ <div v-else>
83
+ <h2 v-if="title" class="oc-block-contact-form-title">{{ title }}</h2>
84
+ <p v-if="intro" class="oc-block-contact-form-intro">{{ intro }}</p>
85
+ <p v-if="alertText" class="oc-alert oc-alert--danger" role="alert">{{ alertText }}</p>
86
+ <form class="oc-form oc-form--filled" :action="action" :method="method" novalidate :aria-busy="busy || undefined" @submit="emit('submit', $event)">
87
+ <div class="oc-honeypot" aria-hidden="true">
88
+ <label :for="fid('website')">Leave this field empty</label>
89
+ <input :id="fid('website')" type="text" name="website" tabindex="-1" autocomplete="off" />
90
+ </div>
91
+ <slot name="fields">
92
+ <div class="oc-field-group oc-field-group--row">
93
+ <div class="oc-field">
94
+ <label class="oc-field-label" :for="fid('name')">Name</label>
95
+ <input :id="fid('name')" class="oc-input" type="text" name="name" required maxlength="100" autocomplete="name" :value="values.name" v-bind="invalid('name')" />
96
+ <p v-if="errors.name" id="err-name" class="oc-field-error">{{ errors.name }}</p>
97
+ </div>
98
+ <div class="oc-field">
99
+ <label class="oc-field-label" :for="fid('email')">E-mail</label>
100
+ <input :id="fid('email')" class="oc-input" type="email" name="email" required maxlength="254" autocomplete="email" :value="values.email" v-bind="invalid('email')" />
101
+ <p v-if="errors.email" id="err-email" class="oc-field-error">{{ errors.email }}</p>
102
+ </div>
103
+ </div>
104
+ <div :class="['oc-field-group', subjects.length && 'oc-field-group--row']">
105
+ <div class="oc-field">
106
+ <label class="oc-field-label" :for="fid('phone')">Phone <span class="oc-field-label__hint">(optional)</span></label>
107
+ <input :id="fid('phone')" class="oc-input" type="tel" name="phone" maxlength="40" autocomplete="tel" inputmode="tel" :value="values.phone" v-bind="invalid('phone')" />
108
+ <p v-if="errors.phone" id="err-phone" class="oc-field-error">{{ errors.phone }}</p>
109
+ </div>
110
+ <div v-if="subjects.length" class="oc-field">
111
+ <label class="oc-field-label" :for="fid('subject')">What is it about?</label>
112
+ <select :id="fid('subject')" class="oc-select" name="subject" required v-bind="invalid('subject')">
113
+ <option value="" :selected="!values.subject">Choose a subject</option>
114
+ <option v-for="s in subjects" :key="s.value" :value="s.value" :selected="values.subject === s.value">{{ s.label }}</option>
115
+ </select>
116
+ <p v-if="errors.subject" id="err-subject" class="oc-field-error">{{ errors.subject }}</p>
117
+ </div>
118
+ </div>
119
+ <div class="oc-field">
120
+ <label class="oc-field-label" :for="fid('message')">Your message</label>
121
+ <textarea :id="fid('message')" class="oc-input oc-textarea" name="message" rows="6" required maxlength="5000" :value="values.message" v-bind="invalid('message')" />
122
+ <p v-if="errors.message" id="err-message" class="oc-field-error">{{ errors.message }}</p>
123
+ </div>
124
+ </slot>
125
+ <div class="oc-form-actions">
126
+ <button class="oc-btn oc-btn--lg" type="submit" :disabled="busy">{{ busy ? busyLabel : submitLabel }}</button>
127
+ <p v-if="note || $slots.note" class="oc-form-note"><slot name="note">{{ note }}</slot></p>
128
+ </div>
129
+ </form>
130
+ </div>
131
+ </div>
132
+ <div v-if="cards.length" class="oc-block-contact-form-cards">
133
+ <article v-for="(card, i) in cards" :key="i" class="oc-block-contact-card">
134
+ <span v-if="card.icon || $slots['card-icon']" class="oc-icon-tile oc-icon-tile--sm" aria-hidden="true">
135
+ <slot name="card-icon" :card="card"><iconify-icon :icon="card.icon" /></slot>
136
+ </span>
137
+ <h3 class="oc-block-contact-title">{{ card.title }}</h3>
138
+ <template v-for="(line, j) in card.lines" :key="j">
139
+ <a v-if="line.href" class="oc-block-contact-line" :href="line.href">{{ line.text }}</a>
140
+ <p v-else class="oc-block-contact-line">{{ line.text }}</p>
141
+ </template>
142
+ </article>
143
+ </div>
144
+ </div>
145
+ </section>
146
+ </template>
@@ -0,0 +1,15 @@
1
+ <!-- File: packages/vue/src/components/OcBrandMark.vue | Map: link -> icon (slot) -> word (name + accent) -> tagline -->
2
+ <script setup lang="ts">
3
+ /** Logo from an icon and text (snippets/brand-mark.html). Slot icon: an <svg class="oc-brand-mark__icon"> or <img>. */
4
+ withDefaults(defineProps<{ name: string; accent?: string; tagline?: string; href?: string; size?: 'sm' | 'md' | 'lg' }>(), { href: '/', size: 'md' })
5
+ </script>
6
+
7
+ <template>
8
+ <a :class="['oc-brand-mark', size !== 'md' && `oc-brand-mark--${size}`]" :href="href">
9
+ <slot name="icon" />
10
+ <span class="oc-brand-mark__text">
11
+ <span class="oc-brand-mark__word">{{ name }}<span v-if="accent" class="oc-brand-mark__accent">{{ accent }}</span></span>
12
+ <span v-if="tagline" class="oc-brand-mark__tagline">{{ tagline }}</span>
13
+ </span>
14
+ </a>
15
+ </template>
@@ -0,0 +1,15 @@
1
+ <!-- File: packages/vue/src/components/OcFeatureCard.vue | Map: tile (icon) -> title -> text (slot) -->
2
+ <script setup lang="ts">
3
+ /** Service or feature card (snippets/feature-card.html). icon = webicon name; slots: default (text), icon. */
4
+ withDefaults(defineProps<{ title: string; icon?: string; accent?: string; headingLevel?: 2 | 3 | 4 }>(), { headingLevel: 3 })
5
+ </script>
6
+
7
+ <template>
8
+ <article class="oc-feature-card">
9
+ <span v-if="icon || $slots.icon" :class="['oc-icon-tile oc-icon-tile--sm', accent && `oc-accent--${accent}`]" aria-hidden="true">
10
+ <slot name="icon"><iconify-icon :icon="icon" /></slot>
11
+ </span>
12
+ <component :is="`h${headingLevel}`" class="oc-feature-card__title">{{ title }}</component>
13
+ <p v-if="$slots.default" class="oc-feature-card__text"><slot /></p>
14
+ </article>
15
+ </template>
@@ -0,0 +1,16 @@
1
+ <!-- File: packages/vue/src/components/OcLinkCard.vue | Map: link -> tile (icon) -> title -> text (slot) -> more -->
2
+ <script setup lang="ts">
3
+ /** A card that is a whole link (snippets/feature-card.html). icon = webicon name; slots: default (text), icon. */
4
+ defineProps<{ title: string; href: string; icon?: string; accent?: string; more?: string }>()
5
+ </script>
6
+
7
+ <template>
8
+ <a :class="['oc-link-card', accent && `oc-accent--${accent}`]" :href="href">
9
+ <span v-if="icon || $slots.icon" class="oc-icon-tile oc-icon-tile--sm" aria-hidden="true">
10
+ <slot name="icon"><iconify-icon :icon="icon" /></slot>
11
+ </span>
12
+ <span class="oc-link-card__title">{{ title }}</span>
13
+ <span v-if="$slots.default" class="oc-link-card__text"><slot /></span>
14
+ <span v-if="more" class="oc-link-card__more">{{ more }}</span>
15
+ </a>
16
+ </template>
@@ -0,0 +1,9 @@
1
+ <!-- File: packages/vue/src/components/OcPanel.vue | Map: glow -> classes -> panel with the slot -->
2
+ <script setup lang="ts">
3
+ /** Framed panel for a form, a call to action or a feature (same markup as snippets/panel.html). glow: start | end | both. */
4
+ defineProps<{ glow?: 'start' | 'end' | 'both' }>()
5
+ </script>
6
+
7
+ <template>
8
+ <div :class="['oc-panel', (glow === 'start' || glow === 'end') && 'oc-panel--glow', glow === 'end' && 'oc-panel--glow-end', glow === 'both' && 'oc-panel--glow-both']"><slot /></div>
9
+ </template>
@@ -0,0 +1,16 @@
1
+ <!-- File: packages/vue/src/components/OcResult.vue | Map: tile (icon) -> title -> text (slot) -> actions (slot) -->
2
+ <script setup lang="ts">
3
+ /** Outcome of an action in place of the form (snippets/result.html). icon = webicon name; slots: default (text), icon, actions. */
4
+ defineProps<{ title: string; icon?: string; accent?: string; center?: boolean }>()
5
+ </script>
6
+
7
+ <template>
8
+ <div :class="['oc-result', center && 'oc-result--center']" role="status" tabindex="-1">
9
+ <span v-if="icon || $slots.icon" :class="['oc-icon-tile oc-icon-tile--sm', accent && `oc-accent--${accent}`]" aria-hidden="true">
10
+ <slot name="icon"><iconify-icon :icon="icon" /></slot>
11
+ </span>
12
+ <h2 class="oc-result__title">{{ title }}</h2>
13
+ <p v-if="$slots.default" class="oc-result__text"><slot /></p>
14
+ <div v-if="$slots.actions" class="oc-result__actions"><slot name="actions" /></div>
15
+ </div>
16
+ </template>
package/src/index.ts CHANGED
@@ -20,6 +20,7 @@ import OcBackLink from './components/OcBackLink.vue'
20
20
  import OcBadge from './components/OcBadge.vue'
21
21
  import OcBarcode from './components/OcBarcode.vue'
22
22
  import OcBarcodeInput from './components/OcBarcodeInput.vue'
23
+ import OcBrandMark from './components/OcBrandMark.vue'
23
24
  import OcBreadcrumb from './components/OcBreadcrumb.vue'
24
25
  import OcBrowserFrame from './components/OcBrowserFrame.vue'
25
26
  import OcBusyOverlay from './components/OcBusyOverlay.vue'
@@ -55,6 +56,7 @@ import OcEmojiPicker from './components/OcEmojiPicker.vue'
55
56
  import OcEmptyState from './components/OcEmptyState.vue'
56
57
  import OcEntityCard from './components/OcEntityCard.vue'
57
58
  import OcFactList from './components/OcFactList.vue'
59
+ import OcFeatureCard from './components/OcFeatureCard.vue'
58
60
  import OcFilterCounts from './components/OcFilterCounts.vue'
59
61
  import OcFlipCard from './components/OcFlipCard.vue'
60
62
  import OcFormField from './components/OcFormField.vue'
@@ -80,6 +82,7 @@ import OcLauncherCard from './components/OcLauncherCard.vue'
80
82
  import OcLazyImage from './components/OcLazyImage.vue'
81
83
  import OcLightbox from './components/OcLightbox.vue'
82
84
  import OcLinkArrow from './components/OcLinkArrow.vue'
85
+ import OcLinkCard from './components/OcLinkCard.vue'
83
86
  import OcLinkRow from './components/OcLinkRow.vue'
84
87
  import OcListToolbar from './components/OcListToolbar.vue'
85
88
  import OcLiveFrame from './components/OcLiveFrame.vue'
@@ -100,6 +103,7 @@ import OcNumberInput from './components/OcNumberInput.vue'
100
103
  import OcOtpInput from './components/OcOtpInput.vue'
101
104
  import OcPageHeader from './components/OcPageHeader.vue'
102
105
  import OcPagination from './components/OcPagination.vue'
106
+ import OcPanel from './components/OcPanel.vue'
103
107
  import OcPasswordInput from './components/OcPasswordInput.vue'
104
108
  import OcPasswordRequirements from './components/OcPasswordRequirements.vue'
105
109
  import OcPhoneInput from './components/OcPhoneInput.vue'
@@ -116,6 +120,7 @@ import OcRadioGroup from './components/OcRadioGroup.vue'
116
120
  import OcRating from './components/OcRating.vue'
117
121
  import OcRatingInput from './components/OcRatingInput.vue'
118
122
  import OcRecoveryCodes from './components/OcRecoveryCodes.vue'
123
+ import OcResult from './components/OcResult.vue'
119
124
  import OcRibbon from './components/OcRibbon.vue'
120
125
  import OcScrollArea from './components/OcScrollArea.vue'
121
126
  import OcSegmented from './components/OcSegmented.vue'
@@ -173,6 +178,7 @@ import OcBlockAppHeader from './blocks/OcBlockAppHeader.vue'
173
178
  import OcBlockAppShell from './blocks/OcBlockAppShell.vue'
174
179
  import OcBlockAuth from './blocks/OcBlockAuth.vue'
175
180
  import OcBlockAuthSplit from './blocks/OcBlockAuthSplit.vue'
181
+ import OcBlockContactForm from './blocks/OcBlockContactForm.vue'
176
182
  import OcBlockContactGrid from './blocks/OcBlockContactGrid.vue'
177
183
  import OcBlockCta from './blocks/OcBlockCta.vue'
178
184
  import OcBlockDataTable from './blocks/OcBlockDataTable.vue'
@@ -221,6 +227,7 @@ export {
221
227
  OcBadge,
222
228
  OcBarcode,
223
229
  OcBarcodeInput,
230
+ OcBrandMark,
224
231
  OcBreadcrumb,
225
232
  OcBrowserFrame,
226
233
  OcBusyOverlay,
@@ -256,6 +263,7 @@ export {
256
263
  OcEmptyState,
257
264
  OcEntityCard,
258
265
  OcFactList,
266
+ OcFeatureCard,
259
267
  OcFilterCounts,
260
268
  OcFlipCard,
261
269
  OcFormField,
@@ -281,6 +289,7 @@ export {
281
289
  OcLazyImage,
282
290
  OcLightbox,
283
291
  OcLinkArrow,
292
+ OcLinkCard,
284
293
  OcLinkRow,
285
294
  OcListToolbar,
286
295
  OcLiveFrame,
@@ -301,6 +310,7 @@ export {
301
310
  OcOtpInput,
302
311
  OcPageHeader,
303
312
  OcPagination,
313
+ OcPanel,
304
314
  OcPasswordInput,
305
315
  OcPasswordRequirements,
306
316
  OcPhoneInput,
@@ -317,6 +327,7 @@ export {
317
327
  OcRating,
318
328
  OcRatingInput,
319
329
  OcRecoveryCodes,
330
+ OcResult,
320
331
  OcRibbon,
321
332
  OcScrollArea,
322
333
  OcSegmented,
@@ -374,6 +385,7 @@ export {
374
385
  OcBlockAppShell,
375
386
  OcBlockAuth,
376
387
  OcBlockAuthSplit,
388
+ OcBlockContactForm,
377
389
  OcBlockContactGrid,
378
390
  OcBlockCta,
379
391
  OcBlockDataTable,
@@ -438,6 +450,7 @@ export type { TableCompactColumn } from './components/OcTableCompact.vue'
438
450
  export type { TabItem } from './components/OcTabs.vue'
439
451
  export type { TimelineEntry } from './components/OcTimeline.vue'
440
452
  export type { OcTreeItem } from './components/OcTree.vue'
453
+ export type { ContactFormLine, ContactFormCard, ContactFormSubject } from './blocks/OcBlockContactForm.vue'
441
454
  export type { ContactGridLine, ContactGridCard } from './blocks/OcBlockContactGrid.vue'
442
455
  export type { FaqItem } from './blocks/OcBlockFaq.vue'
443
456
  export type { FeatureItem } from './blocks/OcBlockFeatures.vue'
@@ -481,6 +494,7 @@ export const components: Record<string, Component> = {
481
494
  OcBadge,
482
495
  OcBarcode,
483
496
  OcBarcodeInput,
497
+ OcBrandMark,
484
498
  OcBreadcrumb,
485
499
  OcBrowserFrame,
486
500
  OcBusyOverlay,
@@ -516,6 +530,7 @@ export const components: Record<string, Component> = {
516
530
  OcEmptyState,
517
531
  OcEntityCard,
518
532
  OcFactList,
533
+ OcFeatureCard,
519
534
  OcFilterCounts,
520
535
  OcFlipCard,
521
536
  OcFormField,
@@ -541,6 +556,7 @@ export const components: Record<string, Component> = {
541
556
  OcLazyImage,
542
557
  OcLightbox,
543
558
  OcLinkArrow,
559
+ OcLinkCard,
544
560
  OcLinkRow,
545
561
  OcListToolbar,
546
562
  OcLiveFrame,
@@ -561,6 +577,7 @@ export const components: Record<string, Component> = {
561
577
  OcOtpInput,
562
578
  OcPageHeader,
563
579
  OcPagination,
580
+ OcPanel,
564
581
  OcPasswordInput,
565
582
  OcPasswordRequirements,
566
583
  OcPhoneInput,
@@ -577,6 +594,7 @@ export const components: Record<string, Component> = {
577
594
  OcRating,
578
595
  OcRatingInput,
579
596
  OcRecoveryCodes,
597
+ OcResult,
580
598
  OcRibbon,
581
599
  OcScrollArea,
582
600
  OcSegmented,
@@ -634,6 +652,7 @@ export const components: Record<string, Component> = {
634
652
  OcBlockAppShell,
635
653
  OcBlockAuth,
636
654
  OcBlockAuthSplit,
655
+ OcBlockContactForm,
637
656
  OcBlockContactGrid,
638
657
  OcBlockCta,
639
658
  OcBlockDataTable,