@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,67 +1,67 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Modal wizard: stepper rail on the left, step content and a Back / Next footer.
|
|
4
|
-
* v-model:open opens the dialog, v-model:step is the active step index. Emits `finish` on the last step's button.
|
|
5
|
-
* Slots: step-<index> (or default, scoped: { step, index }) for the content of each step.
|
|
6
|
-
* Classes: dialog.oc-modal > .oc-modal-content--xl, .oc-block-wizard(-rail/-main/-step), .oc-stepper, .oc-modal-footer
|
|
7
|
-
*/
|
|
8
|
-
import { computed, ref } from 'vue'
|
|
9
|
-
import OcStepper from '../components/OcStepper.vue'
|
|
10
|
-
import { useDialog } from '../composables/useDialog'
|
|
11
|
-
import { useUid } from '../composables/useUid'
|
|
12
|
-
|
|
13
|
-
const props = withDefaults(
|
|
14
|
-
defineProps<{
|
|
15
|
-
steps: { label: string }[]
|
|
16
|
-
backLabel?: string
|
|
17
|
-
nextLabel?: string
|
|
18
|
-
finishLabel?: string
|
|
19
|
-
stepsLabel?: string
|
|
20
|
-
/** Text for "Step 2 of 3"; receives the 1-based index and the count */
|
|
21
|
-
stepText?: (index: number, total: number) => string
|
|
22
|
-
}>(),
|
|
23
|
-
{ backLabel: 'Back', nextLabel: 'Next', finishLabel: 'Finish', stepsLabel: 'Setup steps' },
|
|
24
|
-
)
|
|
25
|
-
const emit = defineEmits<{ finish: [] }>()
|
|
26
|
-
const open = defineModel<boolean>('open', { default: false })
|
|
27
|
-
const step = defineModel<number>('step', { default: 0 })
|
|
28
|
-
const el = ref<HTMLDialogElement | null>(null)
|
|
29
|
-
const { onClose } = useDialog(open, el)
|
|
30
|
-
const titleId = useUid('oc-wizard-title')
|
|
31
|
-
const last = computed(() => step.value >= props.steps.length - 1)
|
|
32
|
-
const description = computed(() => (props.stepText ?? ((i, n) => `Step ${i} of ${n}`))(step.value + 1, props.steps.length))
|
|
33
|
-
|
|
34
|
-
function next() {
|
|
35
|
-
if (last.value) {
|
|
36
|
-
emit('finish')
|
|
37
|
-
open.value = false
|
|
38
|
-
} else {
|
|
39
|
-
step.value++
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
</script>
|
|
43
|
-
|
|
44
|
-
<template>
|
|
45
|
-
<dialog ref="el" class="oc-modal" closedby="closerequest" :aria-labelledby="titleId" @close="onClose">
|
|
46
|
-
<div class="oc-modal-content oc-modal-content--xl">
|
|
47
|
-
<div class="oc-block-wizard">
|
|
48
|
-
<div class="oc-block-wizard-rail">
|
|
49
|
-
<OcStepper v-model="step" :steps="steps" :label="stepsLabel" />
|
|
50
|
-
</div>
|
|
51
|
-
<div class="oc-block-wizard-main">
|
|
52
|
-
<div class="oc-modal-header">
|
|
53
|
-
<h2 :id="titleId" class="oc-modal-title">{{ steps[step]?.label }}</h2>
|
|
54
|
-
<p class="oc-modal-description">{{ description }}</p>
|
|
55
|
-
</div>
|
|
56
|
-
<div class="oc-block-wizard-step">
|
|
57
|
-
<slot :name="`step-${step}`" :step="steps[step]" :index="step"><slot :step="steps[step]" :index="step" /></slot>
|
|
58
|
-
</div>
|
|
59
|
-
<div class="oc-modal-footer">
|
|
60
|
-
<button class="oc-btn oc-btn--outline" type="button" :disabled="step === 0" @click="step--">{{ backLabel }}</button>
|
|
61
|
-
<button class="oc-btn" type="button" @click="next">{{ last ? finishLabel : nextLabel }}</button>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
</dialog>
|
|
67
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Modal wizard: stepper rail on the left, step content and a Back / Next footer.
|
|
4
|
+
* v-model:open opens the dialog, v-model:step is the active step index. Emits `finish` on the last step's button.
|
|
5
|
+
* Slots: step-<index> (or default, scoped: { step, index }) for the content of each step.
|
|
6
|
+
* Classes: dialog.oc-modal > .oc-modal-content--xl, .oc-block-wizard(-rail/-main/-step), .oc-stepper, .oc-modal-footer
|
|
7
|
+
*/
|
|
8
|
+
import { computed, ref } from 'vue'
|
|
9
|
+
import OcStepper from '../components/OcStepper.vue'
|
|
10
|
+
import { useDialog } from '../composables/useDialog'
|
|
11
|
+
import { useUid } from '../composables/useUid'
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(
|
|
14
|
+
defineProps<{
|
|
15
|
+
steps: { label: string }[]
|
|
16
|
+
backLabel?: string
|
|
17
|
+
nextLabel?: string
|
|
18
|
+
finishLabel?: string
|
|
19
|
+
stepsLabel?: string
|
|
20
|
+
/** Text for "Step 2 of 3"; receives the 1-based index and the count */
|
|
21
|
+
stepText?: (index: number, total: number) => string
|
|
22
|
+
}>(),
|
|
23
|
+
{ backLabel: 'Back', nextLabel: 'Next', finishLabel: 'Finish', stepsLabel: 'Setup steps' },
|
|
24
|
+
)
|
|
25
|
+
const emit = defineEmits<{ finish: [] }>()
|
|
26
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
27
|
+
const step = defineModel<number>('step', { default: 0 })
|
|
28
|
+
const el = ref<HTMLDialogElement | null>(null)
|
|
29
|
+
const { onClose } = useDialog(open, el)
|
|
30
|
+
const titleId = useUid('oc-wizard-title')
|
|
31
|
+
const last = computed(() => step.value >= props.steps.length - 1)
|
|
32
|
+
const description = computed(() => (props.stepText ?? ((i, n) => `Step ${i} of ${n}`))(step.value + 1, props.steps.length))
|
|
33
|
+
|
|
34
|
+
function next() {
|
|
35
|
+
if (last.value) {
|
|
36
|
+
emit('finish')
|
|
37
|
+
open.value = false
|
|
38
|
+
} else {
|
|
39
|
+
step.value++
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<dialog ref="el" class="oc-modal" closedby="closerequest" :aria-labelledby="titleId" @close="onClose">
|
|
46
|
+
<div class="oc-modal-content oc-modal-content--xl">
|
|
47
|
+
<div class="oc-block-wizard">
|
|
48
|
+
<div class="oc-block-wizard-rail">
|
|
49
|
+
<OcStepper v-model="step" :steps="steps" :label="stepsLabel" />
|
|
50
|
+
</div>
|
|
51
|
+
<div class="oc-block-wizard-main">
|
|
52
|
+
<div class="oc-modal-header">
|
|
53
|
+
<h2 :id="titleId" class="oc-modal-title">{{ steps[step]?.label }}</h2>
|
|
54
|
+
<p class="oc-modal-description">{{ description }}</p>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="oc-block-wizard-step">
|
|
57
|
+
<slot :name="`step-${step}`" :step="steps[step]" :index="step"><slot :step="steps[step]" :index="step" /></slot>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="oc-modal-footer">
|
|
60
|
+
<button class="oc-btn oc-btn--outline" type="button" :disabled="step === 0" @click="step--">{{ backLabel }}</button>
|
|
61
|
+
<button class="oc-btn" type="button" @click="next">{{ last ? finishLabel : nextLabel }}</button>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</dialog>
|
|
67
|
+
</template>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/** Exclusive accordion on <details name>. Put OcAccordionItem children inside; they share one group name. */
|
|
3
|
-
import { provide } from 'vue'
|
|
4
|
-
import { useUid } from '../composables/useUid'
|
|
5
|
-
|
|
6
|
-
const props = defineProps<{ name?: string }>()
|
|
7
|
-
provide('oc-accordion-name', props.name ?? useUid('oc-accordion'))
|
|
8
|
-
</script>
|
|
9
|
-
|
|
10
|
-
<template>
|
|
11
|
-
<div class="oc-accordion"><slot /></div>
|
|
12
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Exclusive accordion on <details name>. Put OcAccordionItem children inside; they share one group name. */
|
|
3
|
+
import { provide } from 'vue'
|
|
4
|
+
import { useUid } from '../composables/useUid'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ name?: string }>()
|
|
7
|
+
provide('oc-accordion-name', props.name ?? useUid('oc-accordion'))
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div class="oc-accordion"><slot /></div>
|
|
12
|
+
</template>
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcAccordionAccent.vue | Map: div.oc-accordion.oc-accordion--accent (+ color classes) -> OcAccordionItem children -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Accent accordion: separate item cards with a colored start edge. Put OcAccordionItem children inside;
|
|
5
|
-
* they share one group name (like OcAccordion). Color one item with class "oc-accent--violet" or "oc-accordion-item--warning".
|
|
6
|
-
* Classes: .oc-accordion--accent, .oc-accent--*, .oc-accordion--success/--warning/--danger, .oc-accordion--flush, .oc-accordion-accent-meta
|
|
7
|
-
*/
|
|
8
|
-
import { provide } from 'vue'
|
|
9
|
-
import { useUid } from '../composables/useUid'
|
|
10
|
-
|
|
11
|
-
const props = defineProps<{
|
|
12
|
-
name?: string
|
|
13
|
-
/** Color preset for all items. */
|
|
14
|
-
accent?: 'blue' | 'cyan' | 'green' | 'orange' | 'red' | 'violet' | 'yellow'
|
|
15
|
-
/** Semantic color for all items. */
|
|
16
|
-
tone?: 'success' | 'warning' | 'danger'
|
|
17
|
-
flush?: boolean
|
|
18
|
-
}>()
|
|
19
|
-
// Same key as OcAccordion, so OcAccordionItem picks up the group name.
|
|
20
|
-
provide('oc-accordion-name', props.name ?? useUid('oc-accordion'))
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<template>
|
|
24
|
-
<div :class="['oc-accordion', 'oc-accordion--accent', flush && 'oc-accordion--flush', accent && `oc-accent--${accent}`, tone && `oc-accordion--${tone}`]">
|
|
25
|
-
<slot />
|
|
26
|
-
</div>
|
|
27
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAccordionAccent.vue | Map: div.oc-accordion.oc-accordion--accent (+ color classes) -> OcAccordionItem children -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Accent accordion: separate item cards with a colored start edge. Put OcAccordionItem children inside;
|
|
5
|
+
* they share one group name (like OcAccordion). Color one item with class "oc-accent--violet" or "oc-accordion-item--warning".
|
|
6
|
+
* Classes: .oc-accordion--accent, .oc-accent--*, .oc-accordion--success/--warning/--danger, .oc-accordion--flush, .oc-accordion-accent-meta
|
|
7
|
+
*/
|
|
8
|
+
import { provide } from 'vue'
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
name?: string
|
|
13
|
+
/** Color preset for all items. */
|
|
14
|
+
accent?: 'blue' | 'cyan' | 'green' | 'orange' | 'red' | 'violet' | 'yellow'
|
|
15
|
+
/** Semantic color for all items. */
|
|
16
|
+
tone?: 'success' | 'warning' | 'danger'
|
|
17
|
+
flush?: boolean
|
|
18
|
+
}>()
|
|
19
|
+
// Same key as OcAccordion, so OcAccordionItem picks up the group name.
|
|
20
|
+
provide('oc-accordion-name', props.name ?? useUid('oc-accordion'))
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div :class="['oc-accordion', 'oc-accordion--accent', flush && 'oc-accordion--flush', accent && `oc-accent--${accent}`, tone && `oc-accordion--${tone}`]">
|
|
25
|
+
<slot />
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/** One accordion section (details/summary). v-model:open; default slot = content, `title` prop or slot = trigger. */
|
|
3
|
-
import { inject } from 'vue'
|
|
4
|
-
|
|
5
|
-
defineProps<{ title?: string }>()
|
|
6
|
-
const open = defineModel<boolean>('open', { default: false })
|
|
7
|
-
const name = inject<string | undefined>('oc-accordion-name', undefined)
|
|
8
|
-
|
|
9
|
-
function onToggle(e: Event) {
|
|
10
|
-
open.value = (e.target as HTMLDetailsElement).open
|
|
11
|
-
}
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<template>
|
|
15
|
-
<details class="oc-accordion-item" :name="name" :open="open" @toggle="onToggle">
|
|
16
|
-
<summary class="oc-accordion-trigger"><slot name="title">{{ title }}</slot></summary>
|
|
17
|
-
<div class="oc-accordion-content"><slot /></div>
|
|
18
|
-
</details>
|
|
19
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** One accordion section (details/summary). v-model:open; default slot = content, `title` prop or slot = trigger. */
|
|
3
|
+
import { inject } from 'vue'
|
|
4
|
+
|
|
5
|
+
defineProps<{ title?: string }>()
|
|
6
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
7
|
+
const name = inject<string | undefined>('oc-accordion-name', undefined)
|
|
8
|
+
|
|
9
|
+
function onToggle(e: Event) {
|
|
10
|
+
open.value = (e.target as HTMLDetailsElement).open
|
|
11
|
+
}
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<details class="oc-accordion-item" :name="name" :open="open" @toggle="onToggle">
|
|
16
|
+
<summary class="oc-accordion-trigger"><slot name="title">{{ title }}</slot></summary>
|
|
17
|
+
<div class="oc-accordion-content"><slot /></div>
|
|
18
|
+
</details>
|
|
19
|
+
</template>
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcAffixInput.vue | Map: props -> variant presets -> template (input group --affix with inline symbol) -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Input with a symbol inside the frame. variant: currency (€), percent (%) or email (@); or own prefix / suffix (props or slots).
|
|
5
|
-
* Same markup as packages/ui/snippets/affix-input.html. v-model: string.
|
|
6
|
-
* class/style stay on the wrapper, other attributes go to the input.
|
|
7
|
-
*/
|
|
8
|
-
import { computed } from 'vue'
|
|
9
|
-
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
10
|
-
|
|
11
|
-
export type AffixInputKind = 'currency' | 'percent' | 'email'
|
|
12
|
-
|
|
13
|
-
defineOptions({ inheritAttrs: false })
|
|
14
|
-
// alignEnd defaults to undefined (not false), so the variant preset can decide.
|
|
15
|
-
const props = withDefaults(defineProps<{ variant?: AffixInputKind; prefix?: string; suffix?: string; alignEnd?: boolean; error?: boolean }>(), {
|
|
16
|
-
alignEnd: undefined,
|
|
17
|
-
})
|
|
18
|
-
const model = defineModel<string>({ default: '' })
|
|
19
|
-
const { root, control } = useAttrsSplit()
|
|
20
|
-
|
|
21
|
-
// Preset per variant. Email uses type="email" so :user-invalid checks the address.
|
|
22
|
-
const PRESETS = {
|
|
23
|
-
currency: { prefix: '\u20AC', suffix: undefined, end: false, input: { type: 'text', inputmode: 'decimal' } },
|
|
24
|
-
percent: { prefix: '%', suffix: undefined, end: false, input: { type: 'text', inputmode: 'decimal' } },
|
|
25
|
-
email: { prefix: '@', suffix: undefined, end: false, input: { type: 'email', autocomplete: 'email' } },
|
|
26
|
-
} as const
|
|
27
|
-
|
|
28
|
-
const preset = computed(() => (props.variant ? PRESETS[props.variant] : undefined))
|
|
29
|
-
const before = computed(() => props.prefix ?? preset.value?.prefix)
|
|
30
|
-
const after = computed(() => props.suffix ?? preset.value?.suffix)
|
|
31
|
-
const end = computed(() => props.alignEnd ?? preset.value?.end ?? false)
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<template>
|
|
35
|
-
<div v-bind="root" :class="['oc-input-group', 'oc-input-group--affix', end && 'oc-input-group--end', error && 'oc-input-group--error']">
|
|
36
|
-
<span v-if="before || $slots.prefix" class="oc-input-addon" aria-hidden="true"><slot name="prefix">{{ before }}</slot></span>
|
|
37
|
-
<input v-bind="{ type: 'text', ...preset?.input, ...control }" v-model="model" class="oc-input" :aria-invalid="error ? 'true' : undefined" />
|
|
38
|
-
<span v-if="after || $slots.suffix" class="oc-input-addon" aria-hidden="true"><slot name="suffix">{{ after }}</slot></span>
|
|
39
|
-
</div>
|
|
40
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAffixInput.vue | Map: props -> variant presets -> template (input group --affix with inline symbol) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Input with a symbol inside the frame. variant: currency (€), percent (%) or email (@); or own prefix / suffix (props or slots).
|
|
5
|
+
* Same markup as packages/ui/snippets/affix-input.html. v-model: string.
|
|
6
|
+
* class/style stay on the wrapper, other attributes go to the input.
|
|
7
|
+
*/
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
10
|
+
|
|
11
|
+
export type AffixInputKind = 'currency' | 'percent' | 'email'
|
|
12
|
+
|
|
13
|
+
defineOptions({ inheritAttrs: false })
|
|
14
|
+
// alignEnd defaults to undefined (not false), so the variant preset can decide.
|
|
15
|
+
const props = withDefaults(defineProps<{ variant?: AffixInputKind; prefix?: string; suffix?: string; alignEnd?: boolean; error?: boolean }>(), {
|
|
16
|
+
alignEnd: undefined,
|
|
17
|
+
})
|
|
18
|
+
const model = defineModel<string>({ default: '' })
|
|
19
|
+
const { root, control } = useAttrsSplit()
|
|
20
|
+
|
|
21
|
+
// Preset per variant. Email uses type="email" so :user-invalid checks the address.
|
|
22
|
+
const PRESETS = {
|
|
23
|
+
currency: { prefix: '\u20AC', suffix: undefined, end: false, input: { type: 'text', inputmode: 'decimal' } },
|
|
24
|
+
percent: { prefix: '%', suffix: undefined, end: false, input: { type: 'text', inputmode: 'decimal' } },
|
|
25
|
+
email: { prefix: '@', suffix: undefined, end: false, input: { type: 'email', autocomplete: 'email' } },
|
|
26
|
+
} as const
|
|
27
|
+
|
|
28
|
+
const preset = computed(() => (props.variant ? PRESETS[props.variant] : undefined))
|
|
29
|
+
const before = computed(() => props.prefix ?? preset.value?.prefix)
|
|
30
|
+
const after = computed(() => props.suffix ?? preset.value?.suffix)
|
|
31
|
+
const end = computed(() => props.alignEnd ?? preset.value?.end ?? false)
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<div v-bind="root" :class="['oc-input-group', 'oc-input-group--affix', end && 'oc-input-group--end', error && 'oc-input-group--error']">
|
|
36
|
+
<span v-if="before || $slots.prefix" class="oc-input-addon" aria-hidden="true"><slot name="prefix">{{ before }}</slot></span>
|
|
37
|
+
<input v-bind="{ type: 'text', ...preset?.input, ...control }" v-model="model" class="oc-input" :aria-invalid="error ? 'true' : undefined" />
|
|
38
|
+
<span v-if="after || $slots.suffix" class="oc-input-addon" aria-hidden="true"><slot name="suffix">{{ after }}</slot></span>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/** Alert. Classes: .oc-alert--info/success/warning/danger. Default slot = description. */
|
|
3
|
-
withDefaults(
|
|
4
|
-
defineProps<{
|
|
5
|
-
variant?: 'info' | 'success' | 'warning' | 'danger'
|
|
6
|
-
title?: string
|
|
7
|
-
}>(),
|
|
8
|
-
{ variant: 'info' },
|
|
9
|
-
)
|
|
10
|
-
</script>
|
|
11
|
-
|
|
12
|
-
<template>
|
|
13
|
-
<div :class="['oc-alert', `oc-alert--${variant}`]" role="status">
|
|
14
|
-
<h4 v-if="title || $slots.title" class="oc-alert-title"><slot name="title">{{ title }}</slot></h4>
|
|
15
|
-
<p v-if="$slots.default" class="oc-alert-description"><slot /></p>
|
|
16
|
-
</div>
|
|
17
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Alert. Classes: .oc-alert--info/success/warning/danger. Default slot = description. */
|
|
3
|
+
withDefaults(
|
|
4
|
+
defineProps<{
|
|
5
|
+
variant?: 'info' | 'success' | 'warning' | 'danger'
|
|
6
|
+
title?: string
|
|
7
|
+
}>(),
|
|
8
|
+
{ variant: 'info' },
|
|
9
|
+
)
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div :class="['oc-alert', `oc-alert--${variant}`]" role="status">
|
|
14
|
+
<h4 v-if="title || $slots.title" class="oc-alert-title"><slot name="title">{{ title }}</slot></h4>
|
|
15
|
+
<p v-if="$slots.default" class="oc-alert-description"><slot /></p>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
@@ -1,66 +1,66 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Confirmation modal with a status icon. v-model:open. Emits `confirm` / `cancel` (both close the dialog).
|
|
4
|
-
* Slots: icon, title, default (message), footer (scoped: { close }).
|
|
5
|
-
* Classes: .oc-alert-dialog(--success/--error/--warning/--info/--question), .oc-alert-dialog-icon
|
|
6
|
-
*/
|
|
7
|
-
import { computed, ref } from 'vue'
|
|
8
|
-
import { useDialog } from '../composables/useDialog'
|
|
9
|
-
import { useUid } from '../composables/useUid'
|
|
10
|
-
|
|
11
|
-
const props = withDefaults(
|
|
12
|
-
defineProps<{
|
|
13
|
-
type?: 'success' | 'error' | 'warning' | 'info' | 'question'
|
|
14
|
-
title?: string
|
|
15
|
-
message?: string
|
|
16
|
-
confirmLabel?: string
|
|
17
|
-
cancelLabel?: string
|
|
18
|
-
/** Variant of the confirm button; defaults to destructive for warning/error. */
|
|
19
|
-
confirmVariant?: 'default' | 'destructive' | 'success'
|
|
20
|
-
}>(),
|
|
21
|
-
{ type: 'warning', confirmLabel: 'Confirm', cancelLabel: 'Cancel' },
|
|
22
|
-
)
|
|
23
|
-
const emit = defineEmits<{ confirm: []; cancel: [] }>()
|
|
24
|
-
const open = defineModel<boolean>('open', { default: false })
|
|
25
|
-
const el = ref<HTMLDialogElement | null>(null)
|
|
26
|
-
const { onClose } = useDialog(open, el)
|
|
27
|
-
const titleId = useUid('oc-alert-dialog-title')
|
|
28
|
-
const textId = `${titleId}-text`
|
|
29
|
-
|
|
30
|
-
const glyphs = { success: '✓', error: '×', warning: '!', info: 'i', question: '?' } as const
|
|
31
|
-
const variant = computed(() => props.confirmVariant ?? (props.type === 'warning' || props.type === 'error' ? 'destructive' : 'default'))
|
|
32
|
-
const close = () => (open.value = false)
|
|
33
|
-
|
|
34
|
-
function confirm() {
|
|
35
|
-
emit('confirm')
|
|
36
|
-
close()
|
|
37
|
-
}
|
|
38
|
-
function cancel() {
|
|
39
|
-
emit('cancel')
|
|
40
|
-
close()
|
|
41
|
-
}
|
|
42
|
-
</script>
|
|
43
|
-
|
|
44
|
-
<template>
|
|
45
|
-
<dialog
|
|
46
|
-
ref="el"
|
|
47
|
-
class="oc-modal"
|
|
48
|
-
closedby="closerequest"
|
|
49
|
-
role="alertdialog"
|
|
50
|
-
:aria-labelledby="titleId"
|
|
51
|
-
:aria-describedby="textId"
|
|
52
|
-
@close="onClose"
|
|
53
|
-
>
|
|
54
|
-
<div :class="['oc-modal-content', 'oc-modal-content--sm', 'oc-alert-dialog', `oc-alert-dialog--${type}`]">
|
|
55
|
-
<span class="oc-alert-dialog-icon" aria-hidden="true"><slot name="icon">{{ glyphs[type] }}</slot></span>
|
|
56
|
-
<h2 :id="titleId" class="oc-modal-title"><slot name="title">{{ title }}</slot></h2>
|
|
57
|
-
<div :id="textId" class="oc-modal-body"><slot>{{ message }}</slot></div>
|
|
58
|
-
<div class="oc-modal-footer">
|
|
59
|
-
<slot name="footer" :close="close" :confirm="confirm" :cancel="cancel">
|
|
60
|
-
<button class="oc-btn oc-btn--outline" type="button" @click="cancel">{{ cancelLabel }}</button>
|
|
61
|
-
<button :class="['oc-btn', variant !== 'default' && `oc-btn--${variant}`]" type="button" @click="confirm">{{ confirmLabel }}</button>
|
|
62
|
-
</slot>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</dialog>
|
|
66
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Confirmation modal with a status icon. v-model:open. Emits `confirm` / `cancel` (both close the dialog).
|
|
4
|
+
* Slots: icon, title, default (message), footer (scoped: { close }).
|
|
5
|
+
* Classes: .oc-alert-dialog(--success/--error/--warning/--info/--question), .oc-alert-dialog-icon
|
|
6
|
+
*/
|
|
7
|
+
import { computed, ref } from 'vue'
|
|
8
|
+
import { useDialog } from '../composables/useDialog'
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<{
|
|
13
|
+
type?: 'success' | 'error' | 'warning' | 'info' | 'question'
|
|
14
|
+
title?: string
|
|
15
|
+
message?: string
|
|
16
|
+
confirmLabel?: string
|
|
17
|
+
cancelLabel?: string
|
|
18
|
+
/** Variant of the confirm button; defaults to destructive for warning/error. */
|
|
19
|
+
confirmVariant?: 'default' | 'destructive' | 'success'
|
|
20
|
+
}>(),
|
|
21
|
+
{ type: 'warning', confirmLabel: 'Confirm', cancelLabel: 'Cancel' },
|
|
22
|
+
)
|
|
23
|
+
const emit = defineEmits<{ confirm: []; cancel: [] }>()
|
|
24
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
25
|
+
const el = ref<HTMLDialogElement | null>(null)
|
|
26
|
+
const { onClose } = useDialog(open, el)
|
|
27
|
+
const titleId = useUid('oc-alert-dialog-title')
|
|
28
|
+
const textId = `${titleId}-text`
|
|
29
|
+
|
|
30
|
+
const glyphs = { success: '✓', error: '×', warning: '!', info: 'i', question: '?' } as const
|
|
31
|
+
const variant = computed(() => props.confirmVariant ?? (props.type === 'warning' || props.type === 'error' ? 'destructive' : 'default'))
|
|
32
|
+
const close = () => (open.value = false)
|
|
33
|
+
|
|
34
|
+
function confirm() {
|
|
35
|
+
emit('confirm')
|
|
36
|
+
close()
|
|
37
|
+
}
|
|
38
|
+
function cancel() {
|
|
39
|
+
emit('cancel')
|
|
40
|
+
close()
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<dialog
|
|
46
|
+
ref="el"
|
|
47
|
+
class="oc-modal"
|
|
48
|
+
closedby="closerequest"
|
|
49
|
+
role="alertdialog"
|
|
50
|
+
:aria-labelledby="titleId"
|
|
51
|
+
:aria-describedby="textId"
|
|
52
|
+
@close="onClose"
|
|
53
|
+
>
|
|
54
|
+
<div :class="['oc-modal-content', 'oc-modal-content--sm', 'oc-alert-dialog', `oc-alert-dialog--${type}`]">
|
|
55
|
+
<span class="oc-alert-dialog-icon" aria-hidden="true"><slot name="icon">{{ glyphs[type] }}</slot></span>
|
|
56
|
+
<h2 :id="titleId" class="oc-modal-title"><slot name="title">{{ title }}</slot></h2>
|
|
57
|
+
<div :id="textId" class="oc-modal-body"><slot>{{ message }}</slot></div>
|
|
58
|
+
<div class="oc-modal-footer">
|
|
59
|
+
<slot name="footer" :close="close" :confirm="confirm" :cancel="cancel">
|
|
60
|
+
<button class="oc-btn oc-btn--outline" type="button" @click="cancel">{{ cancelLabel }}</button>
|
|
61
|
+
<button :class="['oc-btn', variant !== 'default' && `oc-btn--${variant}`]" type="button" @click="confirm">{{ confirmLabel }}</button>
|
|
62
|
+
</slot>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</dialog>
|
|
66
|
+
</template>
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcAlphaTabs.vue | Map: nav -> "All" item -> one item per letter (current / disabled) -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* All + A-Z letter filter. v-model = current letter ("all" by default).
|
|
5
|
-
* With `href` (e.g. "?letter={letter}") the items are links; otherwise buttons that update v-model.
|
|
6
|
-
* Classes: nav.oc-alpha-tabs(--wrap/--sm/--plain), .oc-alpha-tabs-item(--all), aria-current, aria-disabled
|
|
7
|
-
*/
|
|
8
|
-
import { computed } from 'vue'
|
|
9
|
-
|
|
10
|
-
const props = withDefaults(
|
|
11
|
-
defineProps<{
|
|
12
|
-
letters?: string[]
|
|
13
|
-
/** Letters with entries; the others are disabled. Default: all. */
|
|
14
|
-
available?: string[]
|
|
15
|
-
/** Link pattern; "{letter}" is replaced, e.g. "?letter={letter}". */
|
|
16
|
-
href?: string
|
|
17
|
-
allLabel?: string
|
|
18
|
-
wrap?: boolean
|
|
19
|
-
size?: 'sm'
|
|
20
|
-
plain?: boolean
|
|
21
|
-
}>(),
|
|
22
|
-
{ letters: () => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''), allLabel: 'All' },
|
|
23
|
-
)
|
|
24
|
-
const current = defineModel<string>({ default: 'all' })
|
|
25
|
-
|
|
26
|
-
const items = computed(() => [
|
|
27
|
-
{ key: 'all', label: props.allLabel, all: true },
|
|
28
|
-
...props.letters.map((l) => ({ key: l, label: l, all: false })),
|
|
29
|
-
])
|
|
30
|
-
const isDisabled = (key: string) => key !== 'all' && props.available !== undefined && !props.available.includes(key)
|
|
31
|
-
const link = (key: string) => props.href?.replace('{letter}', key)
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<template>
|
|
35
|
-
<nav :class="['oc-alpha-tabs', wrap && 'oc-alpha-tabs--wrap', size && `oc-alpha-tabs--${size}`, plain && 'oc-alpha-tabs--plain']" aria-label="Filter by letter">
|
|
36
|
-
<template v-for="item in items" :key="item.key">
|
|
37
|
-
<a
|
|
38
|
-
v-if="href"
|
|
39
|
-
:class="['oc-alpha-tabs-item', item.all && 'oc-alpha-tabs-item--all']"
|
|
40
|
-
:href="isDisabled(item.key) ? undefined : link(item.key)"
|
|
41
|
-
:aria-current="current === item.key ? 'true' : undefined"
|
|
42
|
-
:aria-disabled="isDisabled(item.key) ? 'true' : undefined"
|
|
43
|
-
>{{ item.label }}</a>
|
|
44
|
-
<button
|
|
45
|
-
v-else
|
|
46
|
-
type="button"
|
|
47
|
-
:class="['oc-alpha-tabs-item', item.all && 'oc-alpha-tabs-item--all']"
|
|
48
|
-
:aria-current="current === item.key ? 'true' : undefined"
|
|
49
|
-
:disabled="isDisabled(item.key)"
|
|
50
|
-
@click="current = item.key"
|
|
51
|
-
>{{ item.label }}</button>
|
|
52
|
-
</template>
|
|
53
|
-
</nav>
|
|
54
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAlphaTabs.vue | Map: nav -> "All" item -> one item per letter (current / disabled) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* All + A-Z letter filter. v-model = current letter ("all" by default).
|
|
5
|
+
* With `href` (e.g. "?letter={letter}") the items are links; otherwise buttons that update v-model.
|
|
6
|
+
* Classes: nav.oc-alpha-tabs(--wrap/--sm/--plain), .oc-alpha-tabs-item(--all), aria-current, aria-disabled
|
|
7
|
+
*/
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
letters?: string[]
|
|
13
|
+
/** Letters with entries; the others are disabled. Default: all. */
|
|
14
|
+
available?: string[]
|
|
15
|
+
/** Link pattern; "{letter}" is replaced, e.g. "?letter={letter}". */
|
|
16
|
+
href?: string
|
|
17
|
+
allLabel?: string
|
|
18
|
+
wrap?: boolean
|
|
19
|
+
size?: 'sm'
|
|
20
|
+
plain?: boolean
|
|
21
|
+
}>(),
|
|
22
|
+
{ letters: () => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''), allLabel: 'All' },
|
|
23
|
+
)
|
|
24
|
+
const current = defineModel<string>({ default: 'all' })
|
|
25
|
+
|
|
26
|
+
const items = computed(() => [
|
|
27
|
+
{ key: 'all', label: props.allLabel, all: true },
|
|
28
|
+
...props.letters.map((l) => ({ key: l, label: l, all: false })),
|
|
29
|
+
])
|
|
30
|
+
const isDisabled = (key: string) => key !== 'all' && props.available !== undefined && !props.available.includes(key)
|
|
31
|
+
const link = (key: string) => props.href?.replace('{letter}', key)
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<nav :class="['oc-alpha-tabs', wrap && 'oc-alpha-tabs--wrap', size && `oc-alpha-tabs--${size}`, plain && 'oc-alpha-tabs--plain']" aria-label="Filter by letter">
|
|
36
|
+
<template v-for="item in items" :key="item.key">
|
|
37
|
+
<a
|
|
38
|
+
v-if="href"
|
|
39
|
+
:class="['oc-alpha-tabs-item', item.all && 'oc-alpha-tabs-item--all']"
|
|
40
|
+
:href="isDisabled(item.key) ? undefined : link(item.key)"
|
|
41
|
+
:aria-current="current === item.key ? 'true' : undefined"
|
|
42
|
+
:aria-disabled="isDisabled(item.key) ? 'true' : undefined"
|
|
43
|
+
>{{ item.label }}</a>
|
|
44
|
+
<button
|
|
45
|
+
v-else
|
|
46
|
+
type="button"
|
|
47
|
+
:class="['oc-alpha-tabs-item', item.all && 'oc-alpha-tabs-item--all']"
|
|
48
|
+
:aria-current="current === item.key ? 'true' : undefined"
|
|
49
|
+
:disabled="isDisabled(item.key)"
|
|
50
|
+
@click="current = item.key"
|
|
51
|
+
>{{ item.label }}</button>
|
|
52
|
+
</template>
|
|
53
|
+
</nav>
|
|
54
|
+
</template>
|