@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.
- package/LICENSE +21 -21
- package/README.md +49 -49
- package/dist/omniaui-vue.js.map +1 -1
- package/package.json +69 -69
- package/src/blocks/OcBlockAboutTimeline.vue +44 -44
- package/src/blocks/OcBlockAppHeader.vue +36 -36
- package/src/blocks/OcBlockAppShell.vue +29 -29
- package/src/blocks/OcBlockAuth.vue +24 -24
- package/src/blocks/OcBlockAuthSplit.vue +47 -47
- package/src/blocks/OcBlockContactGrid.vue +53 -53
- package/src/blocks/OcBlockCta.vue +12 -12
- package/src/blocks/OcBlockDataTable.vue +27 -27
- package/src/blocks/OcBlockFaq.vue +49 -49
- package/src/blocks/OcBlockFeatures.vue +23 -23
- package/src/blocks/OcBlockFooter.vue +25 -25
- package/src/blocks/OcBlockFormPage.vue +24 -24
- package/src/blocks/OcBlockHero.vue +13 -13
- package/src/blocks/OcBlockHeroCode.vue +42 -42
- package/src/blocks/OcBlockHeroOrbit.vue +69 -69
- package/src/blocks/OcBlockHeroSplit.vue +15 -15
- package/src/blocks/OcBlockModuleHeader.vue +21 -21
- package/src/blocks/OcBlockPageHero.vue +13 -13
- package/src/blocks/OcBlockPortfolioGrid.vue +63 -63
- package/src/blocks/OcBlockPricing.vue +39 -39
- package/src/blocks/OcBlockProcess.vue +39 -39
- package/src/blocks/OcBlockProductGrid.vue +41 -41
- package/src/blocks/OcBlockProfileHeader.vue +50 -50
- package/src/blocks/OcBlockShowcase.vue +43 -43
- package/src/blocks/OcBlockSiteFooter.vue +54 -54
- package/src/blocks/OcBlockSiteHeader.vue +34 -34
- package/src/blocks/OcBlockSpotlight.vue +47 -47
- package/src/blocks/OcBlockStats.vue +17 -17
- package/src/blocks/OcBlockStatsCounter.vue +20 -20
- package/src/blocks/OcBlockTeamList.vue +53 -53
- package/src/blocks/OcBlockTwoFactor.vue +61 -61
- package/src/blocks/OcBlockWizardDialog.vue +67 -67
- package/src/components/OcAccordion.vue +12 -12
- package/src/components/OcAccordionAccent.vue +27 -27
- package/src/components/OcAccordionItem.vue +19 -19
- package/src/components/OcAffixInput.vue +40 -40
- package/src/components/OcAlert.vue +17 -17
- package/src/components/OcAlertDialog.vue +66 -66
- package/src/components/OcAlphaTabs.vue +54 -54
- package/src/components/OcAspectRatio.vue +21 -21
- package/src/components/OcAttachment.vue +59 -59
- package/src/components/OcAttachmentImage.vue +13 -13
- package/src/components/OcAttachmentList.vue +31 -31
- package/src/components/OcAttachmentThumb.vue +14 -14
- package/src/components/OcAvatar.vue +18 -18
- package/src/components/OcAvatarGroup.vue +7 -7
- package/src/components/OcBackLink.vue +8 -8
- package/src/components/OcBadge.vue +13 -13
- package/src/components/OcBarcodeInput.vue +51 -51
- package/src/components/OcBreadcrumb.vue +22 -22
- package/src/components/OcBrowserFrame.vue +33 -33
- package/src/components/OcBusyOverlay.vue +32 -32
- package/src/components/OcButton.vue +47 -47
- package/src/components/OcButtonGroup.vue +10 -10
- package/src/components/OcButtonGroupText.vue +7 -7
- package/src/components/OcCard.vue +20 -20
- package/src/components/OcChartLine.vue +80 -80
- package/src/components/OcChatComposer.vue +42 -42
- package/src/components/OcCheckbox.vue +26 -26
- package/src/components/OcChecklist.vue +29 -29
- package/src/components/OcCollapsible.vue +19 -19
- package/src/components/OcColorField.vue +15 -15
- package/src/components/OcColorSelect.vue +33 -33
- package/src/components/OcColumnMenu.vue +50 -50
- package/src/components/OcCommand.vue +119 -119
- package/src/components/OcConfirmDialog.vue +65 -65
- package/src/components/OcCookieConsent.vue +119 -119
- package/src/components/OcCopyField.vue +37 -37
- package/src/components/OcCounter.vue +15 -15
- package/src/components/OcDataTable.vue +144 -144
- package/src/components/OcDefinitionList.vue +35 -35
- package/src/components/OcDropdown.vue +23 -23
- package/src/components/OcDropdownItem.vue +9 -9
- package/src/components/OcDropdownLabel.vue +7 -7
- package/src/components/OcDropdownSeparator.vue +7 -7
- package/src/components/OcDropzone.vue +36 -36
- package/src/components/OcEmojiPicker.vue +45 -45
- package/src/components/OcEmptyState.vue +13 -13
- package/src/components/OcEntityCard.vue +47 -47
- package/src/components/OcFactList.vue +37 -37
- package/src/components/OcFilterCounts.vue +38 -38
- package/src/components/OcFlipCard.vue +36 -36
- package/src/components/OcFormField.vue +36 -36
- package/src/components/OcGreeting.vue +52 -52
- package/src/components/OcHeading.vue +19 -19
- package/src/components/OcHoverCard.vue +25 -25
- package/src/components/OcImageZoom.vue +39 -39
- package/src/components/OcInput.vue +9 -9
- package/src/components/OcInputGroup.vue +12 -12
- package/src/components/OcItem.vue +40 -40
- package/src/components/OcItemGroup.vue +7 -7
- package/src/components/OcItemSeparator.vue +7 -7
- package/src/components/OcKbd.vue +7 -7
- package/src/components/OcKbdGroup.vue +15 -15
- package/src/components/OcLabel.vue +10 -10
- package/src/components/OcLanguageSelect.vue +40 -40
- package/src/components/OcLauncherCard.vue +34 -34
- package/src/components/OcLightbox.vue +63 -63
- package/src/components/OcLinkArrow.vue +25 -25
- package/src/components/OcListToolbar.vue +44 -44
- package/src/components/OcMarquee.vue +36 -36
- package/src/components/OcMediaCard.vue +28 -28
- package/src/components/OcMediaRow.vue +28 -28
- package/src/components/OcMediaThumb.vue +38 -38
- package/src/components/OcMessage.vue +91 -91
- package/src/components/OcMessageList.vue +43 -43
- package/src/components/OcMessageThread.vue +155 -155
- package/src/components/OcModal.vue +42 -42
- package/src/components/OcNavmenu.vue +18 -18
- package/src/components/OcNavmenuList.vue +38 -38
- package/src/components/OcNextTeaser.vue +25 -25
- package/src/components/OcNumberInput.vue +47 -47
- package/src/components/OcOtpInput.vue +34 -34
- package/src/components/OcPageHeader.vue +34 -34
- package/src/components/OcPagination.vue +33 -33
- package/src/components/OcPasswordInput.vue +27 -27
- package/src/components/OcPasswordRequirements.vue +28 -28
- package/src/components/OcPhoneInput.vue +72 -72
- package/src/components/OcPopover.vue +25 -25
- package/src/components/OcPriceCard.vue +69 -69
- package/src/components/OcProductMark.vue +31 -31
- package/src/components/OcProgress.vue +21 -21
- package/src/components/OcProjectCard.vue +36 -36
- package/src/components/OcProse.vue +57 -57
- package/src/components/OcRadio.vue +40 -40
- package/src/components/OcRadioGroup.vue +32 -32
- package/src/components/OcRecoveryCodes.vue +55 -55
- package/src/components/OcScrollArea.vue +15 -15
- package/src/components/OcSegmented.vue +28 -28
- package/src/components/OcSelect.vue +21 -21
- package/src/components/OcSeparator.vue +18 -18
- package/src/components/OcSeverityCard.vue +46 -46
- package/src/components/OcSheet.vue +49 -49
- package/src/components/OcSidebarCollapsible.vue +19 -19
- package/src/components/OcSidebarGroup.vue +11 -11
- package/src/components/OcSidebarMenu.vue +7 -7
- package/src/components/OcSidebarMenuButton.vue +31 -31
- package/src/components/OcSidebarSeparator.vue +7 -7
- package/src/components/OcSkeleton.vue +8 -8
- package/src/components/OcSlider.vue +27 -27
- package/src/components/OcSortableRow.vue +40 -40
- package/src/components/OcSpinner.vue +13 -13
- package/src/components/OcStackCard.vue +24 -24
- package/src/components/OcStackList.vue +31 -31
- package/src/components/OcStatCard.vue +39 -39
- package/src/components/OcStepper.vue +34 -34
- package/src/components/OcSwitch.vue +15 -15
- package/src/components/OcTable.vue +115 -115
- package/src/components/OcTableActions.vue +26 -26
- package/src/components/OcTableCompact.vue +46 -46
- package/src/components/OcTabs.vue +37 -37
- package/src/components/OcTagInput.vue +50 -50
- package/src/components/OcTerminalLog.vue +50 -50
- package/src/components/OcTextarea.vue +9 -9
- package/src/components/OcThemeSwitch.vue +31 -31
- package/src/components/OcTimeInput.vue +50 -50
- package/src/components/OcTimeline.vue +36 -36
- package/src/components/OcToast.vue +31 -31
- package/src/components/OcToastContainer.vue +21 -21
- package/src/components/OcToggle.vue +63 -63
- package/src/components/OcToggleField.vue +28 -28
- package/src/components/OcToggleGroup.vue +40 -40
- package/src/components/OcTooltip.vue +8 -8
- package/src/components/OcTopProgress.vue +54 -54
- package/src/components/OcTree.vue +70 -70
- package/src/components/OcUpgradeBanner.vue +42 -42
- package/src/components/OcUser.vue +25 -25
- package/src/components/OcVersionBanner.vue +34 -34
- package/src/composables/chartLine.ts +28 -28
- package/src/composables/keys.ts +22 -22
- package/src/composables/useAttrsSplit.ts +16 -16
- package/src/composables/useDialog.ts +21 -21
- package/src/composables/useToast.ts +66 -66
- package/src/composables/useUid.ts +12 -12
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcBusyOverlay.vue | Map: props + model -> <dialog closedby="none"> (spinner or progress, title, text) -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Modal "please wait" overlay (same markup as snippets/busy-overlay.html). v-model:open shows it while a long submit or
|
|
5
|
-
* upload runs; the page is inert and Esc does not close it. `progress` (0-100) swaps the spinner for a progress bar.
|
|
6
|
-
* Slot: default (replaces the text). Classes: .oc-busy-overlay __title __text; .oc-spinner / .oc-progress
|
|
7
|
-
*/
|
|
8
|
-
import { ref } from 'vue'
|
|
9
|
-
import { useDialog } from '../composables/useDialog'
|
|
10
|
-
import { useUid } from '../composables/useUid'
|
|
11
|
-
import OcProgress from './OcProgress.vue'
|
|
12
|
-
|
|
13
|
-
const props = withDefaults(defineProps<{ title?: string; text?: string; progress?: number; label?: string; id?: string }>(), {
|
|
14
|
-
title: 'Saving…',
|
|
15
|
-
text: 'The photos are being processed. This can take a minute; please keep this page open.',
|
|
16
|
-
label: 'Busy',
|
|
17
|
-
})
|
|
18
|
-
const open = defineModel<boolean>('open', { default: false })
|
|
19
|
-
const el = ref<HTMLDialogElement | null>(null)
|
|
20
|
-
const { onClose } = useDialog(open, el)
|
|
21
|
-
const auto = useUid('oc-busy-overlay')
|
|
22
|
-
const base = props.id ?? auto
|
|
23
|
-
</script>
|
|
24
|
-
|
|
25
|
-
<template>
|
|
26
|
-
<dialog :id="base" ref="el" class="oc-busy-overlay" closedby="none" :aria-labelledby="`${base}-title`" :aria-describedby="`${base}-text`" @close="onClose">
|
|
27
|
-
<span v-if="progress == null" class="oc-spinner" role="status" :aria-label="label"></span>
|
|
28
|
-
<OcProgress v-else :value="progress" :label="label" />
|
|
29
|
-
<h2 :id="`${base}-title`" class="oc-busy-overlay__title">{{ title }}</h2>
|
|
30
|
-
<p :id="`${base}-text`" class="oc-busy-overlay__text"><slot>{{ text }}</slot></p>
|
|
31
|
-
</dialog>
|
|
32
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcBusyOverlay.vue | Map: props + model -> <dialog closedby="none"> (spinner or progress, title, text) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Modal "please wait" overlay (same markup as snippets/busy-overlay.html). v-model:open shows it while a long submit or
|
|
5
|
+
* upload runs; the page is inert and Esc does not close it. `progress` (0-100) swaps the spinner for a progress bar.
|
|
6
|
+
* Slot: default (replaces the text). Classes: .oc-busy-overlay __title __text; .oc-spinner / .oc-progress
|
|
7
|
+
*/
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
import { useDialog } from '../composables/useDialog'
|
|
10
|
+
import { useUid } from '../composables/useUid'
|
|
11
|
+
import OcProgress from './OcProgress.vue'
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(defineProps<{ title?: string; text?: string; progress?: number; label?: string; id?: string }>(), {
|
|
14
|
+
title: 'Saving…',
|
|
15
|
+
text: 'The photos are being processed. This can take a minute; please keep this page open.',
|
|
16
|
+
label: 'Busy',
|
|
17
|
+
})
|
|
18
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
19
|
+
const el = ref<HTMLDialogElement | null>(null)
|
|
20
|
+
const { onClose } = useDialog(open, el)
|
|
21
|
+
const auto = useUid('oc-busy-overlay')
|
|
22
|
+
const base = props.id ?? auto
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<dialog :id="base" ref="el" class="oc-busy-overlay" closedby="none" :aria-labelledby="`${base}-title`" :aria-describedby="`${base}-text`" @close="onClose">
|
|
27
|
+
<span v-if="progress == null" class="oc-spinner" role="status" :aria-label="label"></span>
|
|
28
|
+
<OcProgress v-else :value="progress" :label="label" />
|
|
29
|
+
<h2 :id="`${base}-title`" class="oc-busy-overlay__title">{{ title }}</h2>
|
|
30
|
+
<p :id="`${base}-text`" class="oc-busy-overlay__text"><slot>{{ text }}</slot></p>
|
|
31
|
+
</dialog>
|
|
32
|
+
</template>
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Button. Renders <a> when `href` is set, otherwise <button>.
|
|
4
|
-
* Classes: .oc-btn + --secondary/--outline/--ghost/--link/--destructive/--success/--gradient/--glass,
|
|
5
|
-
* --xs/--sm/--lg, --icon, --pill
|
|
6
|
-
*/
|
|
7
|
-
withDefaults(
|
|
8
|
-
defineProps<{
|
|
9
|
-
variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive' | 'success' | 'gradient' | 'glass'
|
|
10
|
-
size?: 'xs' | 'sm' | 'md' | 'lg'
|
|
11
|
-
icon?: boolean
|
|
12
|
-
pill?: boolean
|
|
13
|
-
href?: string
|
|
14
|
-
type?: 'button' | 'submit' | 'reset'
|
|
15
|
-
}>(),
|
|
16
|
-
{ variant: 'default', size: 'md', type: 'button' },
|
|
17
|
-
)
|
|
18
|
-
</script>
|
|
19
|
-
|
|
20
|
-
<template>
|
|
21
|
-
<a
|
|
22
|
-
v-if="href"
|
|
23
|
-
:href="href"
|
|
24
|
-
:class="[
|
|
25
|
-
'oc-btn',
|
|
26
|
-
variant !== 'default' && `oc-btn--${variant}`,
|
|
27
|
-
size !== 'md' && `oc-btn--${size}`,
|
|
28
|
-
icon && 'oc-btn--icon',
|
|
29
|
-
pill && 'oc-btn--pill',
|
|
30
|
-
]"
|
|
31
|
-
>
|
|
32
|
-
<slot />
|
|
33
|
-
</a>
|
|
34
|
-
<button
|
|
35
|
-
v-else
|
|
36
|
-
:type="type"
|
|
37
|
-
:class="[
|
|
38
|
-
'oc-btn',
|
|
39
|
-
variant !== 'default' && `oc-btn--${variant}`,
|
|
40
|
-
size !== 'md' && `oc-btn--${size}`,
|
|
41
|
-
icon && 'oc-btn--icon',
|
|
42
|
-
pill && 'oc-btn--pill',
|
|
43
|
-
]"
|
|
44
|
-
>
|
|
45
|
-
<slot />
|
|
46
|
-
</button>
|
|
47
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Button. Renders <a> when `href` is set, otherwise <button>.
|
|
4
|
+
* Classes: .oc-btn + --secondary/--outline/--ghost/--link/--destructive/--success/--gradient/--glass,
|
|
5
|
+
* --xs/--sm/--lg, --icon, --pill
|
|
6
|
+
*/
|
|
7
|
+
withDefaults(
|
|
8
|
+
defineProps<{
|
|
9
|
+
variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive' | 'success' | 'gradient' | 'glass'
|
|
10
|
+
size?: 'xs' | 'sm' | 'md' | 'lg'
|
|
11
|
+
icon?: boolean
|
|
12
|
+
pill?: boolean
|
|
13
|
+
href?: string
|
|
14
|
+
type?: 'button' | 'submit' | 'reset'
|
|
15
|
+
}>(),
|
|
16
|
+
{ variant: 'default', size: 'md', type: 'button' },
|
|
17
|
+
)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<a
|
|
22
|
+
v-if="href"
|
|
23
|
+
:href="href"
|
|
24
|
+
:class="[
|
|
25
|
+
'oc-btn',
|
|
26
|
+
variant !== 'default' && `oc-btn--${variant}`,
|
|
27
|
+
size !== 'md' && `oc-btn--${size}`,
|
|
28
|
+
icon && 'oc-btn--icon',
|
|
29
|
+
pill && 'oc-btn--pill',
|
|
30
|
+
]"
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
</a>
|
|
34
|
+
<button
|
|
35
|
+
v-else
|
|
36
|
+
:type="type"
|
|
37
|
+
:class="[
|
|
38
|
+
'oc-btn',
|
|
39
|
+
variant !== 'default' && `oc-btn--${variant}`,
|
|
40
|
+
size !== 'md' && `oc-btn--${size}`,
|
|
41
|
+
icon && 'oc-btn--icon',
|
|
42
|
+
pill && 'oc-btn--pill',
|
|
43
|
+
]"
|
|
44
|
+
>
|
|
45
|
+
<slot />
|
|
46
|
+
</button>
|
|
47
|
+
</template>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/** Joined buttons with shared borders. `label` becomes aria-label. Put OcButton (usually variant outline) and OcButtonGroupText inside. Classes: .oc-btn-group(--vertical) */
|
|
3
|
-
defineProps<{ vertical?: boolean; label?: string }>()
|
|
4
|
-
</script>
|
|
5
|
-
|
|
6
|
-
<template>
|
|
7
|
-
<div :class="['oc-btn-group', vertical && 'oc-btn-group--vertical']" role="group" :aria-label="label">
|
|
8
|
-
<slot />
|
|
9
|
-
</div>
|
|
10
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Joined buttons with shared borders. `label` becomes aria-label. Put OcButton (usually variant outline) and OcButtonGroupText inside. Classes: .oc-btn-group(--vertical) */
|
|
3
|
+
defineProps<{ vertical?: boolean; label?: string }>()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<div :class="['oc-btn-group', vertical && 'oc-btn-group--vertical']" role="group" :aria-label="label">
|
|
8
|
+
<slot />
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/** Static text cell inside OcButtonGroup (a count, a unit). Class: .oc-btn-group-text */
|
|
3
|
-
</script>
|
|
4
|
-
|
|
5
|
-
<template>
|
|
6
|
-
<span class="oc-btn-group-text"><slot /></span>
|
|
7
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Static text cell inside OcButtonGroup (a count, a unit). Class: .oc-btn-group-text */
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<span class="oc-btn-group-text"><slot /></span>
|
|
7
|
+
</template>
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Card with header, content and footer.
|
|
4
|
-
* Slots: header (replaces title/description), default (content), footer. Classes: .oc-card--sm
|
|
5
|
-
*/
|
|
6
|
-
withDefaults(defineProps<{ title?: string; description?: string; size?: 'sm' | 'md' }>(), { size: 'md' })
|
|
7
|
-
</script>
|
|
8
|
-
|
|
9
|
-
<template>
|
|
10
|
-
<div :class="['oc-card', size === 'sm' && 'oc-card--sm']">
|
|
11
|
-
<div v-if="title || description || $slots.header" class="oc-card-header">
|
|
12
|
-
<slot name="header">
|
|
13
|
-
<h3 v-if="title" class="oc-card-title">{{ title }}</h3>
|
|
14
|
-
<p v-if="description" class="oc-card-description">{{ description }}</p>
|
|
15
|
-
</slot>
|
|
16
|
-
</div>
|
|
17
|
-
<div v-if="$slots.default" class="oc-card-content"><slot /></div>
|
|
18
|
-
<div v-if="$slots.footer" class="oc-card-footer"><slot name="footer" /></div>
|
|
19
|
-
</div>
|
|
20
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Card with header, content and footer.
|
|
4
|
+
* Slots: header (replaces title/description), default (content), footer. Classes: .oc-card--sm
|
|
5
|
+
*/
|
|
6
|
+
withDefaults(defineProps<{ title?: string; description?: string; size?: 'sm' | 'md' }>(), { size: 'md' })
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div :class="['oc-card', size === 'sm' && 'oc-card--sm']">
|
|
11
|
+
<div v-if="title || description || $slots.header" class="oc-card-header">
|
|
12
|
+
<slot name="header">
|
|
13
|
+
<h3 v-if="title" class="oc-card-title">{{ title }}</h3>
|
|
14
|
+
<p v-if="description" class="oc-card-description">{{ description }}</p>
|
|
15
|
+
</slot>
|
|
16
|
+
</div>
|
|
17
|
+
<div v-if="$slots.default" class="oc-card-content"><slot /></div>
|
|
18
|
+
<div v-if="$slots.footer" class="oc-card-footer"><slot name="footer" /></div>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcChartLine.vue | Map: props + demo data -> computed axis / paths -> figure (head, y axis, plot: svg + hover columns, x axis, legend) -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Line / area chart in inline SVG (same markup as snippets/chart-line.html); tooltips open on :hover / :focus-visible (CSS only).
|
|
5
|
-
* series: { name, values, dashed? }[]; labels = one per point (tooltips), xLabels = short axis labels. max defaults to a round
|
|
6
|
-
* number above the data. variant: line | area | spark; dots = always visible. Classes: .oc-chart-line(--area/--dots/--spark) __head
|
|
7
|
-
* __title __sub __value __body __y __plot __svg __series __area __path(--dashed) __cols __col __dot __tip __row __x; .oc-chart-legend
|
|
8
|
-
*/
|
|
9
|
-
import { computed } from 'vue'
|
|
10
|
-
import { chartLinePath, n2, niceMax } from '../composables/chartLine'
|
|
11
|
-
|
|
12
|
-
export interface ChartLineSeries { name: string; values: number[]; dashed?: boolean }
|
|
13
|
-
const props = withDefaults(
|
|
14
|
-
defineProps<{
|
|
15
|
-
title?: string
|
|
16
|
-
sub?: string
|
|
17
|
-
value?: string
|
|
18
|
-
series?: ChartLineSeries[]
|
|
19
|
-
labels?: string[]
|
|
20
|
-
xLabels?: string[]
|
|
21
|
-
max?: number
|
|
22
|
-
rows?: number
|
|
23
|
-
variant?: 'line' | 'area' | 'spark'
|
|
24
|
-
dots?: boolean
|
|
25
|
-
label?: string
|
|
26
|
-
}>(),
|
|
27
|
-
{
|
|
28
|
-
title: 'Visitors',
|
|
29
|
-
sub: 'January – June 2026',
|
|
30
|
-
series: () => [
|
|
31
|
-
{ name: 'Desktop', values: [186, 305, 237, 73, 209, 214] },
|
|
32
|
-
{ name: 'Mobile', values: [80, 200, 120, 190, 130, 140] },
|
|
33
|
-
],
|
|
34
|
-
rows: 4,
|
|
35
|
-
variant: 'line',
|
|
36
|
-
dots: true,
|
|
37
|
-
label: 'Visitors per month',
|
|
38
|
-
},
|
|
39
|
-
)
|
|
40
|
-
const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June']
|
|
41
|
-
const labels = computed(() => props.labels ?? MONTHS)
|
|
42
|
-
const axis = computed(() => props.xLabels ?? props.labels ?? MONTHS.map((m) => m.slice(0, 3)))
|
|
43
|
-
const top = computed(() => props.max ?? niceMax(Math.max(0, ...props.series.flatMap((s) => s.values)), props.rows))
|
|
44
|
-
const count = computed(() => Math.max(0, ...props.series.map((s) => s.values.length)))
|
|
45
|
-
const paths = computed(() => props.series.map((s) => chartLinePath(s.values, top.value)))
|
|
46
|
-
const yLabels = computed(() => Array.from({ length: props.rows + 1 }, (_, i) => top.value - (top.value / props.rows) * i))
|
|
47
|
-
const colLabel = (i: number) => `${labels.value[i] ?? i + 1}: ${props.series.map((s) => `${s.name} ${s.values[i]}`).join(', ')}`
|
|
48
|
-
</script>
|
|
49
|
-
|
|
50
|
-
<template>
|
|
51
|
-
<figure :class="['oc-chart-line', variant !== 'line' && `oc-chart-line--${variant}`, dots && 'oc-chart-line--dots']">
|
|
52
|
-
<figcaption v-if="title || sub || value" class="oc-chart-line__head">
|
|
53
|
-
<span v-if="title" class="oc-chart-line__title">{{ title }}</span>
|
|
54
|
-
<span v-if="sub" class="oc-chart-line__sub">{{ sub }}</span>
|
|
55
|
-
<span v-if="value" class="oc-chart-line__value">{{ value }}</span>
|
|
56
|
-
</figcaption>
|
|
57
|
-
<div class="oc-chart-line__body">
|
|
58
|
-
<ul class="oc-chart-line__y" aria-hidden="true"><li v-for="(y, i) in yLabels" :key="i">{{ y }}</li></ul>
|
|
59
|
-
<div class="oc-chart-line__plot" :style="rows !== 4 ? `--oc-chart-line-rows:${rows}` : undefined">
|
|
60
|
-
<svg class="oc-chart-line__svg" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
|
|
61
|
-
<g v-for="(s, i) in series" :key="i" class="oc-chart-line__series">
|
|
62
|
-
<path class="oc-chart-line__area" :d="`${paths[i]} L100 100 L0 100 Z`" />
|
|
63
|
-
<path :class="['oc-chart-line__path', s.dashed && 'oc-chart-line__path--dashed']" :d="paths[i]" />
|
|
64
|
-
</g>
|
|
65
|
-
</svg>
|
|
66
|
-
<ul class="oc-chart-line__cols" :style="`--n:${count}`" :aria-label="label">
|
|
67
|
-
<li v-for="i in count" :key="i" class="oc-chart-line__col" :style="`--x:${i - 1}`" tabindex="0" :aria-label="colLabel(i - 1)">
|
|
68
|
-
<i v-for="(s, j) in series" :key="j" class="oc-chart-line__dot" :style="`--y:${n2((s.values[i - 1] / top) * 100)}%`"></i>
|
|
69
|
-
<span class="oc-chart-line__tip" aria-hidden="true">
|
|
70
|
-
<strong>{{ labels[i - 1] ?? i }}</strong>
|
|
71
|
-
<span v-for="(s, j) in series" :key="j" class="oc-chart-line__row">{{ s.name }}<b>{{ s.values[i - 1] }}</b></span>
|
|
72
|
-
</span>
|
|
73
|
-
</li>
|
|
74
|
-
</ul>
|
|
75
|
-
</div>
|
|
76
|
-
<ol class="oc-chart-line__x" aria-hidden="true"><li v-for="(l, i) in axis" :key="i">{{ l }}</li></ol>
|
|
77
|
-
</div>
|
|
78
|
-
<ul class="oc-chart-legend"><li v-for="(s, i) in series" :key="i"><span class="oc-chart-swatch"></span>{{ s.name }}</li></ul>
|
|
79
|
-
</figure>
|
|
80
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcChartLine.vue | Map: props + demo data -> computed axis / paths -> figure (head, y axis, plot: svg + hover columns, x axis, legend) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Line / area chart in inline SVG (same markup as snippets/chart-line.html); tooltips open on :hover / :focus-visible (CSS only).
|
|
5
|
+
* series: { name, values, dashed? }[]; labels = one per point (tooltips), xLabels = short axis labels. max defaults to a round
|
|
6
|
+
* number above the data. variant: line | area | spark; dots = always visible. Classes: .oc-chart-line(--area/--dots/--spark) __head
|
|
7
|
+
* __title __sub __value __body __y __plot __svg __series __area __path(--dashed) __cols __col __dot __tip __row __x; .oc-chart-legend
|
|
8
|
+
*/
|
|
9
|
+
import { computed } from 'vue'
|
|
10
|
+
import { chartLinePath, n2, niceMax } from '../composables/chartLine'
|
|
11
|
+
|
|
12
|
+
export interface ChartLineSeries { name: string; values: number[]; dashed?: boolean }
|
|
13
|
+
const props = withDefaults(
|
|
14
|
+
defineProps<{
|
|
15
|
+
title?: string
|
|
16
|
+
sub?: string
|
|
17
|
+
value?: string
|
|
18
|
+
series?: ChartLineSeries[]
|
|
19
|
+
labels?: string[]
|
|
20
|
+
xLabels?: string[]
|
|
21
|
+
max?: number
|
|
22
|
+
rows?: number
|
|
23
|
+
variant?: 'line' | 'area' | 'spark'
|
|
24
|
+
dots?: boolean
|
|
25
|
+
label?: string
|
|
26
|
+
}>(),
|
|
27
|
+
{
|
|
28
|
+
title: 'Visitors',
|
|
29
|
+
sub: 'January – June 2026',
|
|
30
|
+
series: () => [
|
|
31
|
+
{ name: 'Desktop', values: [186, 305, 237, 73, 209, 214] },
|
|
32
|
+
{ name: 'Mobile', values: [80, 200, 120, 190, 130, 140] },
|
|
33
|
+
],
|
|
34
|
+
rows: 4,
|
|
35
|
+
variant: 'line',
|
|
36
|
+
dots: true,
|
|
37
|
+
label: 'Visitors per month',
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June']
|
|
41
|
+
const labels = computed(() => props.labels ?? MONTHS)
|
|
42
|
+
const axis = computed(() => props.xLabels ?? props.labels ?? MONTHS.map((m) => m.slice(0, 3)))
|
|
43
|
+
const top = computed(() => props.max ?? niceMax(Math.max(0, ...props.series.flatMap((s) => s.values)), props.rows))
|
|
44
|
+
const count = computed(() => Math.max(0, ...props.series.map((s) => s.values.length)))
|
|
45
|
+
const paths = computed(() => props.series.map((s) => chartLinePath(s.values, top.value)))
|
|
46
|
+
const yLabels = computed(() => Array.from({ length: props.rows + 1 }, (_, i) => top.value - (top.value / props.rows) * i))
|
|
47
|
+
const colLabel = (i: number) => `${labels.value[i] ?? i + 1}: ${props.series.map((s) => `${s.name} ${s.values[i]}`).join(', ')}`
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<figure :class="['oc-chart-line', variant !== 'line' && `oc-chart-line--${variant}`, dots && 'oc-chart-line--dots']">
|
|
52
|
+
<figcaption v-if="title || sub || value" class="oc-chart-line__head">
|
|
53
|
+
<span v-if="title" class="oc-chart-line__title">{{ title }}</span>
|
|
54
|
+
<span v-if="sub" class="oc-chart-line__sub">{{ sub }}</span>
|
|
55
|
+
<span v-if="value" class="oc-chart-line__value">{{ value }}</span>
|
|
56
|
+
</figcaption>
|
|
57
|
+
<div class="oc-chart-line__body">
|
|
58
|
+
<ul class="oc-chart-line__y" aria-hidden="true"><li v-for="(y, i) in yLabels" :key="i">{{ y }}</li></ul>
|
|
59
|
+
<div class="oc-chart-line__plot" :style="rows !== 4 ? `--oc-chart-line-rows:${rows}` : undefined">
|
|
60
|
+
<svg class="oc-chart-line__svg" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
|
|
61
|
+
<g v-for="(s, i) in series" :key="i" class="oc-chart-line__series">
|
|
62
|
+
<path class="oc-chart-line__area" :d="`${paths[i]} L100 100 L0 100 Z`" />
|
|
63
|
+
<path :class="['oc-chart-line__path', s.dashed && 'oc-chart-line__path--dashed']" :d="paths[i]" />
|
|
64
|
+
</g>
|
|
65
|
+
</svg>
|
|
66
|
+
<ul class="oc-chart-line__cols" :style="`--n:${count}`" :aria-label="label">
|
|
67
|
+
<li v-for="i in count" :key="i" class="oc-chart-line__col" :style="`--x:${i - 1}`" tabindex="0" :aria-label="colLabel(i - 1)">
|
|
68
|
+
<i v-for="(s, j) in series" :key="j" class="oc-chart-line__dot" :style="`--y:${n2((s.values[i - 1] / top) * 100)}%`"></i>
|
|
69
|
+
<span class="oc-chart-line__tip" aria-hidden="true">
|
|
70
|
+
<strong>{{ labels[i - 1] ?? i }}</strong>
|
|
71
|
+
<span v-for="(s, j) in series" :key="j" class="oc-chart-line__row">{{ s.name }}<b>{{ s.values[i - 1] }}</b></span>
|
|
72
|
+
</span>
|
|
73
|
+
</li>
|
|
74
|
+
</ul>
|
|
75
|
+
</div>
|
|
76
|
+
<ol class="oc-chart-line__x" aria-hidden="true"><li v-for="(l, i) in axis" :key="i">{{ l }}</li></ol>
|
|
77
|
+
</div>
|
|
78
|
+
<ul class="oc-chart-legend"><li v-for="(s, i) in series" :key="i"><span class="oc-chart-swatch"></span>{{ s.name }}</li></ul>
|
|
79
|
+
</figure>
|
|
80
|
+
</template>
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcChatComposer.vue | Map: props + v-model -> form -> label + textarea -> bar (tools slot, hint, send) -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Chat composer (same markup as snippets/chat-composer.html). v-model = text. Emits send(text) on submit
|
|
5
|
-
* (then clears the field). Enter sends, Shift+Enter adds a line (sendOnEnter, default true).
|
|
6
|
-
* Props: placeholder, label, hint, inline, disabled. Slot: tools (.oc-chat-composer__tool buttons).
|
|
7
|
-
* Classes: .oc-chat-composer(--inline), __input, __bar, __tools, __tool, __hint, __send
|
|
8
|
-
*/
|
|
9
|
-
import { useUid } from '../composables/useUid'
|
|
10
|
-
|
|
11
|
-
const props = withDefaults(
|
|
12
|
-
defineProps<{ placeholder?: string; label?: string; hint?: string; inline?: boolean; sendOnEnter?: boolean; disabled?: boolean }>(),
|
|
13
|
-
{ placeholder: 'Write a message…', label: 'Message', sendOnEnter: true },
|
|
14
|
-
)
|
|
15
|
-
const text = defineModel<string>({ default: '' })
|
|
16
|
-
const emit = defineEmits<{ send: [text: string] }>()
|
|
17
|
-
const id = useUid('oc-chat')
|
|
18
|
-
|
|
19
|
-
function submit() {
|
|
20
|
-
if (!text.value.trim() || props.disabled) return
|
|
21
|
-
emit('send', text.value.trim())
|
|
22
|
-
text.value = ''
|
|
23
|
-
}
|
|
24
|
-
function onKeydown(e: KeyboardEvent) {
|
|
25
|
-
if (props.sendOnEnter && e.key === 'Enter' && !e.shiftKey && !e.isComposing) {
|
|
26
|
-
e.preventDefault()
|
|
27
|
-
;(e.target as HTMLTextAreaElement).form?.requestSubmit()
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
</script>
|
|
31
|
-
|
|
32
|
-
<template>
|
|
33
|
-
<form :class="['oc-chat-composer', inline && 'oc-chat-composer--inline']" @submit.prevent="submit">
|
|
34
|
-
<label class="oc-visually-hidden" :for="id">{{ label }}</label>
|
|
35
|
-
<textarea :id="id" v-model="text" class="oc-chat-composer__input" rows="1" :placeholder="placeholder" required :disabled="disabled" @keydown="onKeydown" />
|
|
36
|
-
<div class="oc-chat-composer__bar">
|
|
37
|
-
<div v-if="$slots.tools" class="oc-chat-composer__tools"><slot name="tools" /></div>
|
|
38
|
-
<span v-if="hint" class="oc-chat-composer__hint">{{ hint }}</span>
|
|
39
|
-
<button type="submit" class="oc-chat-composer__send" aria-label="Send" :disabled="disabled">➤</button>
|
|
40
|
-
</div>
|
|
41
|
-
</form>
|
|
42
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcChatComposer.vue | Map: props + v-model -> form -> label + textarea -> bar (tools slot, hint, send) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Chat composer (same markup as snippets/chat-composer.html). v-model = text. Emits send(text) on submit
|
|
5
|
+
* (then clears the field). Enter sends, Shift+Enter adds a line (sendOnEnter, default true).
|
|
6
|
+
* Props: placeholder, label, hint, inline, disabled. Slot: tools (.oc-chat-composer__tool buttons).
|
|
7
|
+
* Classes: .oc-chat-composer(--inline), __input, __bar, __tools, __tool, __hint, __send
|
|
8
|
+
*/
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<{ placeholder?: string; label?: string; hint?: string; inline?: boolean; sendOnEnter?: boolean; disabled?: boolean }>(),
|
|
13
|
+
{ placeholder: 'Write a message…', label: 'Message', sendOnEnter: true },
|
|
14
|
+
)
|
|
15
|
+
const text = defineModel<string>({ default: '' })
|
|
16
|
+
const emit = defineEmits<{ send: [text: string] }>()
|
|
17
|
+
const id = useUid('oc-chat')
|
|
18
|
+
|
|
19
|
+
function submit() {
|
|
20
|
+
if (!text.value.trim() || props.disabled) return
|
|
21
|
+
emit('send', text.value.trim())
|
|
22
|
+
text.value = ''
|
|
23
|
+
}
|
|
24
|
+
function onKeydown(e: KeyboardEvent) {
|
|
25
|
+
if (props.sendOnEnter && e.key === 'Enter' && !e.shiftKey && !e.isComposing) {
|
|
26
|
+
e.preventDefault()
|
|
27
|
+
;(e.target as HTMLTextAreaElement).form?.requestSubmit()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<form :class="['oc-chat-composer', inline && 'oc-chat-composer--inline']" @submit.prevent="submit">
|
|
34
|
+
<label class="oc-visually-hidden" :for="id">{{ label }}</label>
|
|
35
|
+
<textarea :id="id" v-model="text" class="oc-chat-composer__input" rows="1" :placeholder="placeholder" required :disabled="disabled" @keydown="onKeydown" />
|
|
36
|
+
<div class="oc-chat-composer__bar">
|
|
37
|
+
<div v-if="$slots.tools" class="oc-chat-composer__tools"><slot name="tools" /></div>
|
|
38
|
+
<span v-if="hint" class="oc-chat-composer__hint">{{ hint }}</span>
|
|
39
|
+
<button type="submit" class="oc-chat-composer__send" aria-label="Send" :disabled="disabled">➤</button>
|
|
40
|
+
</div>
|
|
41
|
+
</form>
|
|
42
|
+
</template>
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Checkbox or radio inside a label. v-model: boolean (checkbox), array (checkbox group with `value`)
|
|
4
|
-
* or the selected `value` (radio). Attributes other than class/style go to the input.
|
|
5
|
-
*/
|
|
6
|
-
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
7
|
-
|
|
8
|
-
defineOptions({ inheritAttrs: false })
|
|
9
|
-
withDefaults(
|
|
10
|
-
defineProps<{
|
|
11
|
-
type?: 'checkbox' | 'radio'
|
|
12
|
-
value?: unknown
|
|
13
|
-
label?: string
|
|
14
|
-
}>(),
|
|
15
|
-
{ type: 'checkbox' },
|
|
16
|
-
)
|
|
17
|
-
const model = defineModel<any>()
|
|
18
|
-
const { root, control } = useAttrsSplit()
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
<template>
|
|
22
|
-
<label v-bind="root">
|
|
23
|
-
<input v-bind="control" v-model="model" :class="type === 'radio' ? 'oc-radio' : 'oc-checkbox'" :type="type" :value="value" />
|
|
24
|
-
<slot><template v-if="label"> {{ label }}</template></slot>
|
|
25
|
-
</label>
|
|
26
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Checkbox or radio inside a label. v-model: boolean (checkbox), array (checkbox group with `value`)
|
|
4
|
+
* or the selected `value` (radio). Attributes other than class/style go to the input.
|
|
5
|
+
*/
|
|
6
|
+
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
7
|
+
|
|
8
|
+
defineOptions({ inheritAttrs: false })
|
|
9
|
+
withDefaults(
|
|
10
|
+
defineProps<{
|
|
11
|
+
type?: 'checkbox' | 'radio'
|
|
12
|
+
value?: unknown
|
|
13
|
+
label?: string
|
|
14
|
+
}>(),
|
|
15
|
+
{ type: 'checkbox' },
|
|
16
|
+
)
|
|
17
|
+
const model = defineModel<any>()
|
|
18
|
+
const { root, control } = useAttrsSplit()
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<label v-bind="root">
|
|
23
|
+
<input v-bind="control" v-model="model" :class="type === 'radio' ? 'oc-radio' : 'oc-checkbox'" :type="type" :value="value" />
|
|
24
|
+
<slot><template v-if="label"> {{ label }}</template></slot>
|
|
25
|
+
</label>
|
|
26
|
+
</template>
|
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Rule / step list with met and unmet states. Classes: .oc-checklist(--card), .oc-checklist-item.is-done
|
|
4
|
-
* Slots: default (replaces all rows), action (scoped: item) in the card variant.
|
|
5
|
-
*/
|
|
6
|
-
export interface ChecklistItem {
|
|
7
|
-
title: string
|
|
8
|
-
hint?: string
|
|
9
|
-
done?: boolean
|
|
10
|
-
}
|
|
11
|
-
withDefaults(defineProps<{ items?: ChecklistItem[]; card?: boolean; label?: string }>(), { items: () => [] })
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<template>
|
|
15
|
-
<ul :class="['oc-checklist', card && 'oc-checklist--card']" aria-live="polite" :aria-label="label">
|
|
16
|
-
<slot>
|
|
17
|
-
<li v-for="(item, i) in items" :key="i" :class="['oc-checklist-item', item.done && 'is-done']">
|
|
18
|
-
<template v-if="card">
|
|
19
|
-
<span class="oc-checklist-text">
|
|
20
|
-
<span class="oc-checklist-title">{{ item.title }}</span>
|
|
21
|
-
<span v-if="item.hint" class="oc-checklist-hint">{{ item.hint }}</span>
|
|
22
|
-
</span>
|
|
23
|
-
<slot name="action" :item="item" />
|
|
24
|
-
</template>
|
|
25
|
-
<template v-else>{{ item.title }}</template>
|
|
26
|
-
</li>
|
|
27
|
-
</slot>
|
|
28
|
-
</ul>
|
|
29
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Rule / step list with met and unmet states. Classes: .oc-checklist(--card), .oc-checklist-item.is-done
|
|
4
|
+
* Slots: default (replaces all rows), action (scoped: item) in the card variant.
|
|
5
|
+
*/
|
|
6
|
+
export interface ChecklistItem {
|
|
7
|
+
title: string
|
|
8
|
+
hint?: string
|
|
9
|
+
done?: boolean
|
|
10
|
+
}
|
|
11
|
+
withDefaults(defineProps<{ items?: ChecklistItem[]; card?: boolean; label?: string }>(), { items: () => [] })
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<ul :class="['oc-checklist', card && 'oc-checklist--card']" aria-live="polite" :aria-label="label">
|
|
16
|
+
<slot>
|
|
17
|
+
<li v-for="(item, i) in items" :key="i" :class="['oc-checklist-item', item.done && 'is-done']">
|
|
18
|
+
<template v-if="card">
|
|
19
|
+
<span class="oc-checklist-text">
|
|
20
|
+
<span class="oc-checklist-title">{{ item.title }}</span>
|
|
21
|
+
<span v-if="item.hint" class="oc-checklist-hint">{{ item.hint }}</span>
|
|
22
|
+
</span>
|
|
23
|
+
<slot name="action" :item="item" />
|
|
24
|
+
</template>
|
|
25
|
+
<template v-else>{{ item.title }}</template>
|
|
26
|
+
</li>
|
|
27
|
+
</slot>
|
|
28
|
+
</ul>
|
|
29
|
+
</template>
|