@birdycom/omniaui-vue 0.11.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +49 -49
- package/dist/omniaui-vue.js.map +1 -1
- package/package.json +69 -69
- package/src/blocks/OcBlockAboutTimeline.vue +44 -44
- package/src/blocks/OcBlockAppHeader.vue +36 -36
- package/src/blocks/OcBlockAppShell.vue +29 -29
- package/src/blocks/OcBlockAuth.vue +24 -24
- package/src/blocks/OcBlockAuthSplit.vue +47 -47
- package/src/blocks/OcBlockContactGrid.vue +53 -53
- package/src/blocks/OcBlockCta.vue +12 -12
- package/src/blocks/OcBlockDataTable.vue +27 -27
- package/src/blocks/OcBlockFaq.vue +49 -49
- package/src/blocks/OcBlockFeatures.vue +23 -23
- package/src/blocks/OcBlockFooter.vue +25 -25
- package/src/blocks/OcBlockFormPage.vue +24 -24
- package/src/blocks/OcBlockHero.vue +13 -13
- package/src/blocks/OcBlockHeroCode.vue +42 -42
- package/src/blocks/OcBlockHeroOrbit.vue +69 -69
- package/src/blocks/OcBlockHeroSplit.vue +15 -15
- package/src/blocks/OcBlockModuleHeader.vue +21 -21
- package/src/blocks/OcBlockPageHero.vue +13 -13
- package/src/blocks/OcBlockPortfolioGrid.vue +63 -63
- package/src/blocks/OcBlockPricing.vue +39 -39
- package/src/blocks/OcBlockProcess.vue +39 -39
- package/src/blocks/OcBlockProductGrid.vue +41 -41
- package/src/blocks/OcBlockProfileHeader.vue +50 -50
- package/src/blocks/OcBlockShowcase.vue +43 -43
- package/src/blocks/OcBlockSiteFooter.vue +54 -54
- package/src/blocks/OcBlockSiteHeader.vue +34 -34
- package/src/blocks/OcBlockSpotlight.vue +47 -47
- package/src/blocks/OcBlockStats.vue +17 -17
- package/src/blocks/OcBlockStatsCounter.vue +20 -20
- package/src/blocks/OcBlockTeamList.vue +53 -53
- package/src/blocks/OcBlockTwoFactor.vue +61 -61
- package/src/blocks/OcBlockWizardDialog.vue +67 -67
- package/src/components/OcAccordion.vue +12 -12
- package/src/components/OcAccordionAccent.vue +27 -27
- package/src/components/OcAccordionItem.vue +19 -19
- package/src/components/OcAffixInput.vue +40 -40
- package/src/components/OcAlert.vue +17 -17
- package/src/components/OcAlertDialog.vue +66 -66
- package/src/components/OcAlphaTabs.vue +54 -54
- package/src/components/OcAspectRatio.vue +21 -21
- package/src/components/OcAttachment.vue +59 -59
- package/src/components/OcAttachmentImage.vue +13 -13
- package/src/components/OcAttachmentList.vue +31 -31
- package/src/components/OcAttachmentThumb.vue +14 -14
- package/src/components/OcAvatar.vue +18 -18
- package/src/components/OcAvatarGroup.vue +7 -7
- package/src/components/OcBackLink.vue +8 -8
- package/src/components/OcBadge.vue +13 -13
- package/src/components/OcBarcodeInput.vue +51 -51
- package/src/components/OcBreadcrumb.vue +22 -22
- package/src/components/OcBrowserFrame.vue +33 -33
- package/src/components/OcBusyOverlay.vue +32 -32
- package/src/components/OcButton.vue +47 -47
- package/src/components/OcButtonGroup.vue +10 -10
- package/src/components/OcButtonGroupText.vue +7 -7
- package/src/components/OcCard.vue +20 -20
- package/src/components/OcChartLine.vue +80 -80
- package/src/components/OcChatComposer.vue +42 -42
- package/src/components/OcCheckbox.vue +26 -26
- package/src/components/OcChecklist.vue +29 -29
- package/src/components/OcCollapsible.vue +19 -19
- package/src/components/OcColorField.vue +15 -15
- package/src/components/OcColorSelect.vue +33 -33
- package/src/components/OcColumnMenu.vue +50 -50
- package/src/components/OcCommand.vue +119 -119
- package/src/components/OcConfirmDialog.vue +65 -65
- package/src/components/OcCookieConsent.vue +119 -119
- package/src/components/OcCopyField.vue +37 -37
- package/src/components/OcCounter.vue +15 -15
- package/src/components/OcDataTable.vue +144 -144
- package/src/components/OcDefinitionList.vue +35 -35
- package/src/components/OcDropdown.vue +23 -23
- package/src/components/OcDropdownItem.vue +9 -9
- package/src/components/OcDropdownLabel.vue +7 -7
- package/src/components/OcDropdownSeparator.vue +7 -7
- package/src/components/OcDropzone.vue +36 -36
- package/src/components/OcEmojiPicker.vue +45 -45
- package/src/components/OcEmptyState.vue +13 -13
- package/src/components/OcEntityCard.vue +47 -47
- package/src/components/OcFactList.vue +37 -37
- package/src/components/OcFilterCounts.vue +38 -38
- package/src/components/OcFlipCard.vue +36 -36
- package/src/components/OcFormField.vue +36 -36
- package/src/components/OcGreeting.vue +52 -52
- package/src/components/OcHeading.vue +19 -19
- package/src/components/OcHoverCard.vue +25 -25
- package/src/components/OcImageZoom.vue +39 -39
- package/src/components/OcInput.vue +9 -9
- package/src/components/OcInputGroup.vue +12 -12
- package/src/components/OcItem.vue +40 -40
- package/src/components/OcItemGroup.vue +7 -7
- package/src/components/OcItemSeparator.vue +7 -7
- package/src/components/OcKbd.vue +7 -7
- package/src/components/OcKbdGroup.vue +15 -15
- package/src/components/OcLabel.vue +10 -10
- package/src/components/OcLanguageSelect.vue +40 -40
- package/src/components/OcLauncherCard.vue +34 -34
- package/src/components/OcLightbox.vue +63 -63
- package/src/components/OcLinkArrow.vue +25 -25
- package/src/components/OcListToolbar.vue +44 -44
- package/src/components/OcMarquee.vue +36 -36
- package/src/components/OcMediaCard.vue +28 -28
- package/src/components/OcMediaRow.vue +28 -28
- package/src/components/OcMediaThumb.vue +38 -38
- package/src/components/OcMessage.vue +91 -91
- package/src/components/OcMessageList.vue +43 -43
- package/src/components/OcMessageThread.vue +155 -155
- package/src/components/OcModal.vue +42 -42
- package/src/components/OcNavmenu.vue +18 -18
- package/src/components/OcNavmenuList.vue +38 -38
- package/src/components/OcNextTeaser.vue +25 -25
- package/src/components/OcNumberInput.vue +47 -47
- package/src/components/OcOtpInput.vue +34 -34
- package/src/components/OcPageHeader.vue +34 -34
- package/src/components/OcPagination.vue +33 -33
- package/src/components/OcPasswordInput.vue +27 -27
- package/src/components/OcPasswordRequirements.vue +28 -28
- package/src/components/OcPhoneInput.vue +72 -72
- package/src/components/OcPopover.vue +25 -25
- package/src/components/OcPriceCard.vue +69 -69
- package/src/components/OcProductMark.vue +31 -31
- package/src/components/OcProgress.vue +21 -21
- package/src/components/OcProjectCard.vue +36 -36
- package/src/components/OcProse.vue +57 -57
- package/src/components/OcRadio.vue +40 -40
- package/src/components/OcRadioGroup.vue +32 -32
- package/src/components/OcRecoveryCodes.vue +55 -55
- package/src/components/OcScrollArea.vue +15 -15
- package/src/components/OcSegmented.vue +28 -28
- package/src/components/OcSelect.vue +21 -21
- package/src/components/OcSeparator.vue +18 -18
- package/src/components/OcSeverityCard.vue +46 -46
- package/src/components/OcSheet.vue +49 -49
- package/src/components/OcSidebarCollapsible.vue +19 -19
- package/src/components/OcSidebarGroup.vue +11 -11
- package/src/components/OcSidebarMenu.vue +7 -7
- package/src/components/OcSidebarMenuButton.vue +31 -31
- package/src/components/OcSidebarSeparator.vue +7 -7
- package/src/components/OcSkeleton.vue +8 -8
- package/src/components/OcSlider.vue +27 -27
- package/src/components/OcSortableRow.vue +40 -40
- package/src/components/OcSpinner.vue +13 -13
- package/src/components/OcStackCard.vue +24 -24
- package/src/components/OcStackList.vue +31 -31
- package/src/components/OcStatCard.vue +39 -39
- package/src/components/OcStepper.vue +34 -34
- package/src/components/OcSwitch.vue +15 -15
- package/src/components/OcTable.vue +115 -115
- package/src/components/OcTableActions.vue +26 -26
- package/src/components/OcTableCompact.vue +46 -46
- package/src/components/OcTabs.vue +37 -37
- package/src/components/OcTagInput.vue +50 -50
- package/src/components/OcTerminalLog.vue +50 -50
- package/src/components/OcTextarea.vue +9 -9
- package/src/components/OcThemeSwitch.vue +31 -31
- package/src/components/OcTimeInput.vue +50 -50
- package/src/components/OcTimeline.vue +36 -36
- package/src/components/OcToast.vue +31 -31
- package/src/components/OcToastContainer.vue +21 -21
- package/src/components/OcToggle.vue +63 -63
- package/src/components/OcToggleField.vue +28 -28
- package/src/components/OcToggleGroup.vue +40 -40
- package/src/components/OcTooltip.vue +8 -8
- package/src/components/OcTopProgress.vue +54 -54
- package/src/components/OcTree.vue +70 -70
- package/src/components/OcUpgradeBanner.vue +42 -42
- package/src/components/OcUser.vue +25 -25
- package/src/components/OcVersionBanner.vue +34 -34
- package/src/composables/chartLine.ts +28 -28
- package/src/composables/keys.ts +22 -22
- package/src/composables/useAttrsSplit.ts +16 -16
- package/src/composables/useDialog.ts +21 -21
- package/src/composables/useToast.ts +66 -66
- package/src/composables/useUid.ts +12 -12
|
@@ -1,144 +1,144 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcDataTable.vue | Map: props -> markup (toolbar, table, footer) -> bind the list-table script on mount -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Korrekt / OmniaDesk list table. Same markup as packages/ui/snippets/data-table.html.
|
|
5
|
-
* Search, sort, pages, columns, resize, drag and bulk actions come from data-table.js.
|
|
6
|
-
* Listen on the root for oc-datatable-bulk, oc-datatable-reorder, oc-datatable-search, oc-datatable-import.
|
|
7
|
-
*/
|
|
8
|
-
import { onMounted, useTemplateRef } from 'vue'
|
|
9
|
-
import { bindDataTable } from '../../../ui/src/data-table.js'
|
|
10
|
-
|
|
11
|
-
export interface DataTableColumn {
|
|
12
|
-
key: string
|
|
13
|
-
header: string
|
|
14
|
-
numeric?: boolean
|
|
15
|
-
sortable?: boolean
|
|
16
|
-
hideable?: boolean
|
|
17
|
-
defaultHidden?: boolean
|
|
18
|
-
}
|
|
19
|
-
export interface DataTableRow {
|
|
20
|
-
key: string
|
|
21
|
-
status?: 'active' | 'deleted' | 'archived'
|
|
22
|
-
cells: Record<string, string>
|
|
23
|
-
values?: Record<string, string>
|
|
24
|
-
href?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const props = withDefaults(
|
|
28
|
-
defineProps<{
|
|
29
|
-
tableId?: string
|
|
30
|
-
title?: string
|
|
31
|
-
columns: DataTableColumn[]
|
|
32
|
-
rows: DataTableRow[]
|
|
33
|
-
pageSize?: number
|
|
34
|
-
searchPlaceholder?: string
|
|
35
|
-
selectable?: boolean
|
|
36
|
-
draggable?: boolean
|
|
37
|
-
filters?: boolean
|
|
38
|
-
live?: boolean
|
|
39
|
-
}>(),
|
|
40
|
-
{ title: 'List', pageSize: 15, searchPlaceholder: 'Search...', selectable: true, draggable: true, filters: true, live: false },
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
const root = useTemplateRef<HTMLElement>('root')
|
|
44
|
-
const sizes = [...new Set([10, 15, 20, 50, 100, props.pageSize])].sort((a, b) => a - b)
|
|
45
|
-
const lead = (props.selectable ? 1 : 0) + (props.draggable ? 1 : 0)
|
|
46
|
-
const id = props.tableId || `oc-dt-${Math.random().toString(36).slice(2, 8)}`
|
|
47
|
-
|
|
48
|
-
onMounted(() => {
|
|
49
|
-
if (root.value) bindDataTable(root.value)
|
|
50
|
-
})
|
|
51
|
-
</script>
|
|
52
|
-
|
|
53
|
-
<template>
|
|
54
|
-
<div ref="root" class="oc-data-table oc-column-menu-scope" :id="id" :data-page-size="pageSize" :data-oc-live="live ? 'true' : undefined">
|
|
55
|
-
<div class="oc-data-table-toolbar">
|
|
56
|
-
<div class="oc-data-table-heading">
|
|
57
|
-
<span class="oc-data-table-title">{{ title }}</span>
|
|
58
|
-
<button class="oc-btn oc-btn--outline oc-btn--icon oc-btn--sm" type="button" :popovertarget="`${id}-menu`" aria-label="Table actions">⚙</button>
|
|
59
|
-
<div class="oc-dropdown-menu" :id="`${id}-menu`" popover>
|
|
60
|
-
<button class="oc-dropdown-item" type="button" data-oc-export>Export CSV</button>
|
|
61
|
-
<button class="oc-dropdown-item" type="button" data-oc-import>Import CSV</button>
|
|
62
|
-
<input class="oc-data-table-file" type="file" accept=".csv,text/csv" data-oc-file>
|
|
63
|
-
<div class="oc-dropdown-separator"></div>
|
|
64
|
-
<button class="oc-dropdown-item" type="button" data-oc-density="comfortable">Comfortable rows</button>
|
|
65
|
-
<button class="oc-dropdown-item" type="button" data-oc-density="compact">Compact rows</button>
|
|
66
|
-
<button class="oc-dropdown-item" type="button" data-oc-reset-widths>Reset column widths</button>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
<input class="oc-input oc-data-table-search" type="search" :placeholder="searchPlaceholder" :aria-label="`Search ${title}`">
|
|
70
|
-
<div v-if="filters" class="oc-filter-pills" role="group" aria-label="Trash filter">
|
|
71
|
-
<button class="oc-filter-pill" type="button" data-oc-filter="active" aria-current="true">Active only</button>
|
|
72
|
-
<button class="oc-filter-pill" type="button" data-oc-filter="all">Including trash</button>
|
|
73
|
-
<button class="oc-filter-pill" type="button" data-oc-filter="deleted">Trash only</button>
|
|
74
|
-
<button class="oc-filter-pill" type="button" data-oc-filter="archived">Archive only</button>
|
|
75
|
-
</div>
|
|
76
|
-
<div class="oc-data-table-view" role="group" aria-label="View">
|
|
77
|
-
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-view="table" aria-pressed="true">Table</button>
|
|
78
|
-
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-view="grid">Cards</button>
|
|
79
|
-
</div>
|
|
80
|
-
<p class="oc-data-table-sort-summary" data-oc-sort-summary role="status"></p>
|
|
81
|
-
</div>
|
|
82
|
-
<div class="oc-table-toolbar">
|
|
83
|
-
<span class="oc-table-toolbar-spacer"></span>
|
|
84
|
-
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" :popovertarget="`${id}-columns`">Columns</button>
|
|
85
|
-
</div>
|
|
86
|
-
<div class="oc-column-menu" :id="`${id}-columns`" popover>
|
|
87
|
-
<p class="oc-column-menu-title">Visible columns</p>
|
|
88
|
-
<form class="oc-column-menu-list">
|
|
89
|
-
<template v-for="(col, index) in columns" :key="col.key">
|
|
90
|
-
<label v-if="col.hideable !== false" class="oc-column-menu-item">
|
|
91
|
-
<input type="checkbox" :data-col="lead + index + 1" :checked="!col.defaultHidden"> {{ col.header }}
|
|
92
|
-
</label>
|
|
93
|
-
</template>
|
|
94
|
-
</form>
|
|
95
|
-
</div>
|
|
96
|
-
<div class="oc-data-table-bulk" hidden>
|
|
97
|
-
<span data-oc-selected-count>0 selected</span>
|
|
98
|
-
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-bulk="export">Export selection</button>
|
|
99
|
-
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-bulk="duplicate">Duplicate</button>
|
|
100
|
-
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-bulk="archive">Move to trash</button>
|
|
101
|
-
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-bulk="restore">Restore</button>
|
|
102
|
-
<button class="oc-btn oc-btn--destructive oc-btn--sm" type="button" data-oc-bulk="delete">Delete permanently</button>
|
|
103
|
-
<button class="oc-btn oc-btn--ghost oc-btn--sm" type="button" data-oc-bulk="clear">Clear</button>
|
|
104
|
-
</div>
|
|
105
|
-
<div class="oc-data-table-frame">
|
|
106
|
-
<div class="oc-data-table-scroll">
|
|
107
|
-
<table class="oc-table oc-data-table-table">
|
|
108
|
-
<thead>
|
|
109
|
-
<tr>
|
|
110
|
-
<th v-if="selectable" data-oc-col="select"><input class="oc-checkbox" type="checkbox" data-oc-select-all aria-label="Select all"></th>
|
|
111
|
-
<th v-if="draggable" data-oc-col="order"><span class="oc-visually-hidden">Order</span></th>
|
|
112
|
-
<th v-for="col in columns" :key="col.key" :data-oc-col="col.key" :data-oc-sort="col.numeric ? 'numeric' : 'text'" :data-numeric="col.numeric ? '' : undefined">
|
|
113
|
-
<span class="oc-data-table-th">
|
|
114
|
-
<button v-if="col.sortable !== false" class="oc-table-sort" type="button">{{ col.header }}</button>
|
|
115
|
-
<template v-else>{{ col.header }}</template>
|
|
116
|
-
<span class="oc-data-table-resize" role="separator" aria-orientation="vertical" :aria-label="`Resize ${col.header}`"></span>
|
|
117
|
-
</span>
|
|
118
|
-
</th>
|
|
119
|
-
<th data-oc-col="actions"><span class="oc-visually-hidden">Actions</span></th>
|
|
120
|
-
</tr>
|
|
121
|
-
</thead>
|
|
122
|
-
<tbody>
|
|
123
|
-
<tr v-for="row in rows" :key="row.key" data-oc-row :data-oc-key="row.key" :data-oc-status="row.status || 'active'" :data-oc-href="row.href">
|
|
124
|
-
<td v-if="selectable" data-oc-col="select"><input class="oc-checkbox" type="checkbox" data-oc-select :aria-label="`Select ${row.cells.name || row.key}`"></td>
|
|
125
|
-
<td v-if="draggable" data-oc-col="order" class="oc-sortable-row-grip-cell"><button class="oc-sortable-row-grip" type="button" :aria-label="`Move ${row.cells.name || row.key}`"></button></td>
|
|
126
|
-
<td v-for="col in columns" :key="col.key" :data-oc-col="col.key" :data-numeric="col.numeric ? '' : undefined" :data-oc-value="row.values?.[col.key]">{{ row.cells[col.key] ?? '' }}</td>
|
|
127
|
-
<td data-oc-col="actions" class="oc-table-actions"><button class="oc-table-row-action" type="button">Edit</button></td>
|
|
128
|
-
</tr>
|
|
129
|
-
</tbody>
|
|
130
|
-
</table>
|
|
131
|
-
</div>
|
|
132
|
-
<p class="oc-data-table-empty" hidden>No results</p>
|
|
133
|
-
</div>
|
|
134
|
-
<div class="oc-data-table-footer">
|
|
135
|
-
<p class="oc-data-table-summary" data-oc-summary></p>
|
|
136
|
-
<label class="oc-data-table-per-page">Rows per page
|
|
137
|
-
<select class="oc-select" data-oc-page-size aria-label="Rows per page">
|
|
138
|
-
<option v-for="n in sizes" :key="n" :value="n" :selected="n === pageSize">{{ n }}</option>
|
|
139
|
-
</select>
|
|
140
|
-
</label>
|
|
141
|
-
<nav class="oc-data-table-pager" data-oc-pager aria-label="Pagination"></nav>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcDataTable.vue | Map: props -> markup (toolbar, table, footer) -> bind the list-table script on mount -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Korrekt / OmniaDesk list table. Same markup as packages/ui/snippets/data-table.html.
|
|
5
|
+
* Search, sort, pages, columns, resize, drag and bulk actions come from data-table.js.
|
|
6
|
+
* Listen on the root for oc-datatable-bulk, oc-datatable-reorder, oc-datatable-search, oc-datatable-import.
|
|
7
|
+
*/
|
|
8
|
+
import { onMounted, useTemplateRef } from 'vue'
|
|
9
|
+
import { bindDataTable } from '../../../ui/src/data-table.js'
|
|
10
|
+
|
|
11
|
+
export interface DataTableColumn {
|
|
12
|
+
key: string
|
|
13
|
+
header: string
|
|
14
|
+
numeric?: boolean
|
|
15
|
+
sortable?: boolean
|
|
16
|
+
hideable?: boolean
|
|
17
|
+
defaultHidden?: boolean
|
|
18
|
+
}
|
|
19
|
+
export interface DataTableRow {
|
|
20
|
+
key: string
|
|
21
|
+
status?: 'active' | 'deleted' | 'archived'
|
|
22
|
+
cells: Record<string, string>
|
|
23
|
+
values?: Record<string, string>
|
|
24
|
+
href?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const props = withDefaults(
|
|
28
|
+
defineProps<{
|
|
29
|
+
tableId?: string
|
|
30
|
+
title?: string
|
|
31
|
+
columns: DataTableColumn[]
|
|
32
|
+
rows: DataTableRow[]
|
|
33
|
+
pageSize?: number
|
|
34
|
+
searchPlaceholder?: string
|
|
35
|
+
selectable?: boolean
|
|
36
|
+
draggable?: boolean
|
|
37
|
+
filters?: boolean
|
|
38
|
+
live?: boolean
|
|
39
|
+
}>(),
|
|
40
|
+
{ title: 'List', pageSize: 15, searchPlaceholder: 'Search...', selectable: true, draggable: true, filters: true, live: false },
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const root = useTemplateRef<HTMLElement>('root')
|
|
44
|
+
const sizes = [...new Set([10, 15, 20, 50, 100, props.pageSize])].sort((a, b) => a - b)
|
|
45
|
+
const lead = (props.selectable ? 1 : 0) + (props.draggable ? 1 : 0)
|
|
46
|
+
const id = props.tableId || `oc-dt-${Math.random().toString(36).slice(2, 8)}`
|
|
47
|
+
|
|
48
|
+
onMounted(() => {
|
|
49
|
+
if (root.value) bindDataTable(root.value)
|
|
50
|
+
})
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<div ref="root" class="oc-data-table oc-column-menu-scope" :id="id" :data-page-size="pageSize" :data-oc-live="live ? 'true' : undefined">
|
|
55
|
+
<div class="oc-data-table-toolbar">
|
|
56
|
+
<div class="oc-data-table-heading">
|
|
57
|
+
<span class="oc-data-table-title">{{ title }}</span>
|
|
58
|
+
<button class="oc-btn oc-btn--outline oc-btn--icon oc-btn--sm" type="button" :popovertarget="`${id}-menu`" aria-label="Table actions">⚙</button>
|
|
59
|
+
<div class="oc-dropdown-menu" :id="`${id}-menu`" popover>
|
|
60
|
+
<button class="oc-dropdown-item" type="button" data-oc-export>Export CSV</button>
|
|
61
|
+
<button class="oc-dropdown-item" type="button" data-oc-import>Import CSV</button>
|
|
62
|
+
<input class="oc-data-table-file" type="file" accept=".csv,text/csv" data-oc-file>
|
|
63
|
+
<div class="oc-dropdown-separator"></div>
|
|
64
|
+
<button class="oc-dropdown-item" type="button" data-oc-density="comfortable">Comfortable rows</button>
|
|
65
|
+
<button class="oc-dropdown-item" type="button" data-oc-density="compact">Compact rows</button>
|
|
66
|
+
<button class="oc-dropdown-item" type="button" data-oc-reset-widths>Reset column widths</button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<input class="oc-input oc-data-table-search" type="search" :placeholder="searchPlaceholder" :aria-label="`Search ${title}`">
|
|
70
|
+
<div v-if="filters" class="oc-filter-pills" role="group" aria-label="Trash filter">
|
|
71
|
+
<button class="oc-filter-pill" type="button" data-oc-filter="active" aria-current="true">Active only</button>
|
|
72
|
+
<button class="oc-filter-pill" type="button" data-oc-filter="all">Including trash</button>
|
|
73
|
+
<button class="oc-filter-pill" type="button" data-oc-filter="deleted">Trash only</button>
|
|
74
|
+
<button class="oc-filter-pill" type="button" data-oc-filter="archived">Archive only</button>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="oc-data-table-view" role="group" aria-label="View">
|
|
77
|
+
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-view="table" aria-pressed="true">Table</button>
|
|
78
|
+
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-view="grid">Cards</button>
|
|
79
|
+
</div>
|
|
80
|
+
<p class="oc-data-table-sort-summary" data-oc-sort-summary role="status"></p>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="oc-table-toolbar">
|
|
83
|
+
<span class="oc-table-toolbar-spacer"></span>
|
|
84
|
+
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" :popovertarget="`${id}-columns`">Columns</button>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="oc-column-menu" :id="`${id}-columns`" popover>
|
|
87
|
+
<p class="oc-column-menu-title">Visible columns</p>
|
|
88
|
+
<form class="oc-column-menu-list">
|
|
89
|
+
<template v-for="(col, index) in columns" :key="col.key">
|
|
90
|
+
<label v-if="col.hideable !== false" class="oc-column-menu-item">
|
|
91
|
+
<input type="checkbox" :data-col="lead + index + 1" :checked="!col.defaultHidden"> {{ col.header }}
|
|
92
|
+
</label>
|
|
93
|
+
</template>
|
|
94
|
+
</form>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="oc-data-table-bulk" hidden>
|
|
97
|
+
<span data-oc-selected-count>0 selected</span>
|
|
98
|
+
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-bulk="export">Export selection</button>
|
|
99
|
+
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-bulk="duplicate">Duplicate</button>
|
|
100
|
+
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-bulk="archive">Move to trash</button>
|
|
101
|
+
<button class="oc-btn oc-btn--outline oc-btn--sm" type="button" data-oc-bulk="restore">Restore</button>
|
|
102
|
+
<button class="oc-btn oc-btn--destructive oc-btn--sm" type="button" data-oc-bulk="delete">Delete permanently</button>
|
|
103
|
+
<button class="oc-btn oc-btn--ghost oc-btn--sm" type="button" data-oc-bulk="clear">Clear</button>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="oc-data-table-frame">
|
|
106
|
+
<div class="oc-data-table-scroll">
|
|
107
|
+
<table class="oc-table oc-data-table-table">
|
|
108
|
+
<thead>
|
|
109
|
+
<tr>
|
|
110
|
+
<th v-if="selectable" data-oc-col="select"><input class="oc-checkbox" type="checkbox" data-oc-select-all aria-label="Select all"></th>
|
|
111
|
+
<th v-if="draggable" data-oc-col="order"><span class="oc-visually-hidden">Order</span></th>
|
|
112
|
+
<th v-for="col in columns" :key="col.key" :data-oc-col="col.key" :data-oc-sort="col.numeric ? 'numeric' : 'text'" :data-numeric="col.numeric ? '' : undefined">
|
|
113
|
+
<span class="oc-data-table-th">
|
|
114
|
+
<button v-if="col.sortable !== false" class="oc-table-sort" type="button">{{ col.header }}</button>
|
|
115
|
+
<template v-else>{{ col.header }}</template>
|
|
116
|
+
<span class="oc-data-table-resize" role="separator" aria-orientation="vertical" :aria-label="`Resize ${col.header}`"></span>
|
|
117
|
+
</span>
|
|
118
|
+
</th>
|
|
119
|
+
<th data-oc-col="actions"><span class="oc-visually-hidden">Actions</span></th>
|
|
120
|
+
</tr>
|
|
121
|
+
</thead>
|
|
122
|
+
<tbody>
|
|
123
|
+
<tr v-for="row in rows" :key="row.key" data-oc-row :data-oc-key="row.key" :data-oc-status="row.status || 'active'" :data-oc-href="row.href">
|
|
124
|
+
<td v-if="selectable" data-oc-col="select"><input class="oc-checkbox" type="checkbox" data-oc-select :aria-label="`Select ${row.cells.name || row.key}`"></td>
|
|
125
|
+
<td v-if="draggable" data-oc-col="order" class="oc-sortable-row-grip-cell"><button class="oc-sortable-row-grip" type="button" :aria-label="`Move ${row.cells.name || row.key}`"></button></td>
|
|
126
|
+
<td v-for="col in columns" :key="col.key" :data-oc-col="col.key" :data-numeric="col.numeric ? '' : undefined" :data-oc-value="row.values?.[col.key]">{{ row.cells[col.key] ?? '' }}</td>
|
|
127
|
+
<td data-oc-col="actions" class="oc-table-actions"><button class="oc-table-row-action" type="button">Edit</button></td>
|
|
128
|
+
</tr>
|
|
129
|
+
</tbody>
|
|
130
|
+
</table>
|
|
131
|
+
</div>
|
|
132
|
+
<p class="oc-data-table-empty" hidden>No results</p>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="oc-data-table-footer">
|
|
135
|
+
<p class="oc-data-table-summary" data-oc-summary></p>
|
|
136
|
+
<label class="oc-data-table-per-page">Rows per page
|
|
137
|
+
<select class="oc-select" data-oc-page-size aria-label="Rows per page">
|
|
138
|
+
<option v-for="n in sizes" :key="n" :value="n" :selected="n === pageSize">{{ n }}</option>
|
|
139
|
+
</select>
|
|
140
|
+
</label>
|
|
141
|
+
<nav class="oc-data-table-pager" data-oc-pager aria-label="Pagination"></nav>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</template>
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Label / value rows in a panel (dl). Pass `rows`, or put your own .oc-dl-row elements in the default slot.
|
|
4
|
-
* Slots: title, description, default (rows), footer.
|
|
5
|
-
*/
|
|
6
|
-
export interface DefinitionRow {
|
|
7
|
-
term: string
|
|
8
|
-
detail: string
|
|
9
|
-
}
|
|
10
|
-
withDefaults(
|
|
11
|
-
defineProps<{
|
|
12
|
-
title?: string
|
|
13
|
-
description?: string
|
|
14
|
-
rows?: DefinitionRow[]
|
|
15
|
-
label?: string
|
|
16
|
-
}>(),
|
|
17
|
-
{ rows: () => [] },
|
|
18
|
-
)
|
|
19
|
-
</script>
|
|
20
|
-
|
|
21
|
-
<template>
|
|
22
|
-
<div class="oc-dl-panel" role="region" :aria-label="label ?? title">
|
|
23
|
-
<h3 v-if="title || $slots.title" class="oc-dl-title"><slot name="title">{{ title }}</slot></h3>
|
|
24
|
-
<p v-if="description || $slots.description" class="oc-dl-description"><slot name="description">{{ description }}</slot></p>
|
|
25
|
-
<dl class="oc-dl">
|
|
26
|
-
<slot>
|
|
27
|
-
<div v-for="(row, i) in rows" :key="i" class="oc-dl-row">
|
|
28
|
-
<dt class="oc-dl-term">{{ row.term }}</dt>
|
|
29
|
-
<dd class="oc-dl-detail">{{ row.detail }}</dd>
|
|
30
|
-
</div>
|
|
31
|
-
</slot>
|
|
32
|
-
</dl>
|
|
33
|
-
<div v-if="$slots.footer" class="oc-dl-footer"><slot name="footer" /></div>
|
|
34
|
-
</div>
|
|
35
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Label / value rows in a panel (dl). Pass `rows`, or put your own .oc-dl-row elements in the default slot.
|
|
4
|
+
* Slots: title, description, default (rows), footer.
|
|
5
|
+
*/
|
|
6
|
+
export interface DefinitionRow {
|
|
7
|
+
term: string
|
|
8
|
+
detail: string
|
|
9
|
+
}
|
|
10
|
+
withDefaults(
|
|
11
|
+
defineProps<{
|
|
12
|
+
title?: string
|
|
13
|
+
description?: string
|
|
14
|
+
rows?: DefinitionRow[]
|
|
15
|
+
label?: string
|
|
16
|
+
}>(),
|
|
17
|
+
{ rows: () => [] },
|
|
18
|
+
)
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<div class="oc-dl-panel" role="region" :aria-label="label ?? title">
|
|
23
|
+
<h3 v-if="title || $slots.title" class="oc-dl-title"><slot name="title">{{ title }}</slot></h3>
|
|
24
|
+
<p v-if="description || $slots.description" class="oc-dl-description"><slot name="description">{{ description }}</slot></p>
|
|
25
|
+
<dl class="oc-dl">
|
|
26
|
+
<slot>
|
|
27
|
+
<div v-for="(row, i) in rows" :key="i" class="oc-dl-row">
|
|
28
|
+
<dt class="oc-dl-term">{{ row.term }}</dt>
|
|
29
|
+
<dd class="oc-dl-detail">{{ row.detail }}</dd>
|
|
30
|
+
</div>
|
|
31
|
+
</slot>
|
|
32
|
+
</dl>
|
|
33
|
+
<div v-if="$slots.footer" class="oc-dl-footer"><slot name="footer" /></div>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Dropdown menu on the popover attribute + anchor positioning. The trigger is the `trigger` slot
|
|
4
|
-
* (scoped: { id }) or a default outline button showing `label`. Default slot = menu content
|
|
5
|
-
* (OcDropdownLabel / OcDropdownItem / OcDropdownSeparator). Attributes land on the menu element.
|
|
6
|
-
*/
|
|
7
|
-
import { useAttrs } from 'vue'
|
|
8
|
-
import { useUid } from '../composables/useUid'
|
|
9
|
-
|
|
10
|
-
defineOptions({ inheritAttrs: false })
|
|
11
|
-
defineProps<{ label?: string }>()
|
|
12
|
-
const attrs = useAttrs()
|
|
13
|
-
const id = (attrs.id as string | undefined) ?? useUid('oc-menu')
|
|
14
|
-
</script>
|
|
15
|
-
|
|
16
|
-
<template>
|
|
17
|
-
<slot name="trigger" :id="id">
|
|
18
|
-
<button class="oc-btn oc-btn--outline" type="button" :popovertarget="id">{{ label }}</button>
|
|
19
|
-
</slot>
|
|
20
|
-
<div v-bind="attrs" :id="id" class="oc-dropdown-menu" popover>
|
|
21
|
-
<slot />
|
|
22
|
-
</div>
|
|
23
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Dropdown menu on the popover attribute + anchor positioning. The trigger is the `trigger` slot
|
|
4
|
+
* (scoped: { id }) or a default outline button showing `label`. Default slot = menu content
|
|
5
|
+
* (OcDropdownLabel / OcDropdownItem / OcDropdownSeparator). Attributes land on the menu element.
|
|
6
|
+
*/
|
|
7
|
+
import { useAttrs } from 'vue'
|
|
8
|
+
import { useUid } from '../composables/useUid'
|
|
9
|
+
|
|
10
|
+
defineOptions({ inheritAttrs: false })
|
|
11
|
+
defineProps<{ label?: string }>()
|
|
12
|
+
const attrs = useAttrs()
|
|
13
|
+
const id = (attrs.id as string | undefined) ?? useUid('oc-menu')
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<slot name="trigger" :id="id">
|
|
18
|
+
<button class="oc-btn oc-btn--outline" type="button" :popovertarget="id">{{ label }}</button>
|
|
19
|
+
</slot>
|
|
20
|
+
<div v-bind="attrs" :id="id" class="oc-dropdown-menu" popover>
|
|
21
|
+
<slot />
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/** Menu item: <a> with `href`, otherwise <button>. */
|
|
3
|
-
defineProps<{ href?: string }>()
|
|
4
|
-
</script>
|
|
5
|
-
|
|
6
|
-
<template>
|
|
7
|
-
<a v-if="href" class="oc-dropdown-item" :href="href"><slot /></a>
|
|
8
|
-
<button v-else class="oc-dropdown-item" type="button"><slot /></button>
|
|
9
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Menu item: <a> with `href`, otherwise <button>. */
|
|
3
|
+
defineProps<{ href?: string }>()
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<a v-if="href" class="oc-dropdown-item" :href="href"><slot /></a>
|
|
8
|
+
<button v-else class="oc-dropdown-item" type="button"><slot /></button>
|
|
9
|
+
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/** Heading inside a dropdown menu. */
|
|
3
|
-
</script>
|
|
4
|
-
|
|
5
|
-
<template>
|
|
6
|
-
<div class="oc-dropdown-label"><slot /></div>
|
|
7
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Heading inside a dropdown menu. */
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div class="oc-dropdown-label"><slot /></div>
|
|
7
|
+
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/** Divider inside a dropdown menu. */
|
|
3
|
-
</script>
|
|
4
|
-
|
|
5
|
-
<template>
|
|
6
|
-
<div class="oc-dropdown-separator"></div>
|
|
7
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/** Divider inside a dropdown menu. */
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div class="oc-dropdown-separator"></div>
|
|
7
|
+
</template>
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* File upload target: a label around a real file input. v-model: File[].
|
|
4
|
-
* .is-dragover is toggled while dragging over. Slots: icon, default (text), hint.
|
|
5
|
-
*/
|
|
6
|
-
import { ref } from 'vue'
|
|
7
|
-
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
8
|
-
|
|
9
|
-
defineOptions({ inheritAttrs: false })
|
|
10
|
-
defineProps<{ accept?: string; multiple?: boolean; disabled?: boolean; hint?: string }>()
|
|
11
|
-
const model = defineModel<File[]>({ default: () => [] })
|
|
12
|
-
const { root, control } = useAttrsSplit()
|
|
13
|
-
const dragover = ref(false)
|
|
14
|
-
|
|
15
|
-
function onChange(e: Event) {
|
|
16
|
-
model.value = Array.from((e.target as HTMLInputElement).files ?? [])
|
|
17
|
-
}
|
|
18
|
-
function onDrop() {
|
|
19
|
-
dragover.value = false
|
|
20
|
-
}
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<template>
|
|
24
|
-
<label
|
|
25
|
-
v-bind="root"
|
|
26
|
-
:class="['oc-dropzone', dragover && 'is-dragover']"
|
|
27
|
-
@dragover.prevent="dragover = true"
|
|
28
|
-
@dragleave="dragover = false"
|
|
29
|
-
@drop="onDrop"
|
|
30
|
-
>
|
|
31
|
-
<input v-bind="control" type="file" :accept="accept" :multiple="multiple" :disabled="disabled" @change="onChange" />
|
|
32
|
-
<span v-if="$slots.icon" class="oc-dropzone-icon" aria-hidden="true"><slot name="icon" /></span>
|
|
33
|
-
<span><slot /></span>
|
|
34
|
-
<span v-if="hint || $slots.hint" class="oc-dropzone-hint"><slot name="hint">{{ hint }}</slot></span>
|
|
35
|
-
</label>
|
|
36
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* File upload target: a label around a real file input. v-model: File[].
|
|
4
|
+
* .is-dragover is toggled while dragging over. Slots: icon, default (text), hint.
|
|
5
|
+
*/
|
|
6
|
+
import { ref } from 'vue'
|
|
7
|
+
import { useAttrsSplit } from '../composables/useAttrsSplit'
|
|
8
|
+
|
|
9
|
+
defineOptions({ inheritAttrs: false })
|
|
10
|
+
defineProps<{ accept?: string; multiple?: boolean; disabled?: boolean; hint?: string }>()
|
|
11
|
+
const model = defineModel<File[]>({ default: () => [] })
|
|
12
|
+
const { root, control } = useAttrsSplit()
|
|
13
|
+
const dragover = ref(false)
|
|
14
|
+
|
|
15
|
+
function onChange(e: Event) {
|
|
16
|
+
model.value = Array.from((e.target as HTMLInputElement).files ?? [])
|
|
17
|
+
}
|
|
18
|
+
function onDrop() {
|
|
19
|
+
dragover.value = false
|
|
20
|
+
}
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<label
|
|
25
|
+
v-bind="root"
|
|
26
|
+
:class="['oc-dropzone', dragover && 'is-dragover']"
|
|
27
|
+
@dragover.prevent="dragover = true"
|
|
28
|
+
@dragleave="dragover = false"
|
|
29
|
+
@drop="onDrop"
|
|
30
|
+
>
|
|
31
|
+
<input v-bind="control" type="file" :accept="accept" :multiple="multiple" :disabled="disabled" @change="onChange" />
|
|
32
|
+
<span v-if="$slots.icon" class="oc-dropzone-icon" aria-hidden="true"><slot name="icon" /></span>
|
|
33
|
+
<span><slot /></span>
|
|
34
|
+
<span v-if="hint || $slots.hint" class="oc-dropzone-hint"><slot name="hint">{{ hint }}</slot></span>
|
|
35
|
+
</label>
|
|
36
|
+
</template>
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcEmojiPicker.vue | Map: props + emit -> trigger button -> popover panel -> groups (label + grid) -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Emoji picker on the popover attribute (same markup as snippets/emoji-picker.html).
|
|
5
|
-
* Props: groups ({ label?, emojis[] }[]), inline (always visible, no popover), triggerLabel, label.
|
|
6
|
-
* Emits: select(emoji) and then closes the popover. Slot: trigger (button content, default 🙂).
|
|
7
|
-
* Classes: .oc-emoji-picker(--inline), __trigger, __label, __grid, __item
|
|
8
|
-
*/
|
|
9
|
-
import { useUid } from '../composables/useUid'
|
|
10
|
-
|
|
11
|
-
withDefaults(
|
|
12
|
-
defineProps<{ groups?: { label?: string; emojis: string[] }[]; inline?: boolean; triggerLabel?: string; label?: string }>(),
|
|
13
|
-
{
|
|
14
|
-
groups: () => [
|
|
15
|
-
{ label: 'Smileys', emojis: ['😀', '😃', '😄', '😁', '😂', '🙂', '😉', '😍', '🤔', '😎', '😢', '😴', '😯', '🥳', '😤', '🤗'] },
|
|
16
|
-
{ label: 'Gestures', emojis: ['👍', '👎', '👏', '🙌', '👋', '🙏', '✌️', '💪'] },
|
|
17
|
-
{ label: 'Objects', emojis: ['❤️', '⭐', '🔥', '🎉', '✅', '🚀', '💡', '☕'] },
|
|
18
|
-
],
|
|
19
|
-
triggerLabel: 'Insert emoji',
|
|
20
|
-
label: 'Emoji',
|
|
21
|
-
},
|
|
22
|
-
)
|
|
23
|
-
const emit = defineEmits<{ select: [emoji: string] }>()
|
|
24
|
-
const id = useUid('oc-emoji')
|
|
25
|
-
|
|
26
|
-
function pick(e: MouseEvent, emoji: string) {
|
|
27
|
-
emit('select', emoji)
|
|
28
|
-
const panel = (e.currentTarget as HTMLElement).closest('[popover]') as (HTMLElement & { hidePopover?: () => void }) | null
|
|
29
|
-
panel?.hidePopover?.()
|
|
30
|
-
}
|
|
31
|
-
</script>
|
|
32
|
-
|
|
33
|
-
<template>
|
|
34
|
-
<button v-if="!inline" type="button" class="oc-emoji-picker__trigger" :popovertarget="id" :aria-label="triggerLabel">
|
|
35
|
-
<slot name="trigger">🙂</slot>
|
|
36
|
-
</button>
|
|
37
|
-
<div :id="id" :class="['oc-emoji-picker', inline && 'oc-emoji-picker--inline']" :popover="inline ? undefined : ''" :role="inline ? 'group' : undefined" :aria-label="label">
|
|
38
|
-
<div v-for="(g, gi) in groups" :key="gi">
|
|
39
|
-
<p v-if="g.label" class="oc-emoji-picker__label">{{ g.label }}</p>
|
|
40
|
-
<div class="oc-emoji-picker__grid">
|
|
41
|
-
<button v-for="emoji in g.emojis" :key="emoji" type="button" class="oc-emoji-picker__item" @click="pick($event, emoji)">{{ emoji }}</button>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcEmojiPicker.vue | Map: props + emit -> trigger button -> popover panel -> groups (label + grid) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Emoji picker on the popover attribute (same markup as snippets/emoji-picker.html).
|
|
5
|
+
* Props: groups ({ label?, emojis[] }[]), inline (always visible, no popover), triggerLabel, label.
|
|
6
|
+
* Emits: select(emoji) and then closes the popover. Slot: trigger (button content, default 🙂).
|
|
7
|
+
* Classes: .oc-emoji-picker(--inline), __trigger, __label, __grid, __item
|
|
8
|
+
*/
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
withDefaults(
|
|
12
|
+
defineProps<{ groups?: { label?: string; emojis: string[] }[]; inline?: boolean; triggerLabel?: string; label?: string }>(),
|
|
13
|
+
{
|
|
14
|
+
groups: () => [
|
|
15
|
+
{ label: 'Smileys', emojis: ['😀', '😃', '😄', '😁', '😂', '🙂', '😉', '😍', '🤔', '😎', '😢', '😴', '😯', '🥳', '😤', '🤗'] },
|
|
16
|
+
{ label: 'Gestures', emojis: ['👍', '👎', '👏', '🙌', '👋', '🙏', '✌️', '💪'] },
|
|
17
|
+
{ label: 'Objects', emojis: ['❤️', '⭐', '🔥', '🎉', '✅', '🚀', '💡', '☕'] },
|
|
18
|
+
],
|
|
19
|
+
triggerLabel: 'Insert emoji',
|
|
20
|
+
label: 'Emoji',
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
const emit = defineEmits<{ select: [emoji: string] }>()
|
|
24
|
+
const id = useUid('oc-emoji')
|
|
25
|
+
|
|
26
|
+
function pick(e: MouseEvent, emoji: string) {
|
|
27
|
+
emit('select', emoji)
|
|
28
|
+
const panel = (e.currentTarget as HTMLElement).closest('[popover]') as (HTMLElement & { hidePopover?: () => void }) | null
|
|
29
|
+
panel?.hidePopover?.()
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<button v-if="!inline" type="button" class="oc-emoji-picker__trigger" :popovertarget="id" :aria-label="triggerLabel">
|
|
35
|
+
<slot name="trigger">🙂</slot>
|
|
36
|
+
</button>
|
|
37
|
+
<div :id="id" :class="['oc-emoji-picker', inline && 'oc-emoji-picker--inline']" :popover="inline ? undefined : ''" :role="inline ? 'group' : undefined" :aria-label="label">
|
|
38
|
+
<div v-for="(g, gi) in groups" :key="gi">
|
|
39
|
+
<p v-if="g.label" class="oc-emoji-picker__label">{{ g.label }}</p>
|
|
40
|
+
<div class="oc-emoji-picker__grid">
|
|
41
|
+
<button v-for="emoji in g.emojis" :key="emoji" type="button" class="oc-emoji-picker__item" @click="pick($event, emoji)">{{ emoji }}</button>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</template>
|