@birdycom/omniaui-vue 0.0.0-stage → 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 -0
- package/README.md +49 -3
- 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 +69 -6
- 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,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>
|
|
@@ -0,0 +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>
|
|
@@ -0,0 +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>
|
|
@@ -0,0 +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>
|
|
@@ -0,0 +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>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcChipFilter.vue | Map: types + props -> template (group -> chip label per option) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Rounded filter chips. `multiple`: checkboxes, v-model = string[]. Otherwise radios, v-model = string.
|
|
5
|
+
* Same markup as packages/ui/snippets/chip-filter.html. Classes: .oc-chip-filter(--sm/--scroll), -item, -count
|
|
6
|
+
*/
|
|
7
|
+
import { useUid } from '../composables/useUid'
|
|
8
|
+
|
|
9
|
+
export interface ChipFilterItem {
|
|
10
|
+
value: string
|
|
11
|
+
label: string
|
|
12
|
+
count?: number
|
|
13
|
+
disabled?: boolean
|
|
14
|
+
}
|
|
15
|
+
const props = defineProps<{
|
|
16
|
+
options: ChipFilterItem[]
|
|
17
|
+
multiple?: boolean
|
|
18
|
+
/** aria-label of the group */
|
|
19
|
+
label?: string
|
|
20
|
+
size?: 'sm' | 'md'
|
|
21
|
+
scroll?: boolean
|
|
22
|
+
name?: string
|
|
23
|
+
}>()
|
|
24
|
+
const model = defineModel<string | string[]>()
|
|
25
|
+
const uid = useUid('oc-chip')
|
|
26
|
+
|
|
27
|
+
const isChecked = (v: string) => (Array.isArray(model.value) ? model.value.includes(v) : model.value === v)
|
|
28
|
+
|
|
29
|
+
/** Checkbox: add or remove the value (works when v-model starts undefined). Radio: replace it. */
|
|
30
|
+
function toggle(v: string) {
|
|
31
|
+
if (!props.multiple) {
|
|
32
|
+
model.value = v
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
const list = Array.isArray(model.value) ? model.value : []
|
|
36
|
+
model.value = list.includes(v) ? list.filter((x) => x !== v) : [...list, v]
|
|
37
|
+
}
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<div :class="['oc-chip-filter', size === 'sm' && 'oc-chip-filter--sm', scroll && 'oc-chip-filter--scroll']" :role="multiple ? 'group' : 'radiogroup'" :aria-label="label">
|
|
42
|
+
<label v-for="option in options" :key="option.value" class="oc-chip-filter-item">
|
|
43
|
+
<input :type="multiple ? 'checkbox' : 'radio'" :name="name ?? uid" :value="option.value" :checked="isChecked(option.value)" :disabled="option.disabled" @change="toggle(option.value)" />
|
|
44
|
+
{{ option.label }}
|
|
45
|
+
<span v-if="option.count != null" class="oc-chip-filter-count">{{ option.count }}</span>
|
|
46
|
+
</label>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Card-style details/summary section. v-model:open. Slots: icon, title, default (content). Classes: --dense */
|
|
3
|
+
defineProps<{ title?: string; dense?: boolean }>()
|
|
4
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
5
|
+
|
|
6
|
+
function onToggle(e: Event) {
|
|
7
|
+
open.value = (e.target as HTMLDetailsElement).open
|
|
8
|
+
}
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<details :class="['oc-collapsible', dense && 'oc-collapsible--dense']" :open="open" @toggle="onToggle">
|
|
13
|
+
<summary>
|
|
14
|
+
<slot name="icon" />
|
|
15
|
+
<span class="oc-collapsible-title"><slot name="title">{{ title }}</slot></span>
|
|
16
|
+
</summary>
|
|
17
|
+
<div class="oc-collapsible-content"><slot /></div>
|
|
18
|
+
</details>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Native color picker next to a hex text input, kept in sync. v-model: color string (#rrggbb). */
|
|
3
|
+
withDefaults(defineProps<{ pickerLabel?: string; valueLabel?: string }>(), {
|
|
4
|
+
pickerLabel: 'Pick a color',
|
|
5
|
+
valueLabel: 'Color value',
|
|
6
|
+
})
|
|
7
|
+
const model = defineModel<string>({ default: '#000000' })
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div class="oc-color-field">
|
|
12
|
+
<input v-model="model" type="color" :aria-label="pickerLabel" />
|
|
13
|
+
<input v-model="model" class="oc-input" type="text" :aria-label="valueLabel" />
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcColorSelect.vue | Map: types + props -> template (fieldset radiogroup -> label per color) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/** Pick one color from swatches. v-model = selected value. Classes: .oc-color-select(--list/--sm), -option, -swatch, -name */
|
|
4
|
+
import { useUid } from '../composables/useUid'
|
|
5
|
+
|
|
6
|
+
export interface ColorSelectItem {
|
|
7
|
+
value: string
|
|
8
|
+
label: string
|
|
9
|
+
/** CSS color, use Omnia tokens: "var(--color-red-70)". */
|
|
10
|
+
color: string
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
}
|
|
13
|
+
defineProps<{
|
|
14
|
+
options: ColorSelectItem[]
|
|
15
|
+
/** aria-label of the group */
|
|
16
|
+
label?: string
|
|
17
|
+
list?: boolean
|
|
18
|
+
size?: 'sm' | 'md'
|
|
19
|
+
name?: string
|
|
20
|
+
}>()
|
|
21
|
+
const model = defineModel<string>()
|
|
22
|
+
const uid = useUid('oc-color')
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<fieldset :class="['oc-color-select', list && 'oc-color-select--list', size === 'sm' && 'oc-color-select--sm']" role="radiogroup" :aria-label="label">
|
|
27
|
+
<label v-for="option in options" :key="option.value" class="oc-color-select-option" :style="{ '--oc-swatch': option.color }">
|
|
28
|
+
<input v-model="model" type="radio" :name="name ?? uid" :value="option.value" :disabled="option.disabled" />
|
|
29
|
+
<span class="oc-color-select-swatch"></span>
|
|
30
|
+
<span class="oc-color-select-name">{{ option.label }}</span>
|
|
31
|
+
</label>
|
|
32
|
+
</fieldset>
|
|
33
|
+
</template>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcColumnMenu.vue | Map: scope -> toolbar trigger -> popover (title, form with one checkbox per column, reset) -> default slot (the table) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Column visibility menu. CSS hides column n of the table in the default slot when [data-col="n"] is unchecked.
|
|
5
|
+
* Checkboxes are uncontrolled, so "Reset" (form reset) restores the defaults. Emits `toggle(column, visible)`.
|
|
6
|
+
* Classes: .oc-column-menu-scope, .oc-column-menu[popover], -title, form.oc-column-menu-list, -item, -footer
|
|
7
|
+
*/
|
|
8
|
+
import { useUid } from '../composables/useUid'
|
|
9
|
+
|
|
10
|
+
withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
/** One entry per column, in order (max 10). */
|
|
13
|
+
columns: Array<{ label: string; visible?: boolean; locked?: boolean }>
|
|
14
|
+
triggerLabel?: string
|
|
15
|
+
title?: string
|
|
16
|
+
resetLabel?: string
|
|
17
|
+
}>(),
|
|
18
|
+
{ triggerLabel: 'Columns', title: 'Visible columns', resetLabel: 'Reset' },
|
|
19
|
+
)
|
|
20
|
+
const emit = defineEmits<{ toggle: [column: number, visible: boolean] }>()
|
|
21
|
+
const menuId = useUid('oc-column-menu')
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<div class="oc-column-menu-scope">
|
|
26
|
+
<div class="oc-table-toolbar">
|
|
27
|
+
<span class="oc-table-toolbar-spacer"></span>
|
|
28
|
+
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" :popovertarget="menuId">{{ triggerLabel }}</button>
|
|
29
|
+
</div>
|
|
30
|
+
<div :id="menuId" class="oc-column-menu" popover>
|
|
31
|
+
<p class="oc-column-menu-title">{{ title }}</p>
|
|
32
|
+
<form class="oc-column-menu-list">
|
|
33
|
+
<label v-for="(col, i) in columns" :key="i" class="oc-column-menu-item">
|
|
34
|
+
<input
|
|
35
|
+
type="checkbox"
|
|
36
|
+
:data-col="i + 1"
|
|
37
|
+
:checked="col.locked || col.visible !== false"
|
|
38
|
+
:disabled="col.locked"
|
|
39
|
+
@change="emit('toggle', i + 1, ($event.target as HTMLInputElement).checked)"
|
|
40
|
+
/>
|
|
41
|
+
{{ col.label }}
|
|
42
|
+
</label>
|
|
43
|
+
<div class="oc-column-menu-footer">
|
|
44
|
+
<button class="oc-btn oc-btn--ghost oc-btn--sm" type="reset">{{ resetLabel }}</button>
|
|
45
|
+
</div>
|
|
46
|
+
</form>
|
|
47
|
+
</div>
|
|
48
|
+
<slot />
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Command palette / searchable list. Filtering, aria-selected and keyboard navigation
|
|
4
|
+
* (ArrowUp/ArrowDown/Enter) are handled here (the CSS only styles the markup).
|
|
5
|
+
* v-model:query is the search text. Emits `select` with the chosen item.
|
|
6
|
+
* Classes: .oc-command, -input, -list, -group-label, -item, -shortcut, -separator, -empty
|
|
7
|
+
*/
|
|
8
|
+
import { computed, ref, watch } from 'vue'
|
|
9
|
+
|
|
10
|
+
export interface CommandItem {
|
|
11
|
+
label: string
|
|
12
|
+
value?: string
|
|
13
|
+
group?: string
|
|
14
|
+
shortcut?: string
|
|
15
|
+
disabled?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(
|
|
19
|
+
defineProps<{
|
|
20
|
+
items: CommandItem[]
|
|
21
|
+
placeholder?: string
|
|
22
|
+
label?: string
|
|
23
|
+
emptyText?: string
|
|
24
|
+
}>(),
|
|
25
|
+
{ placeholder: 'Type a command or search...', label: 'Search commands', emptyText: 'No results found.' },
|
|
26
|
+
)
|
|
27
|
+
const emit = defineEmits<{ select: [item: CommandItem] }>()
|
|
28
|
+
const query = defineModel<string>('query', { default: '' })
|
|
29
|
+
|
|
30
|
+
const filtered = computed(() => {
|
|
31
|
+
const q = query.value.trim().toLowerCase()
|
|
32
|
+
return q ? props.items.filter((i) => i.label.toLowerCase().includes(q)) : props.items
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const sections = computed(() => {
|
|
36
|
+
const order: string[] = []
|
|
37
|
+
const map = new Map<string, CommandItem[]>()
|
|
38
|
+
for (const item of filtered.value) {
|
|
39
|
+
const key = item.group ?? ''
|
|
40
|
+
if (!map.has(key)) {
|
|
41
|
+
map.set(key, [])
|
|
42
|
+
order.push(key)
|
|
43
|
+
}
|
|
44
|
+
map.get(key)!.push(item)
|
|
45
|
+
}
|
|
46
|
+
return order.map((key) => ({ label: key, items: map.get(key)! }))
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const flat = computed(() => sections.value.flatMap((s) => s.items))
|
|
50
|
+
const active = ref(0)
|
|
51
|
+
|
|
52
|
+
function firstEnabled(from = 0, step = 1) {
|
|
53
|
+
const list = flat.value
|
|
54
|
+
for (let i = from; i >= 0 && i < list.length; i += step) if (!list[i].disabled) return i
|
|
55
|
+
return -1
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
watch(flat, () => {
|
|
59
|
+
active.value = Math.max(firstEnabled(), 0)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
function move(step: 1 | -1) {
|
|
63
|
+
const next = firstEnabled(active.value + step, step)
|
|
64
|
+
if (next !== -1) active.value = next
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function choose(item: CommandItem) {
|
|
68
|
+
if (!item.disabled) emit('select', item)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function onKeydown(e: KeyboardEvent) {
|
|
72
|
+
if (e.key === 'ArrowDown') {
|
|
73
|
+
e.preventDefault()
|
|
74
|
+
move(1)
|
|
75
|
+
} else if (e.key === 'ArrowUp') {
|
|
76
|
+
e.preventDefault()
|
|
77
|
+
move(-1)
|
|
78
|
+
} else if (e.key === 'Enter') {
|
|
79
|
+
const item = flat.value[active.value]
|
|
80
|
+
if (item) {
|
|
81
|
+
e.preventDefault()
|
|
82
|
+
choose(item)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<div class="oc-command" role="combobox" aria-expanded="true" aria-haspopup="listbox">
|
|
90
|
+
<input
|
|
91
|
+
v-model="query"
|
|
92
|
+
class="oc-command-input"
|
|
93
|
+
type="search"
|
|
94
|
+
:placeholder="placeholder"
|
|
95
|
+
:aria-label="label"
|
|
96
|
+
@keydown="onKeydown"
|
|
97
|
+
/>
|
|
98
|
+
<ul class="oc-command-list" role="listbox">
|
|
99
|
+
<template v-for="(section, s) in sections" :key="section.label + s">
|
|
100
|
+
<li v-if="s > 0" class="oc-command-separator" role="separator"></li>
|
|
101
|
+
<li v-if="section.label" class="oc-command-group-label" role="presentation">{{ section.label }}</li>
|
|
102
|
+
<li
|
|
103
|
+
v-for="item in section.items"
|
|
104
|
+
:key="item.value ?? item.label"
|
|
105
|
+
class="oc-command-item"
|
|
106
|
+
role="option"
|
|
107
|
+
:aria-selected="flat[active] === item ? 'true' : undefined"
|
|
108
|
+
:aria-disabled="item.disabled ? 'true' : undefined"
|
|
109
|
+
@click="choose(item)"
|
|
110
|
+
@mousemove="!item.disabled && (active = flat.indexOf(item))"
|
|
111
|
+
>
|
|
112
|
+
{{ item.label }}
|
|
113
|
+
<span v-if="item.shortcut" class="oc-command-shortcut">{{ item.shortcut }}</span>
|
|
114
|
+
</li>
|
|
115
|
+
</template>
|
|
116
|
+
</ul>
|
|
117
|
+
<div class="oc-command-empty" :hidden="flat.length > 0">{{ emptyText }}</div>
|
|
118
|
+
</div>
|
|
119
|
+
</template>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcConfirmDialog.vue | Map: dialog.oc-modal -> confirm content (close X, icon ring, title) -> form (message, typed field, footer: discard, cancel, confirm) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Confirm variant of the alert dialog. v-model:open. Emits `confirm`, `cancel`, `discard` (all close the dialog).
|
|
5
|
+
* With `confirmWord` the browser blocks the submit until the typed value matches (pattern + required).
|
|
6
|
+
* Slots: icon, title, default (message).
|
|
7
|
+
* Classes: .oc-alert-dialog--confirm, .oc-alert-dialog-close, .oc-alert-dialog-form, .oc-alert-dialog-typed,
|
|
8
|
+
* .oc-alert-dialog-confirm, .oc-alert-dialog-discard
|
|
9
|
+
*/
|
|
10
|
+
import { ref } from 'vue'
|
|
11
|
+
import { useDialog } from '../composables/useDialog'
|
|
12
|
+
import { useUid } from '../composables/useUid'
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(
|
|
15
|
+
defineProps<{
|
|
16
|
+
type?: 'success' | 'error' | 'warning' | 'info' | 'question'
|
|
17
|
+
title?: string
|
|
18
|
+
message?: string
|
|
19
|
+
confirmLabel?: string
|
|
20
|
+
cancelLabel?: string
|
|
21
|
+
discardLabel?: string
|
|
22
|
+
confirmWord?: string
|
|
23
|
+
closeLabel?: string
|
|
24
|
+
}>(),
|
|
25
|
+
{ type: 'error', confirmLabel: 'Confirm', cancelLabel: 'Cancel', closeLabel: 'Close' },
|
|
26
|
+
)
|
|
27
|
+
const emit = defineEmits<{ confirm: []; cancel: []; discard: [] }>()
|
|
28
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
29
|
+
const el = ref<HTMLDialogElement | null>(null)
|
|
30
|
+
const { onClose } = useDialog(open, el)
|
|
31
|
+
const titleId = useUid('oc-confirm-dialog-title')
|
|
32
|
+
const textId = `${titleId}-text`
|
|
33
|
+
const glyphs = { success: '✓', error: '×', warning: '!', info: 'i', question: '?' } as const
|
|
34
|
+
|
|
35
|
+
// Every action emits its event, then closes through the model (Vue stays the source of truth).
|
|
36
|
+
function done(event: 'confirm' | 'cancel' | 'discard') {
|
|
37
|
+
if (event === 'confirm') emit('confirm')
|
|
38
|
+
else if (event === 'cancel') emit('cancel')
|
|
39
|
+
else emit('discard')
|
|
40
|
+
open.value = false
|
|
41
|
+
}
|
|
42
|
+
const destructive = () => props.type === 'error'
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<template>
|
|
46
|
+
<dialog ref="el" class="oc-modal" closedby="any" :aria-labelledby="titleId" :aria-describedby="textId" @close="onClose">
|
|
47
|
+
<div :class="['oc-modal-content', 'oc-alert-dialog', `oc-alert-dialog--${type}`, 'oc-alert-dialog--confirm']">
|
|
48
|
+
<button class="oc-alert-dialog-close" type="button" :aria-label="closeLabel" @click="done('cancel')">×</button>
|
|
49
|
+
<span class="oc-alert-dialog-icon" aria-hidden="true"><slot name="icon">{{ glyphs[type] }}</slot></span>
|
|
50
|
+
<h2 :id="titleId" class="oc-modal-title"><slot name="title">{{ title }}</slot></h2>
|
|
51
|
+
<form class="oc-alert-dialog-form" @submit.prevent="done('confirm')">
|
|
52
|
+
<div :id="textId" class="oc-modal-body"><slot>{{ message }}</slot></div>
|
|
53
|
+
<label v-if="confirmWord" class="oc-alert-dialog-typed">
|
|
54
|
+
<span>Type <code>{{ confirmWord }}</code> to confirm</span>
|
|
55
|
+
<input class="oc-input" name="confirm" :pattern="confirmWord" required autocomplete="off" />
|
|
56
|
+
</label>
|
|
57
|
+
<div class="oc-modal-footer">
|
|
58
|
+
<button v-if="discardLabel" class="oc-btn oc-btn--ghost oc-alert-dialog-discard" type="button" @click="done('discard')">{{ discardLabel }}</button>
|
|
59
|
+
<button class="oc-btn oc-btn--outline" type="button" @click="done('cancel')">{{ cancelLabel }}</button>
|
|
60
|
+
<button :class="['oc-btn', destructive() && 'oc-btn--destructive', 'oc-alert-dialog-confirm']" type="submit" value="confirm">{{ confirmLabel }}</button>
|
|
61
|
+
</div>
|
|
62
|
+
</form>
|
|
63
|
+
</div>
|
|
64
|
+
</dialog>
|
|
65
|
+
</template>
|