@birdycom/omniaui-vue 0.11.0 → 0.11.2
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,115 +1,115 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Data table: sortable header (v-model:sort), row selection (v-model:selected), row actions, empty state,
|
|
4
|
-
* density modifiers. Slots: `cell-<key>` (scoped: { row, value }), `actions` (scoped: { row }), `empty`.
|
|
5
|
-
* Classes: .oc-table-wrap > .oc-table(--striped/--compact/--borderless/--sticky), .oc-table-sort,
|
|
6
|
-
* .oc-table-actions, .oc-table-empty; aria-sort / aria-selected / data-numeric attributes.
|
|
7
|
-
*/
|
|
8
|
-
import { computed, useSlots } from 'vue'
|
|
9
|
-
|
|
10
|
-
export interface TableColumn {
|
|
11
|
-
key: string
|
|
12
|
-
label: string
|
|
13
|
-
sortable?: boolean
|
|
14
|
-
numeric?: boolean
|
|
15
|
-
width?: string
|
|
16
|
-
}
|
|
17
|
-
export interface TableSort {
|
|
18
|
-
key: string
|
|
19
|
-
direction: 'ascending' | 'descending'
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const props = withDefaults(
|
|
23
|
-
defineProps<{
|
|
24
|
-
columns: TableColumn[]
|
|
25
|
-
rows: Record<string, any>[]
|
|
26
|
-
rowKey?: string
|
|
27
|
-
caption?: string
|
|
28
|
-
striped?: boolean
|
|
29
|
-
compact?: boolean
|
|
30
|
-
borderless?: boolean
|
|
31
|
-
sticky?: boolean
|
|
32
|
-
selectable?: boolean
|
|
33
|
-
actionsLabel?: string
|
|
34
|
-
selectAllLabel?: string
|
|
35
|
-
}>(),
|
|
36
|
-
{ rowKey: 'id', actionsLabel: 'Actions', selectAllLabel: 'Select all' },
|
|
37
|
-
)
|
|
38
|
-
const selected = defineModel<(string | number)[]>('selected', { default: () => [] })
|
|
39
|
-
const sort = defineModel<TableSort | null>('sort', { default: null })
|
|
40
|
-
const slots = useSlots()
|
|
41
|
-
|
|
42
|
-
const hasActions = computed(() => !!slots.actions)
|
|
43
|
-
const colspan = computed(() => props.columns.length + (props.selectable ? 1 : 0) + (hasActions.value ? 1 : 0))
|
|
44
|
-
const allSelected = computed(() => props.rows.length > 0 && props.rows.every((r) => selected.value.includes(r[props.rowKey])))
|
|
45
|
-
|
|
46
|
-
function toggleAll(e: Event) {
|
|
47
|
-
selected.value = (e.target as HTMLInputElement).checked ? props.rows.map((r) => r[props.rowKey]) : []
|
|
48
|
-
}
|
|
49
|
-
function toggleRow(id: string | number, checked: boolean) {
|
|
50
|
-
selected.value = checked ? [...selected.value, id] : selected.value.filter((x) => x !== id)
|
|
51
|
-
}
|
|
52
|
-
function ariaSort(key: string) {
|
|
53
|
-
return sort.value?.key === key ? sort.value.direction : undefined
|
|
54
|
-
}
|
|
55
|
-
function toggleSort(e: Event, key: string) {
|
|
56
|
-
e.preventDefault()
|
|
57
|
-
const next = sort.value?.key === key && sort.value.direction === 'ascending' ? 'descending' : 'ascending'
|
|
58
|
-
sort.value = { key, direction: next }
|
|
59
|
-
}
|
|
60
|
-
function rowLabel(row: Record<string, any>) {
|
|
61
|
-
return `Select ${String(row[props.columns[0]?.key] ?? '')}`.trim()
|
|
62
|
-
}
|
|
63
|
-
</script>
|
|
64
|
-
|
|
65
|
-
<template>
|
|
66
|
-
<div class="oc-table-wrap">
|
|
67
|
-
<table
|
|
68
|
-
:class="['oc-table', striped && 'oc-table--striped', compact && 'oc-table--compact', borderless && 'oc-table--borderless', sticky && 'oc-table--sticky']"
|
|
69
|
-
>
|
|
70
|
-
<caption v-if="caption">{{ caption }}</caption>
|
|
71
|
-
<thead>
|
|
72
|
-
<tr>
|
|
73
|
-
<th v-if="selectable" style="width: 2.5rem">
|
|
74
|
-
<input class="oc-checkbox" type="checkbox" :checked="allSelected" :aria-label="selectAllLabel" @change="toggleAll" />
|
|
75
|
-
</th>
|
|
76
|
-
<th
|
|
77
|
-
v-for="col in columns"
|
|
78
|
-
:key="col.key"
|
|
79
|
-
:aria-sort="ariaSort(col.key)"
|
|
80
|
-
:data-numeric="col.numeric ? '' : undefined"
|
|
81
|
-
:style="col.width ? { width: col.width } : undefined"
|
|
82
|
-
>
|
|
83
|
-
<a v-if="col.sortable" class="oc-table-sort" :href="`?sort=${col.key}`" @click="toggleSort($event, col.key)">{{ col.label }}</a>
|
|
84
|
-
<template v-else>{{ col.label }}</template>
|
|
85
|
-
</th>
|
|
86
|
-
<th v-if="hasActions"><span class="oc-visually-hidden">{{ actionsLabel }}</span></th>
|
|
87
|
-
</tr>
|
|
88
|
-
</thead>
|
|
89
|
-
<tbody>
|
|
90
|
-
<tr
|
|
91
|
-
v-for="row in rows"
|
|
92
|
-
:key="row[rowKey]"
|
|
93
|
-
:aria-selected="selected.includes(row[rowKey]) ? 'true' : undefined"
|
|
94
|
-
>
|
|
95
|
-
<td v-if="selectable">
|
|
96
|
-
<input
|
|
97
|
-
class="oc-checkbox"
|
|
98
|
-
type="checkbox"
|
|
99
|
-
:checked="selected.includes(row[rowKey])"
|
|
100
|
-
:aria-label="rowLabel(row)"
|
|
101
|
-
@change="toggleRow(row[rowKey], ($event.target as HTMLInputElement).checked)"
|
|
102
|
-
/>
|
|
103
|
-
</td>
|
|
104
|
-
<td v-for="col in columns" :key="col.key" :data-numeric="col.numeric ? '' : undefined">
|
|
105
|
-
<slot :name="`cell-${col.key}`" :row="row" :value="row[col.key]">{{ row[col.key] }}</slot>
|
|
106
|
-
</td>
|
|
107
|
-
<td v-if="hasActions" class="oc-table-actions"><slot name="actions" :row="row" /></td>
|
|
108
|
-
</tr>
|
|
109
|
-
<tr v-if="rows.length === 0 && $slots.empty" class="oc-table-empty">
|
|
110
|
-
<td :colspan="colspan"><slot name="empty" :row="{}" /></td>
|
|
111
|
-
</tr>
|
|
112
|
-
</tbody>
|
|
113
|
-
</table>
|
|
114
|
-
</div>
|
|
115
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Data table: sortable header (v-model:sort), row selection (v-model:selected), row actions, empty state,
|
|
4
|
+
* density modifiers. Slots: `cell-<key>` (scoped: { row, value }), `actions` (scoped: { row }), `empty`.
|
|
5
|
+
* Classes: .oc-table-wrap > .oc-table(--striped/--compact/--borderless/--sticky), .oc-table-sort,
|
|
6
|
+
* .oc-table-actions, .oc-table-empty; aria-sort / aria-selected / data-numeric attributes.
|
|
7
|
+
*/
|
|
8
|
+
import { computed, useSlots } from 'vue'
|
|
9
|
+
|
|
10
|
+
export interface TableColumn {
|
|
11
|
+
key: string
|
|
12
|
+
label: string
|
|
13
|
+
sortable?: boolean
|
|
14
|
+
numeric?: boolean
|
|
15
|
+
width?: string
|
|
16
|
+
}
|
|
17
|
+
export interface TableSort {
|
|
18
|
+
key: string
|
|
19
|
+
direction: 'ascending' | 'descending'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const props = withDefaults(
|
|
23
|
+
defineProps<{
|
|
24
|
+
columns: TableColumn[]
|
|
25
|
+
rows: Record<string, any>[]
|
|
26
|
+
rowKey?: string
|
|
27
|
+
caption?: string
|
|
28
|
+
striped?: boolean
|
|
29
|
+
compact?: boolean
|
|
30
|
+
borderless?: boolean
|
|
31
|
+
sticky?: boolean
|
|
32
|
+
selectable?: boolean
|
|
33
|
+
actionsLabel?: string
|
|
34
|
+
selectAllLabel?: string
|
|
35
|
+
}>(),
|
|
36
|
+
{ rowKey: 'id', actionsLabel: 'Actions', selectAllLabel: 'Select all' },
|
|
37
|
+
)
|
|
38
|
+
const selected = defineModel<(string | number)[]>('selected', { default: () => [] })
|
|
39
|
+
const sort = defineModel<TableSort | null>('sort', { default: null })
|
|
40
|
+
const slots = useSlots()
|
|
41
|
+
|
|
42
|
+
const hasActions = computed(() => !!slots.actions)
|
|
43
|
+
const colspan = computed(() => props.columns.length + (props.selectable ? 1 : 0) + (hasActions.value ? 1 : 0))
|
|
44
|
+
const allSelected = computed(() => props.rows.length > 0 && props.rows.every((r) => selected.value.includes(r[props.rowKey])))
|
|
45
|
+
|
|
46
|
+
function toggleAll(e: Event) {
|
|
47
|
+
selected.value = (e.target as HTMLInputElement).checked ? props.rows.map((r) => r[props.rowKey]) : []
|
|
48
|
+
}
|
|
49
|
+
function toggleRow(id: string | number, checked: boolean) {
|
|
50
|
+
selected.value = checked ? [...selected.value, id] : selected.value.filter((x) => x !== id)
|
|
51
|
+
}
|
|
52
|
+
function ariaSort(key: string) {
|
|
53
|
+
return sort.value?.key === key ? sort.value.direction : undefined
|
|
54
|
+
}
|
|
55
|
+
function toggleSort(e: Event, key: string) {
|
|
56
|
+
e.preventDefault()
|
|
57
|
+
const next = sort.value?.key === key && sort.value.direction === 'ascending' ? 'descending' : 'ascending'
|
|
58
|
+
sort.value = { key, direction: next }
|
|
59
|
+
}
|
|
60
|
+
function rowLabel(row: Record<string, any>) {
|
|
61
|
+
return `Select ${String(row[props.columns[0]?.key] ?? '')}`.trim()
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<template>
|
|
66
|
+
<div class="oc-table-wrap">
|
|
67
|
+
<table
|
|
68
|
+
:class="['oc-table', striped && 'oc-table--striped', compact && 'oc-table--compact', borderless && 'oc-table--borderless', sticky && 'oc-table--sticky']"
|
|
69
|
+
>
|
|
70
|
+
<caption v-if="caption">{{ caption }}</caption>
|
|
71
|
+
<thead>
|
|
72
|
+
<tr>
|
|
73
|
+
<th v-if="selectable" style="width: 2.5rem">
|
|
74
|
+
<input class="oc-checkbox" type="checkbox" :checked="allSelected" :aria-label="selectAllLabel" @change="toggleAll" />
|
|
75
|
+
</th>
|
|
76
|
+
<th
|
|
77
|
+
v-for="col in columns"
|
|
78
|
+
:key="col.key"
|
|
79
|
+
:aria-sort="ariaSort(col.key)"
|
|
80
|
+
:data-numeric="col.numeric ? '' : undefined"
|
|
81
|
+
:style="col.width ? { width: col.width } : undefined"
|
|
82
|
+
>
|
|
83
|
+
<a v-if="col.sortable" class="oc-table-sort" :href="`?sort=${col.key}`" @click="toggleSort($event, col.key)">{{ col.label }}</a>
|
|
84
|
+
<template v-else>{{ col.label }}</template>
|
|
85
|
+
</th>
|
|
86
|
+
<th v-if="hasActions"><span class="oc-visually-hidden">{{ actionsLabel }}</span></th>
|
|
87
|
+
</tr>
|
|
88
|
+
</thead>
|
|
89
|
+
<tbody>
|
|
90
|
+
<tr
|
|
91
|
+
v-for="row in rows"
|
|
92
|
+
:key="row[rowKey]"
|
|
93
|
+
:aria-selected="selected.includes(row[rowKey]) ? 'true' : undefined"
|
|
94
|
+
>
|
|
95
|
+
<td v-if="selectable">
|
|
96
|
+
<input
|
|
97
|
+
class="oc-checkbox"
|
|
98
|
+
type="checkbox"
|
|
99
|
+
:checked="selected.includes(row[rowKey])"
|
|
100
|
+
:aria-label="rowLabel(row)"
|
|
101
|
+
@change="toggleRow(row[rowKey], ($event.target as HTMLInputElement).checked)"
|
|
102
|
+
/>
|
|
103
|
+
</td>
|
|
104
|
+
<td v-for="col in columns" :key="col.key" :data-numeric="col.numeric ? '' : undefined">
|
|
105
|
+
<slot :name="`cell-${col.key}`" :row="row" :value="row[col.key]">{{ row[col.key] }}</slot>
|
|
106
|
+
</td>
|
|
107
|
+
<td v-if="hasActions" class="oc-table-actions"><slot name="actions" :row="row" /></td>
|
|
108
|
+
</tr>
|
|
109
|
+
<tr v-if="rows.length === 0 && $slots.empty" class="oc-table-empty">
|
|
110
|
+
<td :colspan="colspan"><slot name="empty" :row="{}" /></td>
|
|
111
|
+
</tr>
|
|
112
|
+
</tbody>
|
|
113
|
+
</table>
|
|
114
|
+
</div>
|
|
115
|
+
</template>
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcTableActions.vue | Map: td.oc-table-actions -> icon actions (default slot) OR overflow trigger + popover menu (default slot = menu items) -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Row actions cell. Two modes:
|
|
5
|
-
* - no `menuLabel`: the default slot holds icon actions (`a` / `button` with class oc-table-actions-icon(--primary/--success/--destructive)).
|
|
6
|
-
* - with `menuLabel`: a three dots trigger opens a native popover; the default slot holds `.oc-dropdown-item`s.
|
|
7
|
-
* Classes: td.oc-table-actions(--icons), .oc-table-actions-icon, .oc-table-actions-more, .oc-dropdown-menu[popover]
|
|
8
|
-
*/
|
|
9
|
-
import { useUid } from '../composables/useUid'
|
|
10
|
-
|
|
11
|
-
defineProps<{
|
|
12
|
-
/** Accessible name of the overflow trigger, e.g. "Actions for Ada". Turns on menu mode. */
|
|
13
|
-
menuLabel?: string
|
|
14
|
-
}>()
|
|
15
|
-
const menuId = useUid('oc-table-actions-menu')
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
<template>
|
|
19
|
-
<td :class="['oc-table-actions', !menuLabel && 'oc-table-actions--icons']">
|
|
20
|
-
<template v-if="menuLabel">
|
|
21
|
-
<button class="oc-table-actions-more" type="button" :popovertarget="menuId" :aria-label="menuLabel"></button>
|
|
22
|
-
<div :id="menuId" class="oc-dropdown-menu" popover><slot /></div>
|
|
23
|
-
</template>
|
|
24
|
-
<slot v-else />
|
|
25
|
-
</td>
|
|
26
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcTableActions.vue | Map: td.oc-table-actions -> icon actions (default slot) OR overflow trigger + popover menu (default slot = menu items) -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Row actions cell. Two modes:
|
|
5
|
+
* - no `menuLabel`: the default slot holds icon actions (`a` / `button` with class oc-table-actions-icon(--primary/--success/--destructive)).
|
|
6
|
+
* - with `menuLabel`: a three dots trigger opens a native popover; the default slot holds `.oc-dropdown-item`s.
|
|
7
|
+
* Classes: td.oc-table-actions(--icons), .oc-table-actions-icon, .oc-table-actions-more, .oc-dropdown-menu[popover]
|
|
8
|
+
*/
|
|
9
|
+
import { useUid } from '../composables/useUid'
|
|
10
|
+
|
|
11
|
+
defineProps<{
|
|
12
|
+
/** Accessible name of the overflow trigger, e.g. "Actions for Ada". Turns on menu mode. */
|
|
13
|
+
menuLabel?: string
|
|
14
|
+
}>()
|
|
15
|
+
const menuId = useUid('oc-table-actions-menu')
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<td :class="['oc-table-actions', !menuLabel && 'oc-table-actions--icons']">
|
|
20
|
+
<template v-if="menuLabel">
|
|
21
|
+
<button class="oc-table-actions-more" type="button" :popovertarget="menuId" :aria-label="menuLabel"></button>
|
|
22
|
+
<div :id="menuId" class="oc-dropdown-menu" popover><slot /></div>
|
|
23
|
+
</template>
|
|
24
|
+
<slot v-else />
|
|
25
|
+
</td>
|
|
26
|
+
</template>
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
<!-- File: packages/vue/src/components/OcTableCompact.vue | Map: wrapper div -> table.oc-table (thead from columns, tbody from rows, tfoot from footer) OR default slot -->
|
|
2
|
-
<script setup lang="ts">
|
|
3
|
-
/**
|
|
4
|
-
* Small striped table for inside cards. Pass `columns` + `rows` (+ `footer` total row), or put your own
|
|
5
|
-
* <table class="oc-table"> in the default slot. Scoped slot `cell` ({ row, column }) customises body cells.
|
|
6
|
-
* Classes: .oc-table-compact(--bordered/--plain), .oc-table-compact-muted, [data-numeric], [data-wrap]
|
|
7
|
-
*/
|
|
8
|
-
export interface TableCompactColumn {
|
|
9
|
-
key: string
|
|
10
|
-
label: string
|
|
11
|
-
numeric?: boolean
|
|
12
|
-
wrap?: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
withDefaults(
|
|
16
|
-
defineProps<{
|
|
17
|
-
columns?: TableCompactColumn[]
|
|
18
|
-
rows?: Array<Record<string, unknown>>
|
|
19
|
-
footer?: Record<string, unknown>
|
|
20
|
-
bordered?: boolean
|
|
21
|
-
plain?: boolean
|
|
22
|
-
}>(),
|
|
23
|
-
{ rows: () => [] },
|
|
24
|
-
)
|
|
25
|
-
// data-* attributes only when switched on (an empty string means "present").
|
|
26
|
-
const cellAttrs = (c: TableCompactColumn) => ({ 'data-numeric': c.numeric ? '' : undefined, 'data-wrap': c.wrap ? '' : undefined })
|
|
27
|
-
</script>
|
|
28
|
-
|
|
29
|
-
<template>
|
|
30
|
-
<div :class="['oc-table-compact', bordered && 'oc-table-compact--bordered', plain && 'oc-table-compact--plain']">
|
|
31
|
-
<table v-if="columns" class="oc-table">
|
|
32
|
-
<thead>
|
|
33
|
-
<tr><th v-for="c in columns" :key="c.key" v-bind="cellAttrs(c)">{{ c.label }}</th></tr>
|
|
34
|
-
</thead>
|
|
35
|
-
<tbody>
|
|
36
|
-
<tr v-for="(row, i) in rows" :key="i">
|
|
37
|
-
<td v-for="c in columns" :key="c.key" v-bind="cellAttrs(c)"><slot name="cell" :row="row" :column="c">{{ row[c.key] }}</slot></td>
|
|
38
|
-
</tr>
|
|
39
|
-
</tbody>
|
|
40
|
-
<tfoot v-if="footer">
|
|
41
|
-
<tr><td v-for="c in columns" :key="c.key" v-bind="cellAttrs(c)">{{ footer[c.key] }}</td></tr>
|
|
42
|
-
</tfoot>
|
|
43
|
-
</table>
|
|
44
|
-
<slot v-else />
|
|
45
|
-
</div>
|
|
46
|
-
</template>
|
|
1
|
+
<!-- File: packages/vue/src/components/OcTableCompact.vue | Map: wrapper div -> table.oc-table (thead from columns, tbody from rows, tfoot from footer) OR default slot -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
/**
|
|
4
|
+
* Small striped table for inside cards. Pass `columns` + `rows` (+ `footer` total row), or put your own
|
|
5
|
+
* <table class="oc-table"> in the default slot. Scoped slot `cell` ({ row, column }) customises body cells.
|
|
6
|
+
* Classes: .oc-table-compact(--bordered/--plain), .oc-table-compact-muted, [data-numeric], [data-wrap]
|
|
7
|
+
*/
|
|
8
|
+
export interface TableCompactColumn {
|
|
9
|
+
key: string
|
|
10
|
+
label: string
|
|
11
|
+
numeric?: boolean
|
|
12
|
+
wrap?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
withDefaults(
|
|
16
|
+
defineProps<{
|
|
17
|
+
columns?: TableCompactColumn[]
|
|
18
|
+
rows?: Array<Record<string, unknown>>
|
|
19
|
+
footer?: Record<string, unknown>
|
|
20
|
+
bordered?: boolean
|
|
21
|
+
plain?: boolean
|
|
22
|
+
}>(),
|
|
23
|
+
{ rows: () => [] },
|
|
24
|
+
)
|
|
25
|
+
// data-* attributes only when switched on (an empty string means "present").
|
|
26
|
+
const cellAttrs = (c: TableCompactColumn) => ({ 'data-numeric': c.numeric ? '' : undefined, 'data-wrap': c.wrap ? '' : undefined })
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<div :class="['oc-table-compact', bordered && 'oc-table-compact--bordered', plain && 'oc-table-compact--plain']">
|
|
31
|
+
<table v-if="columns" class="oc-table">
|
|
32
|
+
<thead>
|
|
33
|
+
<tr><th v-for="c in columns" :key="c.key" v-bind="cellAttrs(c)">{{ c.label }}</th></tr>
|
|
34
|
+
</thead>
|
|
35
|
+
<tbody>
|
|
36
|
+
<tr v-for="(row, i) in rows" :key="i">
|
|
37
|
+
<td v-for="c in columns" :key="c.key" v-bind="cellAttrs(c)"><slot name="cell" :row="row" :column="c">{{ row[c.key] }}</slot></td>
|
|
38
|
+
</tr>
|
|
39
|
+
</tbody>
|
|
40
|
+
<tfoot v-if="footer">
|
|
41
|
+
<tr><td v-for="c in columns" :key="c.key" v-bind="cellAttrs(c)">{{ footer[c.key] }}</td></tr>
|
|
42
|
+
</tfoot>
|
|
43
|
+
</table>
|
|
44
|
+
<slot v-else />
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Tabs with radio inputs and :has() (the CSS switches the panels). v-model = value of the active tab.
|
|
4
|
-
* One named slot per tab value renders its panel: <template #account>...</template>.
|
|
5
|
-
* Classes: .oc-tabset, .oc-tab-list, .oc-tab-trigger, .oc-tab-panel
|
|
6
|
-
*/
|
|
7
|
-
import { useUid } from '../composables/useUid'
|
|
8
|
-
|
|
9
|
-
export interface TabItem {
|
|
10
|
-
label: string
|
|
11
|
-
/** Slot name and model value; defaults to the label. */
|
|
12
|
-
value?: string
|
|
13
|
-
}
|
|
14
|
-
defineProps<{ tabs: TabItem[]; name?: string }>()
|
|
15
|
-
const model = defineModel<string>()
|
|
16
|
-
const uid = useUid('oc-tabs')
|
|
17
|
-
</script>
|
|
18
|
-
|
|
19
|
-
<template>
|
|
20
|
-
<div class="oc-tabset">
|
|
21
|
-
<div class="oc-tab-list" role="tablist">
|
|
22
|
-
<label v-for="(tab, i) in tabs" :key="tab.value ?? tab.label" class="oc-tab-trigger">
|
|
23
|
-
<input
|
|
24
|
-
v-model="model"
|
|
25
|
-
type="radio"
|
|
26
|
-
:name="name ?? uid"
|
|
27
|
-
:value="tab.value ?? tab.label"
|
|
28
|
-
:checked="model === undefined && i === 0"
|
|
29
|
-
/>
|
|
30
|
-
{{ tab.label }}
|
|
31
|
-
</label>
|
|
32
|
-
</div>
|
|
33
|
-
<div v-for="tab in tabs" :key="tab.value ?? tab.label" class="oc-tab-panel">
|
|
34
|
-
<slot :name="tab.value ?? tab.label" />
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Tabs with radio inputs and :has() (the CSS switches the panels). v-model = value of the active tab.
|
|
4
|
+
* One named slot per tab value renders its panel: <template #account>...</template>.
|
|
5
|
+
* Classes: .oc-tabset, .oc-tab-list, .oc-tab-trigger, .oc-tab-panel
|
|
6
|
+
*/
|
|
7
|
+
import { useUid } from '../composables/useUid'
|
|
8
|
+
|
|
9
|
+
export interface TabItem {
|
|
10
|
+
label: string
|
|
11
|
+
/** Slot name and model value; defaults to the label. */
|
|
12
|
+
value?: string
|
|
13
|
+
}
|
|
14
|
+
defineProps<{ tabs: TabItem[]; name?: string }>()
|
|
15
|
+
const model = defineModel<string>()
|
|
16
|
+
const uid = useUid('oc-tabs')
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<div class="oc-tabset">
|
|
21
|
+
<div class="oc-tab-list" role="tablist">
|
|
22
|
+
<label v-for="(tab, i) in tabs" :key="tab.value ?? tab.label" class="oc-tab-trigger">
|
|
23
|
+
<input
|
|
24
|
+
v-model="model"
|
|
25
|
+
type="radio"
|
|
26
|
+
:name="name ?? uid"
|
|
27
|
+
:value="tab.value ?? tab.label"
|
|
28
|
+
:checked="model === undefined && i === 0"
|
|
29
|
+
/>
|
|
30
|
+
{{ tab.label }}
|
|
31
|
+
</label>
|
|
32
|
+
</div>
|
|
33
|
+
<div v-for="tab in tabs" :key="tab.value ?? tab.label" class="oc-tab-panel">
|
|
34
|
+
<slot :name="tab.value ?? tab.label" />
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* Chips with remove buttons and a free-text input with suggestions. v-model: string[].
|
|
4
|
-
* Enter or comma adds the typed tag, Backspace on an empty input removes the last one.
|
|
5
|
-
* Classes: .oc-tag, .oc-tag-remove, .oc-tag-input, .oc-tag-suggestions
|
|
6
|
-
*/
|
|
7
|
-
import { computed, ref } from 'vue'
|
|
8
|
-
|
|
9
|
-
const props = withDefaults(
|
|
10
|
-
defineProps<{
|
|
11
|
-
suggestions?: string[]
|
|
12
|
-
placeholder?: string
|
|
13
|
-
label?: string
|
|
14
|
-
}>(),
|
|
15
|
-
{ suggestions: () => [], placeholder: 'Add tag...', label: 'Add tag' },
|
|
16
|
-
)
|
|
17
|
-
const tags = defineModel<string[]>({ default: () => [] })
|
|
18
|
-
const draft = ref('')
|
|
19
|
-
const available = computed(() => props.suggestions.filter((s) => !tags.value.includes(s)))
|
|
20
|
-
|
|
21
|
-
function add(value: string) {
|
|
22
|
-
const tag = value.trim()
|
|
23
|
-
if (tag && !tags.value.includes(tag)) tags.value = [...tags.value, tag]
|
|
24
|
-
draft.value = ''
|
|
25
|
-
}
|
|
26
|
-
function remove(tag: string) {
|
|
27
|
-
tags.value = tags.value.filter((t) => t !== tag)
|
|
28
|
-
}
|
|
29
|
-
function onKeydown(e: KeyboardEvent) {
|
|
30
|
-
if (e.key === 'Enter' || e.key === ',') {
|
|
31
|
-
e.preventDefault()
|
|
32
|
-
add(draft.value)
|
|
33
|
-
} else if (e.key === 'Backspace' && !draft.value && tags.value.length) {
|
|
34
|
-
tags.value = tags.value.slice(0, -1)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
</script>
|
|
38
|
-
|
|
39
|
-
<template>
|
|
40
|
-
<div class="oc-tag-input">
|
|
41
|
-
<span v-for="tag in tags" :key="tag" class="oc-tag">
|
|
42
|
-
{{ tag }}
|
|
43
|
-
<button class="oc-tag-remove" type="button" :aria-label="`Remove ${tag}`" @click="remove(tag)">×</button>
|
|
44
|
-
</span>
|
|
45
|
-
<input v-model="draft" type="text" :placeholder="placeholder" :aria-label="label" @keydown="onKeydown" />
|
|
46
|
-
</div>
|
|
47
|
-
<div v-if="available.length" class="oc-tag-suggestions">
|
|
48
|
-
<button v-for="s in available" :key="s" type="button" @click="add(s)">{{ s }}</button>
|
|
49
|
-
</div>
|
|
50
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Chips with remove buttons and a free-text input with suggestions. v-model: string[].
|
|
4
|
+
* Enter or comma adds the typed tag, Backspace on an empty input removes the last one.
|
|
5
|
+
* Classes: .oc-tag, .oc-tag-remove, .oc-tag-input, .oc-tag-suggestions
|
|
6
|
+
*/
|
|
7
|
+
import { computed, ref } from 'vue'
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(
|
|
10
|
+
defineProps<{
|
|
11
|
+
suggestions?: string[]
|
|
12
|
+
placeholder?: string
|
|
13
|
+
label?: string
|
|
14
|
+
}>(),
|
|
15
|
+
{ suggestions: () => [], placeholder: 'Add tag...', label: 'Add tag' },
|
|
16
|
+
)
|
|
17
|
+
const tags = defineModel<string[]>({ default: () => [] })
|
|
18
|
+
const draft = ref('')
|
|
19
|
+
const available = computed(() => props.suggestions.filter((s) => !tags.value.includes(s)))
|
|
20
|
+
|
|
21
|
+
function add(value: string) {
|
|
22
|
+
const tag = value.trim()
|
|
23
|
+
if (tag && !tags.value.includes(tag)) tags.value = [...tags.value, tag]
|
|
24
|
+
draft.value = ''
|
|
25
|
+
}
|
|
26
|
+
function remove(tag: string) {
|
|
27
|
+
tags.value = tags.value.filter((t) => t !== tag)
|
|
28
|
+
}
|
|
29
|
+
function onKeydown(e: KeyboardEvent) {
|
|
30
|
+
if (e.key === 'Enter' || e.key === ',') {
|
|
31
|
+
e.preventDefault()
|
|
32
|
+
add(draft.value)
|
|
33
|
+
} else if (e.key === 'Backspace' && !draft.value && tags.value.length) {
|
|
34
|
+
tags.value = tags.value.slice(0, -1)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<div class="oc-tag-input">
|
|
41
|
+
<span v-for="tag in tags" :key="tag" class="oc-tag">
|
|
42
|
+
{{ tag }}
|
|
43
|
+
<button class="oc-tag-remove" type="button" :aria-label="`Remove ${tag}`" @click="remove(tag)">×</button>
|
|
44
|
+
</span>
|
|
45
|
+
<input v-model="draft" type="text" :placeholder="placeholder" :aria-label="label" @keydown="onKeydown" />
|
|
46
|
+
</div>
|
|
47
|
+
<div v-if="available.length" class="oc-tag-suggestions">
|
|
48
|
+
<button v-for="s in available" :key="s" type="button" @click="add(s)">{{ s }}</button>
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|