@nuxtseo/design-system 0.0.0-stage → 0.1.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/README.md +181 -2
- package/app/app.config.ts +592 -0
- package/app/component-attributes.d.ts +8 -0
- package/app/components/README.md +38 -0
- package/app/components/brand/UiBrandMark.vue +63 -0
- package/app/components/brand/UiModuleIcon.vue +45 -0
- package/app/components/brand/UiProLettering.vue +26 -0
- package/app/components/brand/UiRangeTerrain.vue +311 -0
- package/app/components/brand/range-glyphs.ts +18 -0
- package/app/components/container/BrowserTab.vue +145 -0
- package/app/components/container/IntegrationRow.vue +120 -0
- package/app/components/container/KnowDoRails.vue +40 -0
- package/app/components/container/RailPanel.vue +93 -0
- package/app/components/container/UiAlert.vue +120 -0
- package/app/components/container/UiAppShell.vue +165 -0
- package/app/components/container/UiCard.vue +204 -0
- package/app/components/container/UiDisclosure.vue +43 -0
- package/app/components/container/UiEmptyState.vue +102 -0
- package/app/components/container/UiErrorState.vue +61 -0
- package/app/components/container/UiLoadingState.vue +20 -0
- package/app/components/container/UiPageHeader.vue +95 -0
- package/app/components/container/UiPopover.vue +102 -0
- package/app/components/container/UiSampleDataOverlay.vue +47 -0
- package/app/components/container/UiSectionHeader.vue +83 -0
- package/app/components/container/UiSummaryStrip.vue +40 -0
- package/app/components/container/UiToast.vue +230 -0
- package/app/components/container/UiToaster.vue +271 -0
- package/app/components/container/UiWidgetState.vue +143 -0
- package/app/components/container/ui-nav-drawer.ts +48 -0
- package/app/components/data/ActionRail.vue +152 -0
- package/app/components/data/FacetStrip.vue +93 -0
- package/app/components/data/UiBarCell.vue +37 -0
- package/app/components/data/UiBarChart.vue +281 -0
- package/app/components/data/UiChartAnnotationControls.vue +74 -0
- package/app/components/data/UiChartAnnotations.vue +163 -0
- package/app/components/data/UiChartBrushOverlay.vue +96 -0
- package/app/components/data/UiChartDataReveal.vue +81 -0
- package/app/components/data/UiChartFrame.vue +123 -0
- package/app/components/data/UiChartTooltip.vue +81 -0
- package/app/components/data/UiCoverageNote.vue +90 -0
- package/app/components/data/UiCwvSparkline.vue +188 -0
- package/app/components/data/UiDataList.vue +276 -0
- package/app/components/data/UiDataTableSection.vue +233 -0
- package/app/components/data/UiDistributionBars.vue +164 -0
- package/app/components/data/UiDonutChart.vue +132 -0
- package/app/components/data/UiDoorwayColumn.vue +108 -0
- package/app/components/data/UiDoorwayGrid.vue +58 -0
- package/app/components/data/UiDoorwayRow.vue +83 -0
- package/app/components/data/UiEntitySummary.vue +261 -0
- package/app/components/data/UiFactsGrid.vue +107 -0
- package/app/components/data/UiImpactMeter.vue +33 -0
- package/app/components/data/UiIssueRow.vue +149 -0
- package/app/components/data/UiLineChart.vue +431 -0
- package/app/components/data/UiMetricBarPopover.vue +121 -0
- package/app/components/data/UiMetricLabel.vue +30 -0
- package/app/components/data/UiMetricStat.vue +73 -0
- package/app/components/data/UiMetricToggle.vue +156 -0
- package/app/components/data/UiMetricsRow.vue +64 -0
- package/app/components/data/UiPositionMetric.vue +53 -0
- package/app/components/data/UiRelatedFixes.vue +113 -0
- package/app/components/data/UiRichText.vue +325 -0
- package/app/components/data/UiScatterPlot.vue +144 -0
- package/app/components/data/UiSeverityMarker.vue +30 -0
- package/app/components/data/UiSparkColumns.vue +160 -0
- package/app/components/data/UiSparkline.vue +681 -0
- package/app/components/data/UiStat.vue +445 -0
- package/app/components/data/UiStats.vue +76 -0
- package/app/components/data/UiTable.vue +517 -0
- package/app/components/data/UiTableExport.vue +40 -0
- package/app/components/data/UiTableFrame.vue +149 -0
- package/app/components/data/UiTablePagination.vue +37 -0
- package/app/components/data/UiTableShell.vue +48 -0
- package/app/components/data/UiTableTd.vue +39 -0
- package/app/components/data/UiTableTh.vue +61 -0
- package/app/components/data/UiTopEntityStackChart.vue +393 -0
- package/app/components/data/cells/UiTableDash.vue +3 -0
- package/app/components/data/cells/UiTableHeaderCell.vue +72 -0
- package/app/components/data/cells/UiTableMetricCell.vue +72 -0
- package/app/components/data/cells/UiTablePathCell.vue +76 -0
- package/app/components/data/cells/UiTableScoreTile.vue +49 -0
- package/app/components/data/cells/UiTableTrendCell.vue +41 -0
- package/app/components/element/UiAtmosphere.vue +64 -0
- package/app/components/element/UiAuthHeading.vue +24 -0
- package/app/components/element/UiAuthProviders.vue +46 -0
- package/app/components/element/UiBrandIcon.vue +39 -0
- package/app/components/element/UiButton.vue +549 -0
- package/app/components/element/UiCelebration.vue +65 -0
- package/app/components/element/UiChannelChips.vue +62 -0
- package/app/components/element/UiChatQuickLink.vue +319 -0
- package/app/components/element/UiChip.vue +101 -0
- package/app/components/element/UiConfirmDialog.vue +84 -0
- package/app/components/element/UiCopyButton.vue +90 -0
- package/app/components/element/UiDitherAvatar.vue +177 -0
- package/app/components/element/UiDotLabel.vue +19 -0
- package/app/components/element/UiEjectMenu.vue +181 -0
- package/app/components/element/UiEjectOverlay.vue +29 -0
- package/app/components/element/UiFavicon.vue +235 -0
- package/app/components/element/UiFaviconStack.vue +84 -0
- package/app/components/element/UiFilterMenu.vue +324 -0
- package/app/components/element/UiFocusedSurface.vue +129 -0
- package/app/components/element/UiHealthDot.vue +32 -0
- package/app/components/element/UiHelpLabel.vue +28 -0
- package/app/components/element/UiIcon.vue +22 -0
- package/app/components/element/UiInput.vue +35 -0
- package/app/components/element/UiLogoPlausible.vue +12 -0
- package/app/components/element/UiLogoPro.vue +25 -0
- package/app/components/element/UiLogoUmami.vue +12 -0
- package/app/components/element/UiNavIcon.vue +107 -0
- package/app/components/element/UiNavList.vue +156 -0
- package/app/components/element/UiOnboardingAnswers.vue +127 -0
- package/app/components/element/UiOverflowCount.vue +31 -0
- package/app/components/element/UiProBadge.vue +19 -0
- package/app/components/element/UiProgressCircle.vue +79 -0
- package/app/components/element/UiProgressPercent.vue +35 -0
- package/app/components/element/UiRelativeTime.vue +58 -0
- package/app/components/element/UiScrollFade.vue +16 -0
- package/app/components/element/UiSelect.vue +68 -0
- package/app/components/element/UiSeverityDot.vue +27 -0
- package/app/components/element/UiSiteFavicon.vue +24 -0
- package/app/components/element/UiSkeleton.vue +148 -0
- package/app/components/element/UiSlotText.vue +65 -0
- package/app/components/element/UiSourceLogos.vue +113 -0
- package/app/components/element/UiSourceRail.vue +71 -0
- package/app/components/element/UiStaticTerminal.vue +107 -0
- package/app/components/element/UiStatusBadge.vue +62 -0
- package/app/components/element/UiSyncDot.vue +46 -0
- package/app/components/element/UiTabs.vue +245 -0
- package/app/components/element/UiTogglePill.vue +86 -0
- package/app/components/element/UiTooltip.vue +390 -0
- package/app/components/element/UiTooltipGrid.vue +457 -0
- package/app/components/element/UiTrend.vue +184 -0
- package/app/components/element/UiTrialBenefits.vue +71 -0
- package/app/components/element/UiUrlLabel.vue +75 -0
- package/app/components/element/UiValenceDot.vue +37 -0
- package/app/components/element/UiWizardModal.vue +284 -0
- package/app/components/element/UiWizardNav.vue +59 -0
- package/app/components/element/UiWizardProgress.vue +78 -0
- package/app/components/element/UiWizardShell.vue +43 -0
- package/app/components/element/eject-menu.ts +359 -0
- package/app/components/element/eject-tracker.ts +34 -0
- package/app/components/element/onboarding-answers.ts +93 -0
- package/app/components/element/onboarding-intents.ts +19 -0
- package/app/composables/dataVizColors.ts +350 -0
- package/app/composables/formatting.ts +252 -0
- package/app/composables/semanticColors.ts +80 -0
- package/app/composables/useActivityOverlay.ts +55 -0
- package/app/composables/useAppShellNav.ts +21 -0
- package/app/composables/useChartBrush.ts +221 -0
- package/app/composables/useChartHover.ts +153 -0
- package/app/composables/useChartTickPlan.ts +186 -0
- package/app/composables/useCloseOnNavigation.ts +7 -0
- package/app/composables/useConfirm.ts +38 -0
- package/app/composables/useEdgeFade.ts +37 -0
- package/app/composables/useEject.ts +69 -0
- package/app/composables/useEntranceMotion.ts +22 -0
- package/app/composables/useFaviconBacking.ts +164 -0
- package/app/composables/useGridCols.ts +39 -0
- package/app/composables/useIssueOverlay.ts +43 -0
- package/app/composables/useOnboardingService.ts +106 -0
- package/app/composables/useOpenedOnce.ts +19 -0
- package/app/composables/useScrollSpy.ts +178 -0
- package/app/composables/useSquircleFallback.ts +21 -0
- package/app/composables/useWizardRoute.ts +123 -0
- package/app/plugins/favicon.ts +15 -0
- package/app/plugins/lazy-motion.ts +22 -0
- package/app/plugins/selection-highlight.client.ts +176 -0
- package/app/shared/cli.ts +10 -0
- package/app/shared/error-state.ts +150 -0
- package/app/shared/icons.ts +1 -0
- package/app/shared/motion.ts +1 -0
- package/app/shared/nav.ts +38 -0
- package/app/shared/navigation.ts +5 -0
- package/app/shared/static-terminal.ts +14 -0
- package/app/shared/table-export.ts +36 -0
- package/app/shared/table.ts +105 -0
- package/app/shared/ui-icons.ts +1 -0
- package/app/shared/view-state.ts +85 -0
- package/app/shared/widget-failure.ts +126 -0
- package/app/utils/app-favicon.ts +64 -0
- package/app/utils/chartAnnotations.ts +326 -0
- package/app/utils/countries.ts +337 -0
- package/app/utils/export-download.ts +100 -0
- package/app/utils/favicon-fallback.ts +12 -0
- package/app/utils/icon-color.ts +105 -0
- package/app/utils/number.ts +74 -0
- package/app/utils/render-link.ts +14 -0
- package/app/utils/slot-text.ts +18 -0
- package/app/utils/squircle.ts +154 -0
- package/app/utils/topEntityStack.ts +197 -0
- package/app/utils/vue.ts +57 -0
- package/build/design-system.ts +69 -0
- package/build/external-links.ts +38 -0
- package/build/fonts.ts +39 -0
- package/build/icon-server-bundle.ts +276 -0
- package/build/runtime/icon-server-bundle.ts +29 -0
- package/css/global.css +1896 -0
- package/nuxt.config.ts +143 -0
- package/package.json +93 -3
- package/public/fonts/README.md +33 -0
- package/public/fonts/fira-code-OFL.txt +93 -0
- package/public/fonts/fira-code-var-latin.woff2 +0 -0
- package/public/fonts/hubot-sans-OFL.txt +93 -0
- package/public/fonts/hubot-sans-var-latin.woff2 +0 -0
- package/server/api/favicon.get.ts +122 -0
- package/server/utils/favicon-source.ts +51 -0
- package/shared/contracts/feedback.ts +42 -0
- package/shared/contracts/index.ts +1 -0
- package/shared/delivery-channels.ts +53 -0
- package/shared/icons/index.ts +143 -0
- package/shared/icons/registry.ts +347 -0
- package/shared/icons/ui-slots.ts +58 -0
- package/shared/markdown.ts +44 -0
- package/shared/motion.ts +120 -0
- package/shared/rangi.ts +60 -0
- package/shared/rich-text.ts +34 -0
- package/shared/ui-icons.ts +6 -0
- package/shared/utils/urls.ts +64 -0
- package/types/index.ts +1 -0
- package/types/ui.ts +339 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const {
|
|
3
|
+
name = 'UiTableFrame',
|
|
4
|
+
bordered = false,
|
|
5
|
+
rowHover = false,
|
|
6
|
+
scrolled = false,
|
|
7
|
+
// No default on purpose. `role="region"` is a LANDMARK: several unlabelled
|
|
8
|
+
// tables on one page would each announce the same generic name, which is the
|
|
9
|
+
// exact failure landmark navigation exists to avoid. Unnamed frames fall back
|
|
10
|
+
// to `role="group"`, which stays keyboard-scrollable without polluting the
|
|
11
|
+
// landmark rotor.
|
|
12
|
+
scrollLabel,
|
|
13
|
+
} = defineProps<{
|
|
14
|
+
name?: string
|
|
15
|
+
bordered?: boolean
|
|
16
|
+
rowHover?: boolean
|
|
17
|
+
scrolled?: boolean
|
|
18
|
+
scrollLabel?: string
|
|
19
|
+
}>()
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div
|
|
24
|
+
:data-ui="name"
|
|
25
|
+
:data-bordered="bordered || undefined"
|
|
26
|
+
:data-row-hover="rowHover || undefined"
|
|
27
|
+
:data-scrolled="scrolled || undefined"
|
|
28
|
+
:aria-label="scrollLabel"
|
|
29
|
+
:role="scrollLabel ? 'region' : 'group'"
|
|
30
|
+
tabindex="0"
|
|
31
|
+
class="w-full overflow-x-auto overscroll-x-contain"
|
|
32
|
+
:class="bordered ? 'rounded-xl border border-default bg-default' : ''"
|
|
33
|
+
>
|
|
34
|
+
<slot />
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<style scoped>
|
|
39
|
+
:deep(table) {
|
|
40
|
+
width: 100%;
|
|
41
|
+
border-collapse: collapse;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
:deep(thead) {
|
|
45
|
+
position: sticky;
|
|
46
|
+
inset-block-start: 0;
|
|
47
|
+
z-index: 10;
|
|
48
|
+
background: var(--ui-bg);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
:deep(thead th) {
|
|
52
|
+
border-block-end: 1px solid var(--ui-border);
|
|
53
|
+
background: var(--ui-bg);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
:deep(tbody > tr.spacer) {
|
|
57
|
+
block-size: 0.25rem;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:deep(tbody > tr.spacer + tr > :is(th, td)) {
|
|
61
|
+
border-block-start: 4px solid transparent;
|
|
62
|
+
background-clip: padding-box;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
:deep(tbody > tr:not(.spacer):not(:last-child) > :is(th, td)) {
|
|
66
|
+
border-block-end: 1px solid var(--ui-border);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
[data-row-hover] :deep(tbody > tr:not(.spacer):not(.expanded-row) > :is(th, td)) {
|
|
70
|
+
transition: background-color 140ms ease-out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
[data-row-hover] :deep(tbody > tr:not(.spacer):not(.expanded-row):not([data-state='selected']):hover > :is(th, td)),
|
|
74
|
+
[data-row-hover] :deep(tbody > tr:not(.spacer):not(.expanded-row):not([data-state='selected']):focus-visible > :is(th, td)) {
|
|
75
|
+
background: var(--ui-bg-muted);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
[data-row-hover] :deep(tbody > tr:not(.spacer):not(.expanded-row):not([data-state='selected']):hover > :is(th, td):first-child),
|
|
79
|
+
[data-row-hover] :deep(tbody > tr:not(.spacer):not(.expanded-row):not([data-state='selected']):focus-visible > :is(th, td):first-child) {
|
|
80
|
+
border-start-start-radius: 0.5rem;
|
|
81
|
+
border-end-start-radius: 0.5rem;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
[data-row-hover] :deep(tbody > tr:not(.spacer):not(.expanded-row):not([data-state='selected']):hover > :is(th, td):last-child),
|
|
85
|
+
[data-row-hover] :deep(tbody > tr:not(.spacer):not(.expanded-row):not([data-state='selected']):focus-visible > :is(th, td):last-child) {
|
|
86
|
+
border-start-end-radius: 0.5rem;
|
|
87
|
+
border-end-end-radius: 0.5rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[data-row-hover] :deep(tbody > tr.spacer + tr:hover > :is(th, td)),
|
|
91
|
+
[data-row-hover] :deep(tbody > tr.spacer + tr:focus-visible > :is(th, td)) {
|
|
92
|
+
background-clip: border-box;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
* One continuous selection band: the fill runs the whole row and only its two
|
|
97
|
+
* end cells round, so the row reads as a single shape rather than a strip of
|
|
98
|
+
* separately rounded cells. Same geometry as the hover treatment above, which
|
|
99
|
+
* is what makes hovering a selected row a no-op instead of a shape change.
|
|
100
|
+
*/
|
|
101
|
+
:deep(tbody > tr[data-state='selected'] > :is(th, td)) {
|
|
102
|
+
background: color-mix(in srgb, var(--ui-primary) 12%, var(--ui-bg));
|
|
103
|
+
border-radius: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
:deep(tbody > tr[data-state='selected'] > :is(th, td):first-child) {
|
|
107
|
+
border-start-start-radius: 0.5rem;
|
|
108
|
+
border-end-start-radius: 0.5rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:deep(tbody > tr[data-state='selected'] > :is(th, td):last-child) {
|
|
112
|
+
border-start-end-radius: 0.5rem;
|
|
113
|
+
border-end-end-radius: 0.5rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
:deep(table[data-size='xs'] tbody > tr:not(.spacer):not(.expanded-row) > :is(th, td)) {
|
|
117
|
+
block-size: 2.75rem;
|
|
118
|
+
padding-block: 0.25rem;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
:deep(table[data-size='sm'] tbody > tr:not(.spacer):not(.expanded-row) > :is(th, td)),
|
|
122
|
+
:deep(table[data-size='md'] tbody > tr:not(.spacer):not(.expanded-row) > :is(th, td)) {
|
|
123
|
+
block-size: 2.75rem;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
[data-scrolled] :deep(thead) {
|
|
127
|
+
box-shadow: var(--elevation-overlay);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
:deep(thead) {
|
|
131
|
+
transition: box-shadow 200ms ease-out;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* Rows shrink on a PRECISE pointer, not on a wide screen. A tablet is both wide
|
|
136
|
+
* and touched, and in a click-through table the row is the primary target — a
|
|
137
|
+
* 40px row on a touch device is under the 44px floor.
|
|
138
|
+
*/
|
|
139
|
+
@media (min-width: 40rem) and (pointer: fine) {
|
|
140
|
+
:deep(table[data-size='xs'] tbody > tr:not(.spacer):not(.expanded-row) > :is(th, td)) {
|
|
141
|
+
block-size: 2rem;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
:deep(table[data-size='sm'] tbody > tr:not(.spacer):not(.expanded-row) > :is(th, td)),
|
|
145
|
+
:deep(table[data-size='md'] tbody > tr:not(.spacer):not(.expanded-row) > :is(th, td)) {
|
|
146
|
+
block-size: 2.5rem;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { UPagination } from '#components'
|
|
3
|
+
|
|
4
|
+
const { total, pageSize } = defineProps<{
|
|
5
|
+
total: number
|
|
6
|
+
pageSize: number
|
|
7
|
+
}>()
|
|
8
|
+
|
|
9
|
+
const page = defineModel<number>('page', { required: true })
|
|
10
|
+
|
|
11
|
+
// `justify-center` keeps the icon or number centred once the target outgrows its content.
|
|
12
|
+
const TARGET = 'min-h-11 min-w-11 justify-center sm:min-h-8 sm:min-w-8'
|
|
13
|
+
const paginationUi = {
|
|
14
|
+
first: TARGET,
|
|
15
|
+
prev: TARGET,
|
|
16
|
+
item: TARGET,
|
|
17
|
+
next: TARGET,
|
|
18
|
+
last: TARGET,
|
|
19
|
+
} as const
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<template>
|
|
23
|
+
<div class="flex flex-col items-stretch gap-3 pt-2 sm:flex-row sm:items-center sm:justify-between sm:gap-4">
|
|
24
|
+
<p class="text-center text-sm text-muted sm:text-left">
|
|
25
|
+
<slot />
|
|
26
|
+
</p>
|
|
27
|
+
<UPagination
|
|
28
|
+
v-model:page="page"
|
|
29
|
+
class="self-center sm:self-auto"
|
|
30
|
+
size="sm"
|
|
31
|
+
:items-per-page="pageSize"
|
|
32
|
+
:total="total"
|
|
33
|
+
:sibling-count="1"
|
|
34
|
+
:ui="paginationUi"
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { UiTableSize } from '../../shared/table'
|
|
3
|
+
import { UiTableFrame } from '#components'
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
size = 'md',
|
|
7
|
+
rowHover = false,
|
|
8
|
+
bordered = false,
|
|
9
|
+
label,
|
|
10
|
+
loading = false,
|
|
11
|
+
tableClass,
|
|
12
|
+
} = defineProps<{
|
|
13
|
+
size?: UiTableSize
|
|
14
|
+
/** Enable hover gradient on body rows. */
|
|
15
|
+
rowHover?: boolean
|
|
16
|
+
/** Wrap the table in a bordered/rounded container (use false when caller owns the chrome). */
|
|
17
|
+
bordered?: boolean
|
|
18
|
+
/** Accessible name for the table. Rendered as a visually-hidden <caption>. */
|
|
19
|
+
label: string
|
|
20
|
+
loading?: boolean
|
|
21
|
+
tableClass?: string
|
|
22
|
+
}>()
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<UiTableFrame
|
|
27
|
+
name="UiTableShell"
|
|
28
|
+
:bordered="bordered"
|
|
29
|
+
:row-hover="rowHover"
|
|
30
|
+
:scroll-label="`${label} table scroll area`"
|
|
31
|
+
>
|
|
32
|
+
<table class="w-full" :class="tableClass" :data-size="size" :aria-busy="loading || undefined">
|
|
33
|
+
<caption v-if="label" class="sr-only">
|
|
34
|
+
{{ label }}
|
|
35
|
+
</caption>
|
|
36
|
+
<thead v-if="$slots.head">
|
|
37
|
+
<tr class="h-10">
|
|
38
|
+
<slot name="head" />
|
|
39
|
+
</tr>
|
|
40
|
+
</thead>
|
|
41
|
+
<tbody>
|
|
42
|
+
<tr class="spacer" aria-hidden="true" />
|
|
43
|
+
<slot />
|
|
44
|
+
</tbody>
|
|
45
|
+
<slot name="tfoot" />
|
|
46
|
+
</table>
|
|
47
|
+
</UiTableFrame>
|
|
48
|
+
</template>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { UiTableCellProps, UiTableSize } from '../../shared/table'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { uiTableCellSizeClass, uiTableVisibleFromClass } from '../../shared/table'
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
align = 'left',
|
|
8
|
+
size = 'md',
|
|
9
|
+
noPadding = false,
|
|
10
|
+
numeric = false,
|
|
11
|
+
rowHeader = false,
|
|
12
|
+
visibleFrom,
|
|
13
|
+
} = defineProps<UiTableCellProps & {
|
|
14
|
+
size?: UiTableSize
|
|
15
|
+
noPadding?: boolean
|
|
16
|
+
rowHeader?: boolean
|
|
17
|
+
}>()
|
|
18
|
+
|
|
19
|
+
const resolvedAlign = computed(() => numeric ? 'right' : align)
|
|
20
|
+
const textAlign = computed(() => resolvedAlign.value === 'right' ? 'text-right' : resolvedAlign.value === 'center' ? 'text-center' : 'text-left')
|
|
21
|
+
const padClass = computed(() => noPadding ? '' : 'px-3')
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<component
|
|
26
|
+
:is="rowHeader ? 'th' : 'td'"
|
|
27
|
+
:scope="rowHeader ? 'row' : undefined"
|
|
28
|
+
class="text-xs font-normal text-default relative"
|
|
29
|
+
:class="[
|
|
30
|
+
uiTableCellSizeClass[size],
|
|
31
|
+
padClass,
|
|
32
|
+
textAlign,
|
|
33
|
+
numeric ? 'tabular-nums' : '',
|
|
34
|
+
visibleFrom ? uiTableVisibleFromClass[visibleFrom] : '',
|
|
35
|
+
]"
|
|
36
|
+
>
|
|
37
|
+
<slot />
|
|
38
|
+
</component>
|
|
39
|
+
</template>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { UiTableCellProps } from '../../shared/table'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { UiIcon } from '#components'
|
|
5
|
+
import { uiTableVisibleFromClass } from '../../shared/table'
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
align = 'left',
|
|
9
|
+
sortable = false,
|
|
10
|
+
noPadding = false,
|
|
11
|
+
numeric = false,
|
|
12
|
+
sortDirection = false,
|
|
13
|
+
ariaLabel,
|
|
14
|
+
visibleFrom,
|
|
15
|
+
} = defineProps<UiTableCellProps & {
|
|
16
|
+
sortable?: boolean
|
|
17
|
+
noPadding?: boolean
|
|
18
|
+
sortDirection?: 'asc' | 'desc' | false
|
|
19
|
+
ariaLabel?: string
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const emit = defineEmits<{ sort: [event: MouseEvent] }>()
|
|
23
|
+
|
|
24
|
+
const resolvedAlign = computed(() => numeric ? 'right' : align)
|
|
25
|
+
const justify = computed(() => resolvedAlign.value === 'right' ? 'justify-end' : resolvedAlign.value === 'center' ? 'justify-center' : 'justify-start')
|
|
26
|
+
// Vertical padding lives here, not on the consumer's <tr>. UiTable sets `h-10`
|
|
27
|
+
// on its own header row, but this primitive exists for hand-rolled tables where
|
|
28
|
+
// there is no such row — without `py-*` a 10px label's line box is the entire
|
|
29
|
+
// header height and the text sits jammed against the bottom border.
|
|
30
|
+
//
|
|
31
|
+
// A sortable header moves its padding onto the inner button so the whole cell
|
|
32
|
+
// is the click target. The button carries the same `px-3` as UiTableTd, or a
|
|
33
|
+
// sortable numeric header sits 4px inboard of its own column's digits.
|
|
34
|
+
const padClass = computed(() => noPadding || sortable ? '' : 'px-3 py-2.5')
|
|
35
|
+
const sortIcon = computed(() => sortDirection ? 'collapse' : 'sort')
|
|
36
|
+
const sortIconClass = computed(() => sortDirection === 'desc' ? 'rotate-180' : '')
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<th
|
|
41
|
+
scope="col"
|
|
42
|
+
class="text-label text-left whitespace-nowrap border-b border-default bg-default"
|
|
43
|
+
:class="[padClass, numeric ? 'text-right tabular-nums' : '', visibleFrom ? uiTableVisibleFromClass[visibleFrom] : '']"
|
|
44
|
+
:aria-sort="sortable ? sortDirection === 'asc' ? 'ascending' : sortDirection === 'desc' ? 'descending' : 'none' : undefined"
|
|
45
|
+
>
|
|
46
|
+
<button
|
|
47
|
+
v-if="sortable"
|
|
48
|
+
type="button"
|
|
49
|
+
class="flex min-h-11 w-full items-center gap-1 rounded px-3 py-2.5 text-label text-muted transition-colors hover:text-default focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary sm:min-h-0"
|
|
50
|
+
:class="justify"
|
|
51
|
+
:aria-label="ariaLabel"
|
|
52
|
+
@click="emit('sort', $event)"
|
|
53
|
+
>
|
|
54
|
+
<slot />
|
|
55
|
+
<UiIcon :name="sortIcon" class="size-3 text-dimmed transition-transform duration-150" :class="sortIconClass" aria-hidden="true" />
|
|
56
|
+
</button>
|
|
57
|
+
<div v-else class="flex items-center gap-1" :class="justify">
|
|
58
|
+
<slot />
|
|
59
|
+
</div>
|
|
60
|
+
</th>
|
|
61
|
+
</template>
|