@birdycom/omniaui-vue 0.0.0-stage → 0.11.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/LICENSE +21 -0
- package/README.md +48 -2
- package/dist/blocks/OcBlockAboutTimeline.vue.d.ts +29 -0
- package/dist/blocks/OcBlockAppHeader.vue.d.ts +33 -0
- package/dist/blocks/OcBlockAppShell.vue.d.ts +33 -0
- package/dist/blocks/OcBlockAuth.vue.d.ts +28 -0
- package/dist/blocks/OcBlockAuthSplit.vue.d.ts +45 -0
- package/dist/blocks/OcBlockContactGrid.vue.d.ts +32 -0
- package/dist/blocks/OcBlockCta.vue.d.ts +17 -0
- package/dist/blocks/OcBlockDataTable.vue.d.ts +33 -0
- package/dist/blocks/OcBlockFaq.vue.d.ts +32 -0
- package/dist/blocks/OcBlockFeatures.vue.d.ts +25 -0
- package/dist/blocks/OcBlockFooter.vue.d.ts +27 -0
- package/dist/blocks/OcBlockFormPage.vue.d.ts +36 -0
- package/dist/blocks/OcBlockHero.vue.d.ts +18 -0
- package/dist/blocks/OcBlockHeroCode.vue.d.ts +32 -0
- package/dist/blocks/OcBlockHeroOrbit.vue.d.ts +36 -0
- package/dist/blocks/OcBlockHeroSplit.vue.d.ts +19 -0
- package/dist/blocks/OcBlockModuleHeader.vue.d.ts +24 -0
- package/dist/blocks/OcBlockPageHero.vue.d.ts +18 -0
- package/dist/blocks/OcBlockPortfolioGrid.vue.d.ts +34 -0
- package/dist/blocks/OcBlockPricing.vue.d.ts +38 -0
- package/dist/blocks/OcBlockProcess.vue.d.ts +18 -0
- package/dist/blocks/OcBlockProductGrid.vue.d.ts +25 -0
- package/dist/blocks/OcBlockProfileHeader.vue.d.ts +36 -0
- package/dist/blocks/OcBlockShowcase.vue.d.ts +23 -0
- package/dist/blocks/OcBlockSiteFooter.vue.d.ts +42 -0
- package/dist/blocks/OcBlockSiteHeader.vue.d.ts +32 -0
- package/dist/blocks/OcBlockSpotlight.vue.d.ts +32 -0
- package/dist/blocks/OcBlockStats.vue.d.ts +10 -0
- package/dist/blocks/OcBlockStatsCounter.vue.d.ts +10 -0
- package/dist/blocks/OcBlockTeamList.vue.d.ts +42 -0
- package/dist/blocks/OcBlockTwoFactor.vue.d.ts +38 -0
- package/dist/blocks/OcBlockWizardDialog.vue.d.ts +52 -0
- package/dist/components/OcAccordion.vue.d.ts +15 -0
- package/dist/components/OcAccordionAccent.vue.d.ts +20 -0
- package/dist/components/OcAccordionItem.vue.d.ts +24 -0
- package/dist/components/OcAffixInput.vue.d.ts +31 -0
- package/dist/components/OcAlert.vue.d.ts +21 -0
- package/dist/components/OcAlertDialog.vue.d.ts +48 -0
- package/dist/components/OcAlphaTabs.vue.d.ts +23 -0
- package/dist/components/OcAspectRatio.vue.d.ts +16 -0
- package/dist/components/OcAttachment.vue.d.ts +26 -0
- package/dist/components/OcAttachmentImage.vue.d.ts +10 -0
- package/dist/components/OcAttachmentList.vue.d.ts +31 -0
- package/dist/components/OcAttachmentThumb.vue.d.ts +16 -0
- package/dist/components/OcAvatar.vue.d.ts +21 -0
- package/dist/components/OcAvatarGroup.vue.d.ts +12 -0
- package/dist/components/OcBackLink.vue.d.ts +18 -0
- package/dist/components/OcBadge.vue.d.ts +18 -0
- package/dist/components/OcBarcodeInput.vue.d.ts +27 -0
- package/dist/components/OcBreadcrumb.vue.d.ts +24 -0
- package/dist/components/OcBrowserFrame.vue.d.ts +35 -0
- package/dist/components/OcBusyOverlay.vue.d.ts +30 -0
- package/dist/components/OcButton.vue.d.ts +31 -0
- package/dist/components/OcButtonGroup.vue.d.ts +17 -0
- package/dist/components/OcButtonGroupText.vue.d.ts +12 -0
- package/dist/components/OcCard.vue.d.ts +27 -0
- package/dist/components/OcChartLine.vue.d.ts +28 -0
- package/dist/components/OcChatComposer.vue.d.ts +33 -0
- package/dist/components/OcCheckbox.vue.d.ts +26 -0
- package/dist/components/OcChecklist.vue.d.ts +32 -0
- package/dist/components/OcChipFilter.vue.d.ts +24 -0
- package/dist/components/OcCollapsible.vue.d.ts +28 -0
- package/dist/components/OcColorField.vue.d.ts +17 -0
- package/dist/components/OcColorSelect.vue.d.ts +24 -0
- package/dist/components/OcColumnMenu.vue.d.ts +31 -0
- package/dist/components/OcCommand.vue.d.ts +28 -0
- package/dist/components/OcConfirmDialog.vue.d.ts +44 -0
- package/dist/components/OcCookieConsent.vue.d.ts +52 -0
- package/dist/components/OcCopyField.vue.d.ts +14 -0
- package/dist/components/OcCounter.vue.d.ts +9 -0
- package/dist/components/OcDataTable.vue.d.ts +37 -0
- package/dist/components/OcDecimalInput.vue.d.ts +19 -0
- package/dist/components/OcDefinitionList.vue.d.ts +34 -0
- package/dist/components/OcDocViewer.vue.d.ts +24 -0
- package/dist/components/OcDropdown.vue.d.ts +19 -0
- package/dist/components/OcDropdownItem.vue.d.ts +18 -0
- package/dist/components/OcDropdownLabel.vue.d.ts +12 -0
- package/dist/components/OcDropdownSeparator.vue.d.ts +2 -0
- package/dist/components/OcDropzone.vue.d.ts +29 -0
- package/dist/components/OcEmojiPicker.vue.d.ts +32 -0
- package/dist/components/OcEmptyState.vue.d.ts +24 -0
- package/dist/components/OcEntityCard.vue.d.ts +37 -0
- package/dist/components/OcFactList.vue.d.ts +29 -0
- package/dist/components/OcFilterCounts.vue.d.ts +25 -0
- package/dist/components/OcFlipCard.vue.d.ts +31 -0
- package/dist/components/OcFormField.vue.d.ts +26 -0
- package/dist/components/OcGreeting.vue.d.ts +26 -0
- package/dist/components/OcHeading.vue.d.ts +24 -0
- package/dist/components/OcHoverCard.vue.d.ts +22 -0
- package/dist/components/OcImageZoom.vue.d.ts +28 -0
- package/dist/components/OcInput.vue.d.ts +13 -0
- package/dist/components/OcInputGroup.vue.d.ts +21 -0
- package/dist/components/OcItem.vue.d.ts +39 -0
- package/dist/components/OcItemGroup.vue.d.ts +12 -0
- package/dist/components/OcItemSeparator.vue.d.ts +2 -0
- package/dist/components/OcKbd.vue.d.ts +12 -0
- package/dist/components/OcKbdGroup.vue.d.ts +17 -0
- package/dist/components/OcLabel.vue.d.ts +19 -0
- package/dist/components/OcLanguageSelect.vue.d.ts +32 -0
- package/dist/components/OcLauncherCard.vue.d.ts +26 -0
- package/dist/components/OcLightbox.vue.d.ts +45 -0
- package/dist/components/OcLinkArrow.vue.d.ts +23 -0
- package/dist/components/OcListToolbar.vue.d.ts +44 -0
- package/dist/components/OcMarquee.vue.d.ts +16 -0
- package/dist/components/OcMediaCard.vue.d.ts +24 -0
- package/dist/components/OcMediaRow.vue.d.ts +21 -0
- package/dist/components/OcMediaThumb.vue.d.ts +32 -0
- package/dist/components/OcMessage.vue.d.ts +63 -0
- package/dist/components/OcMessageList.vue.d.ts +35 -0
- package/dist/components/OcMessageThread.vue.d.ts +80 -0
- package/dist/components/OcModal.vue.d.ts +36 -0
- package/dist/components/OcNavmenu.vue.d.ts +24 -0
- package/dist/components/OcNavmenuList.vue.d.ts +29 -0
- package/dist/components/OcNextTeaser.vue.d.ts +29 -0
- package/dist/components/OcNumberInput.vue.d.ts +25 -0
- package/dist/components/OcOtpInput.vue.d.ts +19 -0
- package/dist/components/OcPageHeader.vue.d.ts +36 -0
- package/dist/components/OcPagination.vue.d.ts +20 -0
- package/dist/components/OcPasswordInput.vue.d.ts +18 -0
- package/dist/components/OcPasswordRequirements.vue.d.ts +19 -0
- package/dist/components/OcPhoneInput.vue.d.ts +25 -0
- package/dist/components/OcPopover.vue.d.ts +24 -0
- package/dist/components/OcPriceCard.vue.d.ts +41 -0
- package/dist/components/OcProductMark.vue.d.ts +25 -0
- package/dist/components/OcProgress.vue.d.ts +11 -0
- package/dist/components/OcProjectCard.vue.d.ts +35 -0
- package/dist/components/OcProse.vue.d.ts +25 -0
- package/dist/components/OcRadio.vue.d.ts +27 -0
- package/dist/components/OcRadioGroup.vue.d.ts +32 -0
- package/dist/components/OcRecoveryCodes.vue.d.ts +36 -0
- package/dist/components/OcScrollArea.vue.d.ts +18 -0
- package/dist/components/OcSegmented.vue.d.ts +21 -0
- package/dist/components/OcSelect.vue.d.ts +28 -0
- package/dist/components/OcSeparator.vue.d.ts +21 -0
- package/dist/components/OcSeverityCard.vue.d.ts +36 -0
- package/dist/components/OcSheet.vue.d.ts +37 -0
- package/dist/components/OcSidebar.vue.d.ts +28 -0
- package/dist/components/OcSidebarCollapsible.vue.d.ts +27 -0
- package/dist/components/OcSidebarGroup.vue.d.ts +16 -0
- package/dist/components/OcSidebarMenu.vue.d.ts +12 -0
- package/dist/components/OcSidebarMenuButton.vue.d.ts +21 -0
- package/dist/components/OcSidebarSeparator.vue.d.ts +2 -0
- package/dist/components/OcSkeleton.vue.d.ts +8 -0
- package/dist/components/OcSlider.vue.d.ts +18 -0
- package/dist/components/OcSortableRow.vue.d.ts +32 -0
- package/dist/components/OcSpinner.vue.d.ts +8 -0
- package/dist/components/OcStackCard.vue.d.ts +22 -0
- package/dist/components/OcStackList.vue.d.ts +28 -0
- package/dist/components/OcStatCard.vue.d.ts +25 -0
- package/dist/components/OcStepper.vue.d.ts +24 -0
- package/dist/components/OcSwitch.vue.d.ts +9 -0
- package/dist/components/OcTable.vue.d.ts +61 -0
- package/dist/components/OcTableActions.vue.d.ts +18 -0
- package/dist/components/OcTableCompact.vue.d.ts +37 -0
- package/dist/components/OcTabs.vue.d.ts +28 -0
- package/dist/components/OcTagInput.vue.d.ts +18 -0
- package/dist/components/OcTerminalLog.vue.d.ts +20 -0
- package/dist/components/OcTextarea.vue.d.ts +13 -0
- package/dist/components/OcThemeSwitch.vue.d.ts +20 -0
- package/dist/components/OcTimeInput.vue.d.ts +14 -0
- package/dist/components/OcTimeline.vue.d.ts +33 -0
- package/dist/components/OcToast.vue.d.ts +31 -0
- package/dist/components/OcToastContainer.vue.d.ts +8 -0
- package/dist/components/OcToggle.vue.d.ts +29 -0
- package/dist/components/OcToggleField.vue.d.ts +27 -0
- package/dist/components/OcToggleGroup.vue.d.ts +27 -0
- package/dist/components/OcTooltip.vue.d.ts +16 -0
- package/dist/components/OcTopProgress.vue.d.ts +18 -0
- package/dist/components/OcTree.vue.d.ts +40 -0
- package/dist/components/OcUpgradeBanner.vue.d.ts +41 -0
- package/dist/components/OcUser.vue.d.ts +20 -0
- package/dist/components/OcVersionBanner.vue.d.ts +32 -0
- package/dist/composables/chartLine.d.ts +6 -0
- package/dist/composables/keys.d.ts +19 -0
- package/dist/composables/useAttrsSplit.d.ts +14 -0
- package/dist/composables/useDialog.d.ts +9 -0
- package/dist/composables/useToast.d.ts +34 -0
- package/dist/composables/useUid.d.ts +5 -0
- package/dist/index.d.ts +233 -0
- package/dist/omniaui-vue.js +8811 -0
- package/dist/omniaui-vue.js.map +1 -0
- package/manifest.json +1036 -0
- package/package.json +67 -4
- package/src/blocks/OcBlockAboutTimeline.vue +44 -0
- package/src/blocks/OcBlockAppHeader.vue +36 -0
- package/src/blocks/OcBlockAppShell.vue +29 -0
- package/src/blocks/OcBlockAuth.vue +24 -0
- package/src/blocks/OcBlockAuthSplit.vue +47 -0
- package/src/blocks/OcBlockContactGrid.vue +53 -0
- package/src/blocks/OcBlockCta.vue +12 -0
- package/src/blocks/OcBlockDataTable.vue +27 -0
- package/src/blocks/OcBlockFaq.vue +49 -0
- package/src/blocks/OcBlockFeatures.vue +23 -0
- package/src/blocks/OcBlockFooter.vue +25 -0
- package/src/blocks/OcBlockFormPage.vue +24 -0
- package/src/blocks/OcBlockHero.vue +13 -0
- package/src/blocks/OcBlockHeroCode.vue +42 -0
- package/src/blocks/OcBlockHeroOrbit.vue +69 -0
- package/src/blocks/OcBlockHeroSplit.vue +15 -0
- package/src/blocks/OcBlockModuleHeader.vue +21 -0
- package/src/blocks/OcBlockPageHero.vue +13 -0
- package/src/blocks/OcBlockPortfolioGrid.vue +63 -0
- package/src/blocks/OcBlockPricing.vue +39 -0
- package/src/blocks/OcBlockProcess.vue +39 -0
- package/src/blocks/OcBlockProductGrid.vue +41 -0
- package/src/blocks/OcBlockProfileHeader.vue +50 -0
- package/src/blocks/OcBlockShowcase.vue +43 -0
- package/src/blocks/OcBlockSiteFooter.vue +54 -0
- package/src/blocks/OcBlockSiteHeader.vue +34 -0
- package/src/blocks/OcBlockSpotlight.vue +47 -0
- package/src/blocks/OcBlockStats.vue +17 -0
- package/src/blocks/OcBlockStatsCounter.vue +20 -0
- package/src/blocks/OcBlockTeamList.vue +53 -0
- package/src/blocks/OcBlockTwoFactor.vue +61 -0
- package/src/blocks/OcBlockWizardDialog.vue +67 -0
- package/src/components/OcAccordion.vue +12 -0
- package/src/components/OcAccordionAccent.vue +27 -0
- package/src/components/OcAccordionItem.vue +19 -0
- package/src/components/OcAffixInput.vue +40 -0
- package/src/components/OcAlert.vue +17 -0
- package/src/components/OcAlertDialog.vue +66 -0
- package/src/components/OcAlphaTabs.vue +54 -0
- package/src/components/OcAspectRatio.vue +21 -0
- package/src/components/OcAttachment.vue +59 -0
- package/src/components/OcAttachmentImage.vue +13 -0
- package/src/components/OcAttachmentList.vue +31 -0
- package/src/components/OcAttachmentThumb.vue +14 -0
- package/src/components/OcAvatar.vue +18 -0
- package/src/components/OcAvatarGroup.vue +7 -0
- package/src/components/OcBackLink.vue +8 -0
- package/src/components/OcBadge.vue +13 -0
- package/src/components/OcBarcodeInput.vue +51 -0
- package/src/components/OcBreadcrumb.vue +22 -0
- package/src/components/OcBrowserFrame.vue +33 -0
- package/src/components/OcBusyOverlay.vue +32 -0
- package/src/components/OcButton.vue +47 -0
- package/src/components/OcButtonGroup.vue +10 -0
- package/src/components/OcButtonGroupText.vue +7 -0
- package/src/components/OcCard.vue +20 -0
- package/src/components/OcChartLine.vue +80 -0
- package/src/components/OcChatComposer.vue +42 -0
- package/src/components/OcCheckbox.vue +26 -0
- package/src/components/OcChecklist.vue +29 -0
- package/src/components/OcChipFilter.vue +48 -0
- package/src/components/OcCollapsible.vue +19 -0
- package/src/components/OcColorField.vue +15 -0
- package/src/components/OcColorSelect.vue +33 -0
- package/src/components/OcColumnMenu.vue +50 -0
- package/src/components/OcCommand.vue +119 -0
- package/src/components/OcConfirmDialog.vue +65 -0
- package/src/components/OcCookieConsent.vue +119 -0
- package/src/components/OcCopyField.vue +37 -0
- package/src/components/OcCounter.vue +15 -0
- package/src/components/OcDataTable.vue +144 -0
- package/src/components/OcDecimalInput.vue +53 -0
- package/src/components/OcDefinitionList.vue +35 -0
- package/src/components/OcDocViewer.vue +79 -0
- package/src/components/OcDropdown.vue +23 -0
- package/src/components/OcDropdownItem.vue +9 -0
- package/src/components/OcDropdownLabel.vue +7 -0
- package/src/components/OcDropdownSeparator.vue +7 -0
- package/src/components/OcDropzone.vue +36 -0
- package/src/components/OcEmojiPicker.vue +45 -0
- package/src/components/OcEmptyState.vue +13 -0
- package/src/components/OcEntityCard.vue +47 -0
- package/src/components/OcFactList.vue +37 -0
- package/src/components/OcFilterCounts.vue +38 -0
- package/src/components/OcFlipCard.vue +36 -0
- package/src/components/OcFormField.vue +36 -0
- package/src/components/OcGreeting.vue +52 -0
- package/src/components/OcHeading.vue +19 -0
- package/src/components/OcHoverCard.vue +25 -0
- package/src/components/OcImageZoom.vue +39 -0
- package/src/components/OcInput.vue +9 -0
- package/src/components/OcInputGroup.vue +12 -0
- package/src/components/OcItem.vue +40 -0
- package/src/components/OcItemGroup.vue +7 -0
- package/src/components/OcItemSeparator.vue +7 -0
- package/src/components/OcKbd.vue +7 -0
- package/src/components/OcKbdGroup.vue +15 -0
- package/src/components/OcLabel.vue +10 -0
- package/src/components/OcLanguageSelect.vue +40 -0
- package/src/components/OcLauncherCard.vue +34 -0
- package/src/components/OcLightbox.vue +63 -0
- package/src/components/OcLinkArrow.vue +25 -0
- package/src/components/OcListToolbar.vue +44 -0
- package/src/components/OcMarquee.vue +36 -0
- package/src/components/OcMediaCard.vue +28 -0
- package/src/components/OcMediaRow.vue +28 -0
- package/src/components/OcMediaThumb.vue +38 -0
- package/src/components/OcMessage.vue +91 -0
- package/src/components/OcMessageList.vue +43 -0
- package/src/components/OcMessageThread.vue +155 -0
- package/src/components/OcModal.vue +42 -0
- package/src/components/OcNavmenu.vue +18 -0
- package/src/components/OcNavmenuList.vue +38 -0
- package/src/components/OcNextTeaser.vue +25 -0
- package/src/components/OcNumberInput.vue +47 -0
- package/src/components/OcOtpInput.vue +34 -0
- package/src/components/OcPageHeader.vue +34 -0
- package/src/components/OcPagination.vue +33 -0
- package/src/components/OcPasswordInput.vue +27 -0
- package/src/components/OcPasswordRequirements.vue +28 -0
- package/src/components/OcPhoneInput.vue +72 -0
- package/src/components/OcPopover.vue +25 -0
- package/src/components/OcPriceCard.vue +69 -0
- package/src/components/OcProductMark.vue +31 -0
- package/src/components/OcProgress.vue +21 -0
- package/src/components/OcProjectCard.vue +36 -0
- package/src/components/OcProse.vue +57 -0
- package/src/components/OcRadio.vue +40 -0
- package/src/components/OcRadioGroup.vue +32 -0
- package/src/components/OcRecoveryCodes.vue +55 -0
- package/src/components/OcScrollArea.vue +15 -0
- package/src/components/OcSegmented.vue +28 -0
- package/src/components/OcSelect.vue +21 -0
- package/src/components/OcSeparator.vue +18 -0
- package/src/components/OcSeverityCard.vue +46 -0
- package/src/components/OcSheet.vue +49 -0
- package/src/components/OcSidebar.vue +16 -0
- package/src/components/OcSidebarCollapsible.vue +19 -0
- package/src/components/OcSidebarGroup.vue +11 -0
- package/src/components/OcSidebarMenu.vue +7 -0
- package/src/components/OcSidebarMenuButton.vue +31 -0
- package/src/components/OcSidebarSeparator.vue +7 -0
- package/src/components/OcSkeleton.vue +8 -0
- package/src/components/OcSlider.vue +27 -0
- package/src/components/OcSortableRow.vue +40 -0
- package/src/components/OcSpinner.vue +13 -0
- package/src/components/OcStackCard.vue +24 -0
- package/src/components/OcStackList.vue +31 -0
- package/src/components/OcStatCard.vue +39 -0
- package/src/components/OcStepper.vue +34 -0
- package/src/components/OcSwitch.vue +15 -0
- package/src/components/OcTable.vue +115 -0
- package/src/components/OcTableActions.vue +26 -0
- package/src/components/OcTableCompact.vue +46 -0
- package/src/components/OcTabs.vue +37 -0
- package/src/components/OcTagInput.vue +50 -0
- package/src/components/OcTerminalLog.vue +50 -0
- package/src/components/OcTextarea.vue +9 -0
- package/src/components/OcThemeSwitch.vue +31 -0
- package/src/components/OcTimeInput.vue +50 -0
- package/src/components/OcTimeline.vue +36 -0
- package/src/components/OcToast.vue +31 -0
- package/src/components/OcToastContainer.vue +21 -0
- package/src/components/OcToggle.vue +63 -0
- package/src/components/OcToggleField.vue +28 -0
- package/src/components/OcToggleGroup.vue +40 -0
- package/src/components/OcTooltip.vue +8 -0
- package/src/components/OcTopProgress.vue +54 -0
- package/src/components/OcTree.vue +70 -0
- package/src/components/OcUpgradeBanner.vue +42 -0
- package/src/components/OcUser.vue +25 -0
- package/src/components/OcVersionBanner.vue +34 -0
- package/src/composables/chartLine.ts +28 -0
- package/src/composables/keys.ts +22 -0
- package/src/composables/useAttrsSplit.ts +16 -0
- package/src/composables/useDialog.ts +21 -0
- package/src/composables/useToast.ts +66 -0
- package/src/composables/useUid.ts +12 -0
- package/src/index.ts +589 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcEntityCard.vue | Map: props -> article -> grid (frame | body: eyebrow, title, description, facts) -> footer -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Framed media cell + detail column (same markup as snippets/entity-card.html).
|
|
5
|
+
* Props: title, eyebrow, description, src / alt or glyph (frame content), contain, facts ([key, value][]), stacked, size (sm|md).
|
|
6
|
+
* Slots: media (frame content), default (extra body content), footer. Classes: .oc-entity-card(--stacked/--sm), __grid,
|
|
7
|
+
* __frame(--contain), __glyph, __body, __eyebrow, __title, __description, __facts, __footer
|
|
8
|
+
*/
|
|
9
|
+
withDefaults(
|
|
10
|
+
defineProps<{
|
|
11
|
+
title: string
|
|
12
|
+
eyebrow?: string
|
|
13
|
+
description?: string
|
|
14
|
+
src?: string
|
|
15
|
+
alt?: string
|
|
16
|
+
glyph?: string
|
|
17
|
+
contain?: boolean
|
|
18
|
+
facts?: [string, string][]
|
|
19
|
+
stacked?: boolean
|
|
20
|
+
size?: 'sm' | 'md'
|
|
21
|
+
}>(),
|
|
22
|
+
{ alt: '', size: 'md' },
|
|
23
|
+
)
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<article :class="['oc-entity-card', stacked && 'oc-entity-card--stacked', size === 'sm' && 'oc-entity-card--sm']">
|
|
28
|
+
<div class="oc-entity-card__grid">
|
|
29
|
+
<div :class="['oc-entity-card__frame', contain && 'oc-entity-card__frame--contain']">
|
|
30
|
+
<slot name="media">
|
|
31
|
+
<img v-if="src" :src="src" :alt="alt" />
|
|
32
|
+
<span v-else-if="glyph" class="oc-entity-card__glyph" aria-hidden="true">{{ glyph }}</span>
|
|
33
|
+
</slot>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="oc-entity-card__body">
|
|
36
|
+
<p v-if="eyebrow" class="oc-entity-card__eyebrow">{{ eyebrow }}</p>
|
|
37
|
+
<h3 class="oc-entity-card__title">{{ title }}</h3>
|
|
38
|
+
<p v-if="description" class="oc-entity-card__description">{{ description }}</p>
|
|
39
|
+
<dl v-if="facts?.length" class="oc-entity-card__facts">
|
|
40
|
+
<template v-for="([k, v], i) in facts" :key="i"><dt>{{ k }}</dt><dd>{{ v }}</dd></template>
|
|
41
|
+
</dl>
|
|
42
|
+
<slot />
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
<div v-if="$slots.footer" class="oc-entity-card__footer"><slot name="footer" /></div>
|
|
46
|
+
</article>
|
|
47
|
+
</template>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcFactList.vue | Map: props (items + modifiers) -> dl.oc-fact-list -> div > dt + dd -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Key / value product facts (variant of the definition list). Pass `items`, or your own <div><dt><dd> rows in the slot.
|
|
5
|
+
* Classes: .oc-fact-list (--plain, --compact, --stacked, --accent)
|
|
6
|
+
*/
|
|
7
|
+
export interface FactListRow {
|
|
8
|
+
label: string
|
|
9
|
+
value: string
|
|
10
|
+
}
|
|
11
|
+
withDefaults(
|
|
12
|
+
defineProps<{
|
|
13
|
+
items?: FactListRow[]
|
|
14
|
+
plain?: boolean
|
|
15
|
+
compact?: boolean
|
|
16
|
+
stacked?: boolean
|
|
17
|
+
accent?: boolean
|
|
18
|
+
}>(),
|
|
19
|
+
{ items: () => [] },
|
|
20
|
+
)
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<dl
|
|
25
|
+
:class="[
|
|
26
|
+
'oc-fact-list',
|
|
27
|
+
plain && 'oc-fact-list--plain',
|
|
28
|
+
compact && 'oc-fact-list--compact',
|
|
29
|
+
stacked && 'oc-fact-list--stacked',
|
|
30
|
+
accent && 'oc-fact-list--accent',
|
|
31
|
+
]"
|
|
32
|
+
>
|
|
33
|
+
<slot>
|
|
34
|
+
<div v-for="(row, i) in items" :key="i"><dt>{{ row.label }}</dt><dd>{{ row.value }}</dd></div>
|
|
35
|
+
</slot>
|
|
36
|
+
</dl>
|
|
37
|
+
</template>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcFilterCounts.vue | Map: types + props -> template (fieldset radiogroup -> tile per view) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* View filter tiles with counts (Active / Deleted / All). v-model = selected value.
|
|
5
|
+
* Same markup as packages/ui/snippets/filter-counts.html. Classes: .oc-filter-counts(--compact), -item(--success/--destructive/--muted), -value, -label, -hint
|
|
6
|
+
*/
|
|
7
|
+
import { useUid } from '../composables/useUid'
|
|
8
|
+
|
|
9
|
+
export interface FilterCountsItem {
|
|
10
|
+
value: string
|
|
11
|
+
label: string
|
|
12
|
+
count: number | string
|
|
13
|
+
hint?: string
|
|
14
|
+
/** Accent color of the tile; default primary. */
|
|
15
|
+
tone?: 'primary' | 'success' | 'destructive' | 'muted'
|
|
16
|
+
disabled?: boolean
|
|
17
|
+
}
|
|
18
|
+
defineProps<{
|
|
19
|
+
options: FilterCountsItem[]
|
|
20
|
+
/** aria-label of the group */
|
|
21
|
+
label?: string
|
|
22
|
+
compact?: boolean
|
|
23
|
+
name?: string
|
|
24
|
+
}>()
|
|
25
|
+
const model = defineModel<string>()
|
|
26
|
+
const uid = useUid('oc-filter')
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<fieldset :class="['oc-filter-counts', compact && 'oc-filter-counts--compact']" role="radiogroup" :aria-label="label">
|
|
31
|
+
<label v-for="option in options" :key="option.value" :class="['oc-filter-counts-item', option.tone && option.tone !== 'primary' && `oc-filter-counts-item--${option.tone}`]">
|
|
32
|
+
<input v-model="model" type="radio" :name="name ?? uid" :value="option.value" :disabled="option.disabled" />
|
|
33
|
+
<span class="oc-filter-counts-value">{{ option.count }}</span>
|
|
34
|
+
<span class="oc-filter-counts-label">{{ option.label }}</span>
|
|
35
|
+
<span v-if="option.hint" class="oc-filter-counts-hint">{{ option.hint }}</span>
|
|
36
|
+
</label>
|
|
37
|
+
</fieldset>
|
|
38
|
+
</template>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcFlipCard.vue | Map: props + v-model:flipped -> card -> checkbox (toggle) -> inner -> front / back faces -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* 3D flip card (same markup as snippets/flip-card.html). v-model:flipped. trigger: toggle (checkbox, default) |
|
|
5
|
+
* hover (--hover, tabindex 0) | manual (.is-flipped from the model only). Props: vertical, frontLabel, backLabel.
|
|
6
|
+
* Slots: front, back. Classes: .oc-flip-card(--hover/--vertical, .is-flipped), __toggle, __inner, __face(--front/--back),
|
|
7
|
+
* __title, __text, __flip
|
|
8
|
+
*/
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
withDefaults(
|
|
12
|
+
defineProps<{ trigger?: 'toggle' | 'hover' | 'manual'; vertical?: boolean; frontLabel?: string; backLabel?: string }>(),
|
|
13
|
+
{ trigger: 'toggle', frontLabel: 'Show answer', backLabel: 'Back' },
|
|
14
|
+
)
|
|
15
|
+
const flipped = defineModel<boolean>('flipped', { default: false })
|
|
16
|
+
const id = useUid('oc-flip')
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<div
|
|
21
|
+
:class="['oc-flip-card', trigger === 'hover' && 'oc-flip-card--hover', vertical && 'oc-flip-card--vertical', trigger !== 'toggle' && flipped && 'is-flipped']"
|
|
22
|
+
:tabindex="trigger === 'hover' ? 0 : undefined"
|
|
23
|
+
>
|
|
24
|
+
<input v-if="trigger === 'toggle'" :id="id" v-model="flipped" type="checkbox" class="oc-flip-card__toggle" />
|
|
25
|
+
<div class="oc-flip-card__inner">
|
|
26
|
+
<div class="oc-flip-card__face oc-flip-card__face--front">
|
|
27
|
+
<slot name="front" />
|
|
28
|
+
<label v-if="trigger === 'toggle'" class="oc-flip-card__flip" :for="id">{{ frontLabel }}</label>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="oc-flip-card__face oc-flip-card__face--back">
|
|
31
|
+
<slot name="back" />
|
|
32
|
+
<label v-if="trigger === 'toggle'" class="oc-flip-card__flip" :for="id">{{ backLabel }}</label>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Form field: label, control (default slot), description and error.
|
|
4
|
+
* The slot receives { id, describedBy, invalid } so the control can be wired:
|
|
5
|
+
* <OcFormField label="Email" description="..." v-slot="f"><OcInput :id="f.id" :aria-describedby="f.describedBy" /></OcFormField>
|
|
6
|
+
* Classes: .oc-field(--horizontal), .oc-field-label, .oc-field-description, .oc-field-error
|
|
7
|
+
*/
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
label?: string
|
|
13
|
+
description?: string
|
|
14
|
+
error?: string
|
|
15
|
+
/** id of the control; generated when omitted */
|
|
16
|
+
for?: string
|
|
17
|
+
horizontal?: boolean
|
|
18
|
+
}>()
|
|
19
|
+
const generated = useUid('oc-field')
|
|
20
|
+
const id = computed(() => props.for ?? generated)
|
|
21
|
+
const helpId = computed(() => `${id.value}-help`)
|
|
22
|
+
const errorId = computed(() => `${id.value}-error`)
|
|
23
|
+
const describedBy = computed(() => {
|
|
24
|
+
const ids = [props.description && helpId.value, props.error && errorId.value].filter(Boolean)
|
|
25
|
+
return ids.length ? ids.join(' ') : undefined
|
|
26
|
+
})
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<div :class="['oc-field', horizontal && 'oc-field--horizontal']">
|
|
31
|
+
<label v-if="label || $slots.label" class="oc-field-label" :for="id"><slot name="label">{{ label }}</slot></label>
|
|
32
|
+
<slot :id="id" :described-by="describedBy" :invalid="!!error" />
|
|
33
|
+
<p v-if="description" :id="helpId" class="oc-field-description">{{ description }}</p>
|
|
34
|
+
<p v-if="error" :id="errorId" class="oc-field-error" role="alert">{{ error }}</p>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcGreeting.vue | Map: time-of-day logic -> header -> icon slot -> body (eyebrow, title + name, date) -> actions slot -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Dashboard greeting. The time of day comes from `date` (default now): morning 05-12, afternoon 12-18, evening otherwise.
|
|
5
|
+
* Slots: icon, actions. Classes: .oc-greeting(--morning/--afternoon/--evening/--lg), -icon, -body, -eyebrow, -title, -name, -date, -actions
|
|
6
|
+
*/
|
|
7
|
+
import { computed } from 'vue'
|
|
8
|
+
|
|
9
|
+
type TimeOfDay = 'morning' | 'afternoon' | 'evening'
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
name?: string
|
|
13
|
+
date?: Date
|
|
14
|
+
timeOfDay?: TimeOfDay
|
|
15
|
+
/** Translated greetings, e.g. { morning: 'Goedemorgen' }. */
|
|
16
|
+
words?: Partial<Record<TimeOfDay, string>>
|
|
17
|
+
locale?: string
|
|
18
|
+
hideDate?: boolean
|
|
19
|
+
eyebrow?: string
|
|
20
|
+
size?: 'lg'
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const WORDS: Record<TimeOfDay, string> = { morning: 'Good morning', afternoon: 'Good afternoon', evening: 'Good evening' }
|
|
24
|
+
|
|
25
|
+
const when = computed(() => props.date ?? new Date())
|
|
26
|
+
const tod = computed<TimeOfDay>(() => {
|
|
27
|
+
if (props.timeOfDay) return props.timeOfDay
|
|
28
|
+
const h = when.value.getHours()
|
|
29
|
+
return h >= 5 && h < 12 ? 'morning' : h >= 12 && h < 18 ? 'afternoon' : 'evening'
|
|
30
|
+
})
|
|
31
|
+
const word = computed(() => props.words?.[tod.value] ?? WORDS[tod.value])
|
|
32
|
+
const dateText = computed(() =>
|
|
33
|
+
new Intl.DateTimeFormat(props.locale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }).format(when.value),
|
|
34
|
+
)
|
|
35
|
+
// Local YYYY-MM-DD for datetime (toISOString would use UTC).
|
|
36
|
+
const iso = computed(() => {
|
|
37
|
+
const d = when.value
|
|
38
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
|
39
|
+
})
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<header :class="['oc-greeting', `oc-greeting--${tod}`, size && `oc-greeting--${size}`]">
|
|
44
|
+
<span v-if="$slots.icon" class="oc-greeting-icon" aria-hidden="true"><slot name="icon" /></span>
|
|
45
|
+
<div class="oc-greeting-body">
|
|
46
|
+
<p v-if="eyebrow" class="oc-greeting-eyebrow">{{ eyebrow }}</p>
|
|
47
|
+
<h2 class="oc-greeting-title">{{ word }}<template v-if="name">, <span class="oc-greeting-name">{{ name }}</span></template></h2>
|
|
48
|
+
<time v-if="!hideDate" class="oc-greeting-date" :datetime="iso">{{ dateText }}</time>
|
|
49
|
+
</div>
|
|
50
|
+
<div v-if="$slots.actions" class="oc-greeting-actions"><slot name="actions" /></div>
|
|
51
|
+
</header>
|
|
52
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Page or section title with a muted description. Classes: .oc-heading(--sm). `as` picks the title tag. */
|
|
3
|
+
withDefaults(
|
|
4
|
+
defineProps<{
|
|
5
|
+
title?: string
|
|
6
|
+
description?: string
|
|
7
|
+
size?: 'sm' | 'md'
|
|
8
|
+
as?: 'h1' | 'h2' | 'h3' | 'h4'
|
|
9
|
+
}>(),
|
|
10
|
+
{ size: 'md', as: 'h2' },
|
|
11
|
+
)
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div :class="['oc-heading', size === 'sm' && 'oc-heading--sm']">
|
|
16
|
+
<component :is="as" class="oc-heading-title"><slot name="title">{{ title }}</slot></component>
|
|
17
|
+
<p v-if="description || $slots.default" class="oc-heading-description"><slot>{{ description }}</slot></p>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Preview on hover/focus (interestfor + popover, CSS hover fallback). The trigger is the `trigger` slot
|
|
4
|
+
* (scoped: { id }) or a link with `label` and `href`; default slot = card content.
|
|
5
|
+
* Classes: .oc-hover-card(--top), -trigger, -content(--sm). Attributes land on the content element.
|
|
6
|
+
*/
|
|
7
|
+
import { useAttrs } from 'vue'
|
|
8
|
+
import { useUid } from '../composables/useUid'
|
|
9
|
+
|
|
10
|
+
defineOptions({ inheritAttrs: false })
|
|
11
|
+
defineProps<{ label?: string; href?: string; top?: boolean; small?: boolean }>()
|
|
12
|
+
const attrs = useAttrs()
|
|
13
|
+
const id = (attrs.id as string | undefined) ?? useUid('oc-hover-card')
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<span :class="['oc-hover-card', top && 'oc-hover-card--top']">
|
|
18
|
+
<slot name="trigger" :id="id">
|
|
19
|
+
<a class="oc-hover-card-trigger" :href="href ?? '#'" :interestfor="id">{{ label }}</a>
|
|
20
|
+
</slot>
|
|
21
|
+
<div v-bind="attrs" :id="id" :class="['oc-hover-card-content', small && 'oc-hover-card-content--sm']" popover>
|
|
22
|
+
<slot />
|
|
23
|
+
</div>
|
|
24
|
+
</span>
|
|
25
|
+
</template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcImageZoom.vue | Map: props + v-model:zoom -> toolbar (radio levels) -> viewport -> canvas -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Zoom / pan viewport (same markup as snippets/image-zoom.html). v-model:zoom = current level (default 1).
|
|
5
|
+
* levels: toolbar values (default [1, 1.5, 2, 3]; [] = no toolbar). ratio: square | photo | CSS ratio.
|
|
6
|
+
* Default slot replaces the <img>. Classes: .oc-image-zoom(--square/--photo), __toolbar, __label, __level, __viewport, __canvas
|
|
7
|
+
*/
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<{ src?: string; alt?: string; levels?: number[]; ratio?: string }>(), {
|
|
12
|
+
alt: '',
|
|
13
|
+
levels: () => [1, 1.5, 2, 3],
|
|
14
|
+
})
|
|
15
|
+
const zoom = defineModel<number>('zoom', { default: 1 })
|
|
16
|
+
const name = useUid('oc-zoom')
|
|
17
|
+
const preset = computed(() => (props.ratio === 'square' || props.ratio === 'photo' ? props.ratio : undefined))
|
|
18
|
+
// Writing --oc-zoom inline means any level works, not only the ones the CSS :has() rules know.
|
|
19
|
+
const vars = computed(() => ({ '--oc-zoom': String(zoom.value), ...(props.ratio && !preset.value ? { '--oc-image-zoom-ratio': props.ratio } : {}) }))
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div :class="['oc-image-zoom', preset && `oc-image-zoom--${preset}`]" :style="vars">
|
|
24
|
+
<div v-if="levels.length" class="oc-image-zoom__toolbar" role="radiogroup" aria-label="Zoom level">
|
|
25
|
+
<span class="oc-image-zoom__label">Zoom</span>
|
|
26
|
+
<label v-for="l in levels" :key="l" class="oc-image-zoom__level">
|
|
27
|
+
<input v-model="zoom" type="radio" :name="name" :value="l" />{{ Math.round(l * 100) }}%
|
|
28
|
+
</label>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="oc-image-zoom__viewport" tabindex="0" role="region" :aria-label="alt ? `${alt}, scroll to pan` : 'Scroll to pan'">
|
|
31
|
+
<div class="oc-image-zoom__canvas">
|
|
32
|
+
<slot>
|
|
33
|
+
<img v-if="src" :src="src" :alt="alt" />
|
|
34
|
+
<div v-else class="oc-media-art" role="img" :aria-label="alt" />
|
|
35
|
+
</slot>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Text input. v-model. `error` sets .oc-input--error + aria-invalid. Any native attribute falls through. */
|
|
3
|
+
defineProps<{ error?: boolean }>()
|
|
4
|
+
const model = defineModel<string | number>()
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<input v-model="model" :class="['oc-input', error && 'oc-input--error']" :aria-invalid="error ? 'true' : undefined" />
|
|
9
|
+
</template>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Input with prefix/suffix addon. Default slot = the input(s); `prefix` / `suffix` are text props or slots. */
|
|
3
|
+
defineProps<{ prefix?: string; suffix?: string }>()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<div class="oc-input-group">
|
|
8
|
+
<span v-if="prefix || $slots.prefix" class="oc-input-addon"><slot name="prefix">{{ prefix }}</slot></span>
|
|
9
|
+
<slot />
|
|
10
|
+
<span v-if="suffix || $slots.suffix" class="oc-input-addon"><slot name="suffix">{{ suffix }}</slot></span>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* List row. Renders <a> when `href` is set. Slots: media (left; `icon` boxes it, `image` makes it an image),
|
|
4
|
+
* default (replaces title + description), actions (right), header / footer (full-width rows).
|
|
5
|
+
* Classes: .oc-item(--outline/--muted/--sm/--interactive), -media(--icon/--image), -content, -title, -description,
|
|
6
|
+
* -actions, -header, -footer
|
|
7
|
+
*/
|
|
8
|
+
withDefaults(
|
|
9
|
+
defineProps<{
|
|
10
|
+
variant?: 'default' | 'outline' | 'muted'
|
|
11
|
+
size?: 'sm' | 'md'
|
|
12
|
+
interactive?: boolean
|
|
13
|
+
href?: string
|
|
14
|
+
title?: string
|
|
15
|
+
description?: string
|
|
16
|
+
icon?: boolean
|
|
17
|
+
image?: boolean
|
|
18
|
+
}>(),
|
|
19
|
+
{ variant: 'default', size: 'md' },
|
|
20
|
+
)
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<component
|
|
25
|
+
:is="href ? 'a' : 'div'"
|
|
26
|
+
:href="href"
|
|
27
|
+
:class="['oc-item', variant !== 'default' && `oc-item--${variant}`, size === 'sm' && 'oc-item--sm', interactive && 'oc-item--interactive']"
|
|
28
|
+
>
|
|
29
|
+
<div v-if="$slots.header" class="oc-item-header"><slot name="header" /></div>
|
|
30
|
+
<div v-if="$slots.media" :class="['oc-item-media', icon && 'oc-item-media--icon', image && 'oc-item-media--image']"><slot name="media" /></div>
|
|
31
|
+
<div class="oc-item-content">
|
|
32
|
+
<slot>
|
|
33
|
+
<p v-if="title" class="oc-item-title">{{ title }}</p>
|
|
34
|
+
<p v-if="description" class="oc-item-description">{{ description }}</p>
|
|
35
|
+
</slot>
|
|
36
|
+
</div>
|
|
37
|
+
<div v-if="$slots.actions" class="oc-item-actions"><slot name="actions" /></div>
|
|
38
|
+
<div v-if="$slots.footer" class="oc-item-footer"><slot name="footer" /></div>
|
|
39
|
+
</component>
|
|
40
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Shortcut: `keys` ["Ctrl", "K"] renders one key per entry, with a "+" separator when `separator` is set. Or pass OcKbd in the slot. Classes: .oc-kbd-group, .oc-kbd-sep */
|
|
3
|
+
defineProps<{ keys?: string[]; separator?: boolean }>()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<span class="oc-kbd-group">
|
|
8
|
+
<slot>
|
|
9
|
+
<template v-for="(key, i) in keys" :key="key + i">
|
|
10
|
+
<span v-if="separator && i > 0" class="oc-kbd-sep">+</span>
|
|
11
|
+
<kbd class="oc-kbd">{{ key }}</kbd>
|
|
12
|
+
</template>
|
|
13
|
+
</slot>
|
|
14
|
+
</span>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Form label. `for` links it to a control. Classes: .oc-label(--required/--inline/--muted) */
|
|
3
|
+
defineProps<{ for?: string; required?: boolean; inline?: boolean; muted?: boolean }>()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<label :class="['oc-label', required && 'oc-label--required', inline && 'oc-label--inline', muted && 'oc-label--muted']" :for="$props.for">
|
|
8
|
+
<slot />
|
|
9
|
+
</label>
|
|
10
|
+
</template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcLanguageSelect.vue | Map: types + props -> template (label pill: icon slot + native select) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Compact language / currency switcher. v-model = selected value. Slot `icon` replaces the globe / symbol.
|
|
5
|
+
* Same markup as packages/ui/snippets/language-select.html. class/style on the pill, other attributes on the select.
|
|
6
|
+
*/
|
|
7
|
+
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
8
|
+
|
|
9
|
+
export interface LanguageSelectItem {
|
|
10
|
+
value: string
|
|
11
|
+
label: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
defineOptions({ inheritAttrs: false })
|
|
15
|
+
defineProps<{
|
|
16
|
+
options: LanguageSelectItem[]
|
|
17
|
+
/** aria-label of the select (default "Language" / "Currency") */
|
|
18
|
+
label?: string
|
|
19
|
+
currency?: boolean
|
|
20
|
+
ghost?: boolean
|
|
21
|
+
/** Text icon, e.g. "€" (default: globe, or € with `currency`) */
|
|
22
|
+
icon?: string
|
|
23
|
+
}>()
|
|
24
|
+
const model = defineModel<string>()
|
|
25
|
+
const { root, control } = useAttrsSplit()
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<label v-bind="root" :class="['oc-language-select', ghost && 'oc-language-select--ghost', currency && 'oc-language-select--currency']">
|
|
30
|
+
<span class="oc-language-select-icon" aria-hidden="true">
|
|
31
|
+
<slot name="icon">
|
|
32
|
+
<template v-if="icon || currency">{{ icon ?? '€' }}</template>
|
|
33
|
+
<svg v-else viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9" /><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" /></svg>
|
|
34
|
+
</slot>
|
|
35
|
+
</span>
|
|
36
|
+
<select v-bind="control" v-model="model" class="oc-language-select-control" :aria-label="label ?? (currency ? 'Currency' : 'Language')">
|
|
37
|
+
<option v-for="option in options" :key="option.value" :value="option.value">{{ option.label }}</option>
|
|
38
|
+
</select>
|
|
39
|
+
</label>
|
|
40
|
+
</template>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Large clickable card with icon, title and description. Renders <a> with `href`, <span> when disabled,
|
|
4
|
+
* otherwise <button>. State: `current` (aria-current), `disabled` (aria-disabled).
|
|
5
|
+
* Slots: icon, title (+ trailing badge), description, footer.
|
|
6
|
+
*/
|
|
7
|
+
import { computed } from 'vue'
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
href?: string
|
|
11
|
+
title?: string
|
|
12
|
+
description?: string
|
|
13
|
+
footer?: string
|
|
14
|
+
current?: boolean
|
|
15
|
+
disabled?: boolean
|
|
16
|
+
}>()
|
|
17
|
+
const tag = computed(() => (props.disabled ? 'span' : props.href ? 'a' : 'button'))
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<component
|
|
22
|
+
:is="tag"
|
|
23
|
+
class="oc-launcher"
|
|
24
|
+
:href="tag === 'a' ? href : undefined"
|
|
25
|
+
:type="tag === 'button' ? 'button' : undefined"
|
|
26
|
+
:aria-current="current ? 'true' : undefined"
|
|
27
|
+
:aria-disabled="disabled ? 'true' : undefined"
|
|
28
|
+
>
|
|
29
|
+
<span v-if="$slots.icon" class="oc-launcher-icon" aria-hidden="true"><slot name="icon" /></span>
|
|
30
|
+
<h3 class="oc-launcher-title"><slot name="title">{{ title }}</slot></h3>
|
|
31
|
+
<p v-if="description || $slots.description" class="oc-launcher-description"><slot name="description">{{ description }}</slot></p>
|
|
32
|
+
<span v-if="footer || $slots.footer" class="oc-launcher-footer"><slot name="footer">{{ footer }}</slot></span>
|
|
33
|
+
</component>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcLightbox.vue | Map: props + model -> open at photo -> thumbnails -> <dialog> (bar, track of slides) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Lightbox on a native <dialog> (same markup as snippets/lightbox.html). v-model:open controls it.
|
|
5
|
+
* images: { src?, alt, caption?, thumb? }[] (no src = placeholder art). Prev / next are anchor links.
|
|
6
|
+
* Slot: caption (scoped: { image, index }). Classes: .oc-lightbox(--single), __bar, __title, __close, __track,
|
|
7
|
+
* __slide, __media, __nav(--prev/--next), __caption, __counter; .oc-lightbox-thumbs / -thumb.
|
|
8
|
+
*/
|
|
9
|
+
import { nextTick, ref } from 'vue'
|
|
10
|
+
import { useDialog } from '../composables/useDialog'
|
|
11
|
+
import { useUid } from '../composables/useUid'
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(
|
|
14
|
+
defineProps<{ images: { src?: string; alt: string; caption?: string; thumb?: string }[]; title?: string; label?: string; thumbnails?: boolean }>(),
|
|
15
|
+
{ label: 'Photo viewer', thumbnails: true },
|
|
16
|
+
)
|
|
17
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
18
|
+
const el = ref<HTMLDialogElement | null>(null)
|
|
19
|
+
const { onClose } = useDialog(open, el)
|
|
20
|
+
const base = useUid('oc-lightbox')
|
|
21
|
+
const n = () => props.images.length
|
|
22
|
+
const slideId = (i: number) => `${base}-${((i % n()) + n()) % n() + 1}`
|
|
23
|
+
|
|
24
|
+
// Open the viewer and jump (without animation) to photo i.
|
|
25
|
+
async function show(i: number) {
|
|
26
|
+
open.value = true
|
|
27
|
+
await nextTick()
|
|
28
|
+
document.getElementById(slideId(i))?.scrollIntoView?.({ behavior: 'instant' as ScrollBehavior, block: 'nearest', inline: 'center' })
|
|
29
|
+
}
|
|
30
|
+
defineExpose({ show, close: () => (open.value = false) })
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<div v-if="thumbnails" class="oc-lightbox-thumbs">
|
|
35
|
+
<button v-for="(img, i) in images" :key="i" type="button" class="oc-lightbox-thumb" :aria-label="`Open photo ${i + 1}: ${img.alt}`" @click="show(i)">
|
|
36
|
+
<img v-if="img.thumb || img.src" :src="img.thumb || img.src" :alt="img.alt" />
|
|
37
|
+
<span v-else class="oc-media-art" />
|
|
38
|
+
</button>
|
|
39
|
+
</div>
|
|
40
|
+
<dialog ref="el" :class="['oc-lightbox', images.length === 1 && 'oc-lightbox--single']" closedby="any" :aria-label="label" @close="onClose">
|
|
41
|
+
<div class="oc-lightbox__bar">
|
|
42
|
+
<h2 v-if="title" class="oc-lightbox__title">{{ title }}</h2>
|
|
43
|
+
<span v-else />
|
|
44
|
+
<button type="button" class="oc-lightbox__close" aria-label="Close" @click="open = false">✕</button>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="oc-lightbox__track" tabindex="0" aria-label="Photos, use the arrow keys">
|
|
47
|
+
<figure v-for="(img, i) in images" :id="slideId(i)" :key="i" class="oc-lightbox__slide">
|
|
48
|
+
<div class="oc-lightbox__media">
|
|
49
|
+
<img v-if="img.src" :src="img.src" :alt="img.alt" />
|
|
50
|
+
<span v-else class="oc-media-art" role="img" :aria-label="img.alt" />
|
|
51
|
+
</div>
|
|
52
|
+
<template v-if="images.length > 1">
|
|
53
|
+
<a class="oc-lightbox__nav oc-lightbox__nav--prev" :href="`#${slideId(i - 1)}`" aria-label="Previous photo">←</a>
|
|
54
|
+
<a class="oc-lightbox__nav oc-lightbox__nav--next" :href="`#${slideId(i + 1)}`" aria-label="Next photo">→</a>
|
|
55
|
+
</template>
|
|
56
|
+
<figcaption class="oc-lightbox__caption">
|
|
57
|
+
<span v-if="images.length > 1" class="oc-lightbox__counter">{{ i + 1 }} / {{ images.length }}</span>
|
|
58
|
+
<slot name="caption" :image="img" :index="i">{{ img.caption ?? img.alt }}</slot>
|
|
59
|
+
</figcaption>
|
|
60
|
+
</figure>
|
|
61
|
+
</div>
|
|
62
|
+
</dialog>
|
|
63
|
+
</template>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcLinkArrow.vue | Map: props (direction, tone, size) -> a.oc-link-arrow -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/** Text link with a moving arrow. Classes: .oc-link-arrow + --back/--external, --accent/--muted, --sm/--lg */
|
|
4
|
+
withDefaults(
|
|
5
|
+
defineProps<{
|
|
6
|
+
href: string
|
|
7
|
+
direction?: 'forward' | 'back' | 'external'
|
|
8
|
+
tone?: 'default' | 'accent' | 'muted'
|
|
9
|
+
size?: 'sm' | 'default' | 'lg'
|
|
10
|
+
}>(),
|
|
11
|
+
{ direction: 'forward', tone: 'default', size: 'default' },
|
|
12
|
+
)
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<a
|
|
17
|
+
:class="[
|
|
18
|
+
'oc-link-arrow',
|
|
19
|
+
direction !== 'forward' && `oc-link-arrow--${direction}`,
|
|
20
|
+
tone !== 'default' && `oc-link-arrow--${tone}`,
|
|
21
|
+
size !== 'default' && `oc-link-arrow--${size}`,
|
|
22
|
+
]"
|
|
23
|
+
:href="href"
|
|
24
|
+
><slot /></a>
|
|
25
|
+
</template>
|