@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,12 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Exclusive accordion on <details name>. Put OcAccordionItem children inside; they share one group name. */
|
|
3
|
+
import { provide } from 'vue'
|
|
4
|
+
import { useUid } from '../composables/useUid'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ name?: string }>()
|
|
7
|
+
provide('oc-accordion-name', props.name ?? useUid('oc-accordion'))
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<div class="oc-accordion"><slot /></div>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAccordionAccent.vue | Map: div.oc-accordion.oc-accordion--accent (+ color classes) -> OcAccordionItem children -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Accent accordion: separate item cards with a colored start edge. Put OcAccordionItem children inside;
|
|
5
|
+
* they share one group name (like OcAccordion). Color one item with class "oc-accent--violet" or "oc-accordion-item--warning".
|
|
6
|
+
* Classes: .oc-accordion--accent, .oc-accent--*, .oc-accordion--success/--warning/--danger, .oc-accordion--flush, .oc-accordion-accent-meta
|
|
7
|
+
*/
|
|
8
|
+
import { provide } from 'vue'
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
name?: string
|
|
13
|
+
/** Color preset for all items. */
|
|
14
|
+
accent?: 'blue' | 'cyan' | 'green' | 'orange' | 'red' | 'violet' | 'yellow'
|
|
15
|
+
/** Semantic color for all items. */
|
|
16
|
+
tone?: 'success' | 'warning' | 'danger'
|
|
17
|
+
flush?: boolean
|
|
18
|
+
}>()
|
|
19
|
+
// Same key as OcAccordion, so OcAccordionItem picks up the group name.
|
|
20
|
+
provide('oc-accordion-name', props.name ?? useUid('oc-accordion'))
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<div :class="['oc-accordion', 'oc-accordion--accent', flush && 'oc-accordion--flush', accent && `oc-accent--${accent}`, tone && `oc-accordion--${tone}`]">
|
|
25
|
+
<slot />
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** One accordion section (details/summary). v-model:open; default slot = content, `title` prop or slot = trigger. */
|
|
3
|
+
import { inject } from 'vue'
|
|
4
|
+
|
|
5
|
+
defineProps<{ title?: string }>()
|
|
6
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
7
|
+
const name = inject<string | undefined>('oc-accordion-name', undefined)
|
|
8
|
+
|
|
9
|
+
function onToggle(e: Event) {
|
|
10
|
+
open.value = (e.target as HTMLDetailsElement).open
|
|
11
|
+
}
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<details class="oc-accordion-item" :name="name" :open="open" @toggle="onToggle">
|
|
16
|
+
<summary class="oc-accordion-trigger"><slot name="title">{{ title }}</slot></summary>
|
|
17
|
+
<div class="oc-accordion-content"><slot /></div>
|
|
18
|
+
</details>
|
|
19
|
+
</template>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAffixInput.vue | Map: props -> variant presets -> template (input group --affix with inline symbol) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Input with a symbol inside the frame. variant: currency (€), percent (%) or email (@); or own prefix / suffix (props or slots).
|
|
5
|
+
* Same markup as packages/ui/snippets/affix-input.html. v-model: string.
|
|
6
|
+
* class/style stay on the wrapper, other attributes go to the input.
|
|
7
|
+
*/
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
10
|
+
|
|
11
|
+
export type AffixInputKind = 'currency' | 'percent' | 'email'
|
|
12
|
+
|
|
13
|
+
defineOptions({ inheritAttrs: false })
|
|
14
|
+
// alignEnd defaults to undefined (not false), so the variant preset can decide.
|
|
15
|
+
const props = withDefaults(defineProps<{ variant?: AffixInputKind; prefix?: string; suffix?: string; alignEnd?: boolean; error?: boolean }>(), {
|
|
16
|
+
alignEnd: undefined,
|
|
17
|
+
})
|
|
18
|
+
const model = defineModel<string>({ default: '' })
|
|
19
|
+
const { root, control } = useAttrsSplit()
|
|
20
|
+
|
|
21
|
+
// Preset per variant. Email uses type="email" so :user-invalid checks the address.
|
|
22
|
+
const PRESETS = {
|
|
23
|
+
currency: { prefix: '\u20AC', suffix: undefined, end: false, input: { type: 'text', inputmode: 'decimal' } },
|
|
24
|
+
percent: { prefix: '%', suffix: undefined, end: false, input: { type: 'text', inputmode: 'decimal' } },
|
|
25
|
+
email: { prefix: '@', suffix: undefined, end: false, input: { type: 'email', autocomplete: 'email' } },
|
|
26
|
+
} as const
|
|
27
|
+
|
|
28
|
+
const preset = computed(() => (props.variant ? PRESETS[props.variant] : undefined))
|
|
29
|
+
const before = computed(() => props.prefix ?? preset.value?.prefix)
|
|
30
|
+
const after = computed(() => props.suffix ?? preset.value?.suffix)
|
|
31
|
+
const end = computed(() => props.alignEnd ?? preset.value?.end ?? false)
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<div v-bind="root" :class="['oc-input-group', 'oc-input-group--affix', end && 'oc-input-group--end', error && 'oc-input-group--error']">
|
|
36
|
+
<span v-if="before || $slots.prefix" class="oc-input-addon" aria-hidden="true"><slot name="prefix">{{ before }}</slot></span>
|
|
37
|
+
<input v-bind="{ type: 'text', ...preset?.input, ...control }" v-model="model" class="oc-input" :aria-invalid="error ? 'true' : undefined" />
|
|
38
|
+
<span v-if="after || $slots.suffix" class="oc-input-addon" aria-hidden="true"><slot name="suffix">{{ after }}</slot></span>
|
|
39
|
+
</div>
|
|
40
|
+
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Alert. Classes: .oc-alert--info/success/warning/danger. Default slot = description. */
|
|
3
|
+
withDefaults(
|
|
4
|
+
defineProps<{
|
|
5
|
+
variant?: 'info' | 'success' | 'warning' | 'danger'
|
|
6
|
+
title?: string
|
|
7
|
+
}>(),
|
|
8
|
+
{ variant: 'info' },
|
|
9
|
+
)
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div :class="['oc-alert', `oc-alert--${variant}`]" role="status">
|
|
14
|
+
<h4 v-if="title || $slots.title" class="oc-alert-title"><slot name="title">{{ title }}</slot></h4>
|
|
15
|
+
<p v-if="$slots.default" class="oc-alert-description"><slot /></p>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Confirmation modal with a status icon. v-model:open. Emits `confirm` / `cancel` (both close the dialog).
|
|
4
|
+
* Slots: icon, title, default (message), footer (scoped: { close }).
|
|
5
|
+
* Classes: .oc-alert-dialog(--success/--error/--warning/--info/--question), .oc-alert-dialog-icon
|
|
6
|
+
*/
|
|
7
|
+
import { computed, ref } from 'vue'
|
|
8
|
+
import { useDialog } from '../composables/useDialog'
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(
|
|
12
|
+
defineProps<{
|
|
13
|
+
type?: 'success' | 'error' | 'warning' | 'info' | 'question'
|
|
14
|
+
title?: string
|
|
15
|
+
message?: string
|
|
16
|
+
confirmLabel?: string
|
|
17
|
+
cancelLabel?: string
|
|
18
|
+
/** Variant of the confirm button; defaults to destructive for warning/error. */
|
|
19
|
+
confirmVariant?: 'default' | 'destructive' | 'success'
|
|
20
|
+
}>(),
|
|
21
|
+
{ type: 'warning', confirmLabel: 'Confirm', cancelLabel: 'Cancel' },
|
|
22
|
+
)
|
|
23
|
+
const emit = defineEmits<{ confirm: []; cancel: [] }>()
|
|
24
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
25
|
+
const el = ref<HTMLDialogElement | null>(null)
|
|
26
|
+
const { onClose } = useDialog(open, el)
|
|
27
|
+
const titleId = useUid('oc-alert-dialog-title')
|
|
28
|
+
const textId = `${titleId}-text`
|
|
29
|
+
|
|
30
|
+
const glyphs = { success: '✓', error: '×', warning: '!', info: 'i', question: '?' } as const
|
|
31
|
+
const variant = computed(() => props.confirmVariant ?? (props.type === 'warning' || props.type === 'error' ? 'destructive' : 'default'))
|
|
32
|
+
const close = () => (open.value = false)
|
|
33
|
+
|
|
34
|
+
function confirm() {
|
|
35
|
+
emit('confirm')
|
|
36
|
+
close()
|
|
37
|
+
}
|
|
38
|
+
function cancel() {
|
|
39
|
+
emit('cancel')
|
|
40
|
+
close()
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<dialog
|
|
46
|
+
ref="el"
|
|
47
|
+
class="oc-modal"
|
|
48
|
+
closedby="closerequest"
|
|
49
|
+
role="alertdialog"
|
|
50
|
+
:aria-labelledby="titleId"
|
|
51
|
+
:aria-describedby="textId"
|
|
52
|
+
@close="onClose"
|
|
53
|
+
>
|
|
54
|
+
<div :class="['oc-modal-content', 'oc-modal-content--sm', 'oc-alert-dialog', `oc-alert-dialog--${type}`]">
|
|
55
|
+
<span class="oc-alert-dialog-icon" aria-hidden="true"><slot name="icon">{{ glyphs[type] }}</slot></span>
|
|
56
|
+
<h2 :id="titleId" class="oc-modal-title"><slot name="title">{{ title }}</slot></h2>
|
|
57
|
+
<div :id="textId" class="oc-modal-body"><slot>{{ message }}</slot></div>
|
|
58
|
+
<div class="oc-modal-footer">
|
|
59
|
+
<slot name="footer" :close="close" :confirm="confirm" :cancel="cancel">
|
|
60
|
+
<button class="oc-btn oc-btn--outline" type="button" @click="cancel">{{ cancelLabel }}</button>
|
|
61
|
+
<button :class="['oc-btn', variant !== 'default' && `oc-btn--${variant}`]" type="button" @click="confirm">{{ confirmLabel }}</button>
|
|
62
|
+
</slot>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</dialog>
|
|
66
|
+
</template>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAlphaTabs.vue | Map: nav -> "All" item -> one item per letter (current / disabled) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* All + A-Z letter filter. v-model = current letter ("all" by default).
|
|
5
|
+
* With `href` (e.g. "?letter={letter}") the items are links; otherwise buttons that update v-model.
|
|
6
|
+
* Classes: nav.oc-alpha-tabs(--wrap/--sm/--plain), .oc-alpha-tabs-item(--all), aria-current, aria-disabled
|
|
7
|
+
*/
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
letters?: string[]
|
|
13
|
+
/** Letters with entries; the others are disabled. Default: all. */
|
|
14
|
+
available?: string[]
|
|
15
|
+
/** Link pattern; "{letter}" is replaced, e.g. "?letter={letter}". */
|
|
16
|
+
href?: string
|
|
17
|
+
allLabel?: string
|
|
18
|
+
wrap?: boolean
|
|
19
|
+
size?: 'sm'
|
|
20
|
+
plain?: boolean
|
|
21
|
+
}>(),
|
|
22
|
+
{ letters: () => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''), allLabel: 'All' },
|
|
23
|
+
)
|
|
24
|
+
const current = defineModel<string>({ default: 'all' })
|
|
25
|
+
|
|
26
|
+
const items = computed(() => [
|
|
27
|
+
{ key: 'all', label: props.allLabel, all: true },
|
|
28
|
+
...props.letters.map((l) => ({ key: l, label: l, all: false })),
|
|
29
|
+
])
|
|
30
|
+
const isDisabled = (key: string) => key !== 'all' && props.available !== undefined && !props.available.includes(key)
|
|
31
|
+
const link = (key: string) => props.href?.replace('{letter}', key)
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<template>
|
|
35
|
+
<nav :class="['oc-alpha-tabs', wrap && 'oc-alpha-tabs--wrap', size && `oc-alpha-tabs--${size}`, plain && 'oc-alpha-tabs--plain']" aria-label="Filter by letter">
|
|
36
|
+
<template v-for="item in items" :key="item.key">
|
|
37
|
+
<a
|
|
38
|
+
v-if="href"
|
|
39
|
+
:class="['oc-alpha-tabs-item', item.all && 'oc-alpha-tabs-item--all']"
|
|
40
|
+
:href="isDisabled(item.key) ? undefined : link(item.key)"
|
|
41
|
+
:aria-current="current === item.key ? 'true' : undefined"
|
|
42
|
+
:aria-disabled="isDisabled(item.key) ? 'true' : undefined"
|
|
43
|
+
>{{ item.label }}</a>
|
|
44
|
+
<button
|
|
45
|
+
v-else
|
|
46
|
+
type="button"
|
|
47
|
+
:class="['oc-alpha-tabs-item', item.all && 'oc-alpha-tabs-item--all']"
|
|
48
|
+
:aria-current="current === item.key ? 'true' : undefined"
|
|
49
|
+
:disabled="isDisabled(item.key)"
|
|
50
|
+
@click="current = item.key"
|
|
51
|
+
>{{ item.label }}</button>
|
|
52
|
+
</template>
|
|
53
|
+
</nav>
|
|
54
|
+
</template>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Aspect-ratio box. `ratio` is a preset (square | photo | portrait | wide), a number (1.5) or a CSS ratio ("5 / 2");
|
|
4
|
+
* default 16 / 9. `center` centers the content. Classes: .oc-aspect(--square/--photo/--portrait/--wide/--center), --oc-aspect
|
|
5
|
+
*/
|
|
6
|
+
import { computed } from 'vue'
|
|
7
|
+
|
|
8
|
+
const presets = ['square', 'photo', 'portrait', 'wide']
|
|
9
|
+
const props = defineProps<{ ratio?: 'square' | 'photo' | 'portrait' | 'wide' | number | string; center?: boolean }>()
|
|
10
|
+
const preset = computed(() => (typeof props.ratio === 'string' && presets.includes(props.ratio) ? props.ratio : undefined))
|
|
11
|
+
const custom = computed(() => (props.ratio !== undefined && !preset.value ? String(props.ratio) : undefined))
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<div
|
|
16
|
+
:class="['oc-aspect', preset && `oc-aspect--${preset}`, center && 'oc-aspect--center']"
|
|
17
|
+
:style="custom ? { '--oc-aspect': custom } : undefined"
|
|
18
|
+
>
|
|
19
|
+
<slot />
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAttachment.vue | Map: type / extension helpers -> props -> a[download] or div chip (icon, name, size, remove) -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
export type AttachmentType = 'pdf' | 'doc' | 'sheet' | 'zip' | 'image'
|
|
4
|
+
export interface AttachmentFile {
|
|
5
|
+
name: string
|
|
6
|
+
size?: string
|
|
7
|
+
type?: AttachmentType
|
|
8
|
+
ext?: string
|
|
9
|
+
href?: string
|
|
10
|
+
image?: string
|
|
11
|
+
state?: 'uploading' | 'error'
|
|
12
|
+
progress?: number
|
|
13
|
+
removable?: boolean
|
|
14
|
+
removeLabel?: string
|
|
15
|
+
}
|
|
16
|
+
const TYPES: Record<string, AttachmentType> = {
|
|
17
|
+
pdf: 'pdf', doc: 'doc', docx: 'doc', odt: 'doc', rtf: 'doc', txt: 'doc', xls: 'sheet', xlsx: 'sheet', csv: 'sheet', ods: 'sheet',
|
|
18
|
+
zip: 'zip', rar: 'zip', '7z': 'zip', gz: 'zip', jpg: 'image', jpeg: 'image', png: 'image', gif: 'image', webp: 'image', heic: 'image', avif: 'image',
|
|
19
|
+
}
|
|
20
|
+
const extOf = (name: string) => (name.includes('.') ? name.split('.').pop()!.toLowerCase() : '')
|
|
21
|
+
/** Icon text and tint of a file name (Plan.xlsx -> XLS, sheet). */
|
|
22
|
+
export const attachmentMeta = (f: AttachmentFile) => ({
|
|
23
|
+
ext: f.ext ?? extOf(f.name).toUpperCase().replace(/^(DOC|XLS|PPT)X$/, '$1'),
|
|
24
|
+
type: f.type ?? TYPES[extOf(f.name)],
|
|
25
|
+
})
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
/**
|
|
30
|
+
* File chip (same markup as snippets/attachment.html): icon tile by type, name, size, upload / error state and remove button.
|
|
31
|
+
* With href (and not removable) it is an <a download>. Emits: remove. Classes: .oc-attachment(--pdf/--doc/--sheet/--zip/--image,
|
|
32
|
+
* --uploading/--error) __icon __body __name __size __remove; --oc-attachment-progress while uploading.
|
|
33
|
+
*/
|
|
34
|
+
import { computed } from 'vue'
|
|
35
|
+
|
|
36
|
+
const props = withDefaults(defineProps<AttachmentFile>(), { progress: 0 })
|
|
37
|
+
const emit = defineEmits<{ remove: [] }>()
|
|
38
|
+
const meta = computed(() => attachmentMeta(props))
|
|
39
|
+
const uploading = computed(() => props.state === 'uploading')
|
|
40
|
+
const link = computed(() => !!props.href && !props.removable)
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<component
|
|
45
|
+
:is="link ? 'a' : 'div'"
|
|
46
|
+
:class="['oc-attachment', meta.type && `oc-attachment--${meta.type}`, state && `oc-attachment--${state}`]"
|
|
47
|
+
:href="link ? href : undefined"
|
|
48
|
+
:download="link ? '' : undefined"
|
|
49
|
+
:style="uploading ? `--oc-attachment-progress:${progress}%` : undefined"
|
|
50
|
+
>
|
|
51
|
+
<span class="oc-attachment__icon" aria-hidden="true"><img v-if="image" :src="image" alt="" /><template v-else>{{ meta.ext }}</template></span>
|
|
52
|
+
<span class="oc-attachment__body">
|
|
53
|
+
<span class="oc-attachment__name">{{ name }}</span>
|
|
54
|
+
<span v-if="uploading" class="oc-attachment__size" role="status">{{ size ?? `Uploading… ${progress}%` }}</span>
|
|
55
|
+
<span v-else-if="size" class="oc-attachment__size">{{ size }}</span>
|
|
56
|
+
</span>
|
|
57
|
+
<button v-if="removable" type="button" class="oc-attachment__remove" :aria-label="removeLabel ?? (uploading ? `Cancel upload of ${name}` : `Remove ${name}`)" @click="emit('remove')">✕</button>
|
|
58
|
+
</component>
|
|
59
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAttachmentImage.vue | Map: props -> a.oc-attachment-image (img or placeholder art, caption) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/** Image preview inside a message (4 : 3, same markup as snippets/attachment.html). No src = placeholder art. Classes: .oc-attachment-image, __caption */
|
|
4
|
+
defineProps<{ href?: string; src?: string; alt: string; art?: number; caption?: string }>()
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<a class="oc-attachment-image" :href="href">
|
|
9
|
+
<img v-if="src" :src="src" :alt="alt" />
|
|
10
|
+
<span v-else :class="['oc-media-art', art != null && `oc-media-art--${art}`]" role="img" :aria-label="alt"></span>
|
|
11
|
+
<span v-if="caption" class="oc-attachment-image__caption">{{ caption }}</span>
|
|
12
|
+
</a>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAttachmentList.vue | Map: props + demo files -> ul (li > chip) or div of chips -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Wrapping row of file chips (snippets/attachment.html). items = AttachmentFile[] (demo files when empty); slot default replaces them.
|
|
5
|
+
* stack = one per line. as = ul (default) or div. Emits: remove(file, index). Classes: .oc-attachment-list(--stack)
|
|
6
|
+
*/
|
|
7
|
+
import OcAttachment, { type AttachmentFile } from './OcAttachment.vue'
|
|
8
|
+
|
|
9
|
+
withDefaults(defineProps<{ items?: AttachmentFile[]; stack?: boolean; as?: 'ul' | 'div'; label?: string }>(), {
|
|
10
|
+
as: 'ul',
|
|
11
|
+
label: 'Files in this message',
|
|
12
|
+
items: () => [
|
|
13
|
+
{ name: 'Quote kitchen Dewitte v2.pdf', size: '1.2 MB', href: '#quote.pdf' },
|
|
14
|
+
{ name: 'Measurements ground floor.xlsx', size: '86 KB', href: '#measurements.xlsx' },
|
|
15
|
+
{ name: 'Contract addendum.docx', size: '240 KB', href: '#contract.docx' },
|
|
16
|
+
{ name: 'Plans architect.zip', size: '14.8 MB', href: '#plans.zip' },
|
|
17
|
+
],
|
|
18
|
+
})
|
|
19
|
+
const emit = defineEmits<{ remove: [file: AttachmentFile, index: number] }>()
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<component :is="as" :class="['oc-attachment-list', stack && 'oc-attachment-list--stack']" :aria-label="label">
|
|
24
|
+
<slot>
|
|
25
|
+
<template v-for="(f, i) in items" :key="i">
|
|
26
|
+
<li v-if="as === 'ul'"><OcAttachment v-bind="f" @remove="emit('remove', f, i)" /></li>
|
|
27
|
+
<OcAttachment v-else v-bind="f" @remove="emit('remove', f, i)" />
|
|
28
|
+
</template>
|
|
29
|
+
</slot>
|
|
30
|
+
</component>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcAttachmentThumb.vue | Map: props -> .oc-attachment-thumb (img or placeholder art, remove button) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/** Square thumbnail of a pasted screenshot (snippets/attachment.html). removeLabel adds the remove button. Emits: remove. */
|
|
4
|
+
withDefaults(defineProps<{ src?: string; alt: string; art?: number; removeLabel?: string; as?: 'div' | 'span' }>(), { as: 'div' })
|
|
5
|
+
const emit = defineEmits<{ remove: [] }>()
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<component :is="as" class="oc-attachment-thumb">
|
|
10
|
+
<img v-if="src" :src="src" :alt="alt" />
|
|
11
|
+
<span v-else :class="['oc-media-art', art != null && `oc-media-art--${art}`]" role="img" :aria-label="alt"></span>
|
|
12
|
+
<button v-if="removeLabel" type="button" class="oc-attachment__remove" :aria-label="removeLabel" @click="emit('remove')">✕</button>
|
|
13
|
+
</component>
|
|
14
|
+
</template>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Avatar (initials via the default slot, or an image via `src`). Classes: .oc-avatar--sm/lg/xl */
|
|
3
|
+
withDefaults(
|
|
4
|
+
defineProps<{
|
|
5
|
+
size?: 'sm' | 'md' | 'lg' | 'xl'
|
|
6
|
+
src?: string
|
|
7
|
+
alt?: string
|
|
8
|
+
}>(),
|
|
9
|
+
{ size: 'md', alt: '' },
|
|
10
|
+
)
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<span :class="['oc-avatar', size !== 'md' && `oc-avatar--${size}`]">
|
|
15
|
+
<img v-if="src" :src="src" :alt="alt" />
|
|
16
|
+
<slot v-else />
|
|
17
|
+
</span>
|
|
18
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Badge. Classes: .oc-badge + --secondary/--outline/--destructive/--success/--warning/--info */
|
|
3
|
+
withDefaults(
|
|
4
|
+
defineProps<{
|
|
5
|
+
variant?: 'default' | 'secondary' | 'outline' | 'destructive' | 'success' | 'warning' | 'info'
|
|
6
|
+
}>(),
|
|
7
|
+
{ variant: 'default' },
|
|
8
|
+
)
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<span :class="['oc-badge', variant !== 'default' && `oc-badge--${variant}`]"><slot /></span>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcBarcodeInput.vue | Map: props + model -> template (label, digits-only field, preview figure + slot) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Barcode / EAN field with a preview. v-model: string (digits only).
|
|
5
|
+
* Slot `preview`: your own <svg>/<canvas> barcode; default = decorative placeholder bars.
|
|
6
|
+
* Same markup as packages/ui/snippets/barcode-input.html. class/style on the wrapper, other attributes on the input.
|
|
7
|
+
*/
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
10
|
+
import { useUid } from '../composables/useUid'
|
|
11
|
+
|
|
12
|
+
defineOptions({ inheritAttrs: false })
|
|
13
|
+
const props = withDefaults(defineProps<{ label?: string; id?: string; emptyText?: string; error?: boolean }>(), { emptyText: 'No code yet' })
|
|
14
|
+
const model = defineModel<string>({ default: '' })
|
|
15
|
+
const { root, control } = useAttrsSplit()
|
|
16
|
+
const auto = useUid('oc-barcode')
|
|
17
|
+
const inputId = computed(() => props.id ?? auto)
|
|
18
|
+
|
|
19
|
+
function onInput(e: Event) {
|
|
20
|
+
const input = e.target as HTMLInputElement
|
|
21
|
+
model.value = input.value
|
|
22
|
+
}
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<div v-bind="root" class="oc-barcode-input">
|
|
27
|
+
<label v-if="label" class="oc-field-label" :for="inputId">{{ label }}</label>
|
|
28
|
+
<div class="oc-input-group oc-input-group--affix">
|
|
29
|
+
<span class="oc-input-addon" aria-hidden="true">
|
|
30
|
+
<svg viewBox="0 0 16 16"><path fill="currentColor" d="M1 1h1.4v14H1zm2.2 0h.7v14h-.7zm1.6 0h1.5v14H4.8zm2.4 0h.7v14H7.2zm1.6 0h.7v14H8.8zm1.5 0h1.4v14h-1.4zm2.2 0H15v14h-.9z" /></svg>
|
|
31
|
+
</span>
|
|
32
|
+
<input
|
|
33
|
+
v-bind="control"
|
|
34
|
+
:id="inputId"
|
|
35
|
+
class="oc-input oc-barcode-input-field"
|
|
36
|
+
type="text"
|
|
37
|
+
autocomplete="off"
|
|
38
|
+
spellcheck="false"
|
|
39
|
+
maxlength="80"
|
|
40
|
+
placeholder="EAN-13 or internal code"
|
|
41
|
+
:aria-invalid="error ? 'true' : undefined"
|
|
42
|
+
:value="model"
|
|
43
|
+
@input="onInput"
|
|
44
|
+
/>
|
|
45
|
+
</div>
|
|
46
|
+
<figure :class="['oc-barcode-preview', !model && 'oc-barcode-preview--empty']" aria-label="Barcode preview">
|
|
47
|
+
<slot name="preview"><div class="oc-barcode-preview-bars" aria-hidden="true"></div></slot>
|
|
48
|
+
<figcaption class="oc-barcode-preview-code">{{ model || emptyText }}</figcaption>
|
|
49
|
+
</figure>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Breadcrumb. Pass `items` (the last one is the current page) or use the default slot. */
|
|
3
|
+
export interface BreadcrumbItem {
|
|
4
|
+
label: string
|
|
5
|
+
href?: string
|
|
6
|
+
}
|
|
7
|
+
withDefaults(defineProps<{ items?: BreadcrumbItem[]; label?: string }>(), {
|
|
8
|
+
items: () => [],
|
|
9
|
+
label: 'Breadcrumb',
|
|
10
|
+
})
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<nav class="oc-breadcrumb" :aria-label="label">
|
|
15
|
+
<slot>
|
|
16
|
+
<template v-for="(item, i) in items" :key="i">
|
|
17
|
+
<a v-if="item.href && i < items.length - 1" class="oc-breadcrumb-item" :href="item.href">{{ item.label }}</a>
|
|
18
|
+
<span v-else class="oc-breadcrumb-item" aria-current="page">{{ item.label }}</span>
|
|
19
|
+
</template>
|
|
20
|
+
</slot>
|
|
21
|
+
</nav>
|
|
22
|
+
</template>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcBrowserFrame.vue | Map: props -> figure -> bar (dots, address, actions) -> view (slot or img) -> caption -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Fake browser chrome (same markup as snippets/browser-frame.html).
|
|
5
|
+
* Props: url (address pill), src / alt (screenshot), caption, minimal, dark, tilt, auto (free height), ratio ("16 / 9").
|
|
6
|
+
* Slots: default (live content, replaces the screenshot), actions (glyphs right in the bar), caption.
|
|
7
|
+
* Classes: .oc-browser-frame(--minimal/--dark/--tilt/--auto), __bar, __address, __actions, __view, __caption
|
|
8
|
+
*/
|
|
9
|
+
withDefaults(
|
|
10
|
+
defineProps<{ url?: string; src?: string; alt?: string; caption?: string; minimal?: boolean; dark?: boolean; tilt?: boolean; auto?: boolean; ratio?: string }>(),
|
|
11
|
+
{ alt: '' },
|
|
12
|
+
)
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<figure
|
|
17
|
+
:class="['oc-browser-frame', minimal && 'oc-browser-frame--minimal', dark && 'oc-browser-frame--dark', tilt && 'oc-browser-frame--tilt', auto && 'oc-browser-frame--auto']"
|
|
18
|
+
:style="ratio ? { '--oc-browser-frame-ratio': ratio } : undefined"
|
|
19
|
+
>
|
|
20
|
+
<div class="oc-browser-frame__bar">
|
|
21
|
+
<i /><i /><i />
|
|
22
|
+
<span v-if="url" class="oc-browser-frame__address">{{ url }}</span>
|
|
23
|
+
<span v-if="$slots.actions" class="oc-browser-frame__actions" aria-hidden="true"><slot name="actions" /></span>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="oc-browser-frame__view">
|
|
26
|
+
<slot>
|
|
27
|
+
<img v-if="src" :src="src" :alt="alt" />
|
|
28
|
+
<div v-else class="oc-media-art" role="img" :aria-label="alt" />
|
|
29
|
+
</slot>
|
|
30
|
+
</div>
|
|
31
|
+
<figcaption v-if="caption || $slots.caption" class="oc-browser-frame__caption"><slot name="caption">{{ caption }}</slot></figcaption>
|
|
32
|
+
</figure>
|
|
33
|
+
</template>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!-- File: packages/vue/src/components/OcBusyOverlay.vue | Map: props + model -> <dialog closedby="none"> (spinner or progress, title, text) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Modal "please wait" overlay (same markup as snippets/busy-overlay.html). v-model:open shows it while a long submit or
|
|
5
|
+
* upload runs; the page is inert and Esc does not close it. `progress` (0-100) swaps the spinner for a progress bar.
|
|
6
|
+
* Slot: default (replaces the text). Classes: .oc-busy-overlay __title __text; .oc-spinner / .oc-progress
|
|
7
|
+
*/
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
import { useDialog } from '../composables/useDialog'
|
|
10
|
+
import { useUid } from '../composables/useUid'
|
|
11
|
+
import OcProgress from './OcProgress.vue'
|
|
12
|
+
|
|
13
|
+
const props = withDefaults(defineProps<{ title?: string; text?: string; progress?: number; label?: string; id?: string }>(), {
|
|
14
|
+
title: 'Saving…',
|
|
15
|
+
text: 'The photos are being processed. This can take a minute; please keep this page open.',
|
|
16
|
+
label: 'Busy',
|
|
17
|
+
})
|
|
18
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
19
|
+
const el = ref<HTMLDialogElement | null>(null)
|
|
20
|
+
const { onClose } = useDialog(open, el)
|
|
21
|
+
const auto = useUid('oc-busy-overlay')
|
|
22
|
+
const base = props.id ?? auto
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<dialog :id="base" ref="el" class="oc-busy-overlay" closedby="none" :aria-labelledby="`${base}-title`" :aria-describedby="`${base}-text`" @close="onClose">
|
|
27
|
+
<span v-if="progress == null" class="oc-spinner" role="status" :aria-label="label"></span>
|
|
28
|
+
<OcProgress v-else :value="progress" :label="label" />
|
|
29
|
+
<h2 :id="`${base}-title`" class="oc-busy-overlay__title">{{ title }}</h2>
|
|
30
|
+
<p :id="`${base}-text`" class="oc-busy-overlay__text"><slot>{{ text }}</slot></p>
|
|
31
|
+
</dialog>
|
|
32
|
+
</template>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Button. Renders <a> when `href` is set, otherwise <button>.
|
|
4
|
+
* Classes: .oc-btn + --secondary/--outline/--ghost/--link/--destructive/--success/--gradient/--glass,
|
|
5
|
+
* --xs/--sm/--lg, --icon, --pill
|
|
6
|
+
*/
|
|
7
|
+
withDefaults(
|
|
8
|
+
defineProps<{
|
|
9
|
+
variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive' | 'success' | 'gradient' | 'glass'
|
|
10
|
+
size?: 'xs' | 'sm' | 'md' | 'lg'
|
|
11
|
+
icon?: boolean
|
|
12
|
+
pill?: boolean
|
|
13
|
+
href?: string
|
|
14
|
+
type?: 'button' | 'submit' | 'reset'
|
|
15
|
+
}>(),
|
|
16
|
+
{ variant: 'default', size: 'md', type: 'button' },
|
|
17
|
+
)
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<a
|
|
22
|
+
v-if="href"
|
|
23
|
+
:href="href"
|
|
24
|
+
:class="[
|
|
25
|
+
'oc-btn',
|
|
26
|
+
variant !== 'default' && `oc-btn--${variant}`,
|
|
27
|
+
size !== 'md' && `oc-btn--${size}`,
|
|
28
|
+
icon && 'oc-btn--icon',
|
|
29
|
+
pill && 'oc-btn--pill',
|
|
30
|
+
]"
|
|
31
|
+
>
|
|
32
|
+
<slot />
|
|
33
|
+
</a>
|
|
34
|
+
<button
|
|
35
|
+
v-else
|
|
36
|
+
:type="type"
|
|
37
|
+
:class="[
|
|
38
|
+
'oc-btn',
|
|
39
|
+
variant !== 'default' && `oc-btn--${variant}`,
|
|
40
|
+
size !== 'md' && `oc-btn--${size}`,
|
|
41
|
+
icon && 'oc-btn--icon',
|
|
42
|
+
pill && 'oc-btn--pill',
|
|
43
|
+
]"
|
|
44
|
+
>
|
|
45
|
+
<slot />
|
|
46
|
+
</button>
|
|
47
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Joined buttons with shared borders. `label` becomes aria-label. Put OcButton (usually variant outline) and OcButtonGroupText inside. Classes: .oc-btn-group(--vertical) */
|
|
3
|
+
defineProps<{ vertical?: boolean; label?: string }>()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<div :class="['oc-btn-group', vertical && 'oc-btn-group--vertical']" role="group" :aria-label="label">
|
|
8
|
+
<slot />
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|