@livenetworks/ashlar 1.3.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/README.md +177 -0
- package/js/COMPONENTS.md +1102 -0
- package/js/index.js +41 -0
- package/js/ln-accordion/README.md +137 -0
- package/js/ln-accordion/ln-accordion.js +1 -0
- package/js/ln-accordion/src/ln-accordion.js +41 -0
- package/js/ln-ajax/README.md +91 -0
- package/js/ln-ajax/ln-ajax.js +1 -0
- package/js/ln-ajax/src/ln-ajax.js +277 -0
- package/js/ln-api-connector/README.md +150 -0
- package/js/ln-api-connector/ln-api-connector.js +1 -0
- package/js/ln-api-connector/src/ln-api-connector.js +265 -0
- package/js/ln-autoresize/README.md +80 -0
- package/js/ln-autoresize/ln-autoresize.js +1 -0
- package/js/ln-autoresize/src/ln-autoresize.js +47 -0
- package/js/ln-autosave/README.md +92 -0
- package/js/ln-autosave/ln-autosave.js +1 -0
- package/js/ln-autosave/src/ln-autosave.js +147 -0
- package/js/ln-circular-progress/README.md +161 -0
- package/js/ln-circular-progress/ln-circular-progress.js +1 -0
- package/js/ln-circular-progress/src/ln-circular-progress.js +133 -0
- package/js/ln-confirm/README.md +86 -0
- package/js/ln-confirm/_ln-confirm.scss +13 -0
- package/js/ln-confirm/ln-confirm.js +1 -0
- package/js/ln-confirm/src/ln-confirm.js +131 -0
- package/js/ln-core/crypto.js +83 -0
- package/js/ln-core/helpers.js +411 -0
- package/js/ln-core/index.js +5 -0
- package/js/ln-core/persist.js +71 -0
- package/js/ln-core/positioning.js +207 -0
- package/js/ln-core/reactive.js +74 -0
- package/js/ln-couchdb-connector/README.md +156 -0
- package/js/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
- package/js/ln-couchdb-connector/src/ln-couchdb-connector.js +348 -0
- package/js/ln-data-coordinator/README.md +165 -0
- package/js/ln-data-coordinator/ln-data-coordinator.js +1 -0
- package/js/ln-data-coordinator/src/ln-data-coordinator.js +249 -0
- package/js/ln-data-store/README.md +94 -0
- package/js/ln-data-store/ln-data-store.js +1 -0
- package/js/ln-data-store/src/ln-data-store.js +699 -0
- package/js/ln-data-table/README.md +110 -0
- package/js/ln-data-table/ln-data-table.js +1 -0
- package/js/ln-data-table/ln-data-table.scss +10 -0
- package/js/ln-data-table/src/ln-data-table.js +1103 -0
- package/js/ln-date/README.md +151 -0
- package/js/ln-date/ln-date.js +1 -0
- package/js/ln-date/src/ln-date.js +442 -0
- package/js/ln-dropdown/README.md +117 -0
- package/js/ln-dropdown/ln-dropdown.js +1 -0
- package/js/ln-dropdown/ln-dropdown.scss +15 -0
- package/js/ln-dropdown/src/ln-dropdown.js +174 -0
- package/js/ln-external-links/README.md +341 -0
- package/js/ln-external-links/ln-external-links.js +1 -0
- package/js/ln-external-links/src/ln-external-links.js +116 -0
- package/js/ln-filter/README.md +99 -0
- package/js/ln-filter/ln-filter.js +1 -0
- package/js/ln-filter/ln-filter.scss +7 -0
- package/js/ln-filter/src/ln-filter.js +404 -0
- package/js/ln-form/README.md +101 -0
- package/js/ln-form/ln-form.js +1 -0
- package/js/ln-form/src/ln-form.js +199 -0
- package/js/ln-http/README.md +89 -0
- package/js/ln-http/ln-http.js +1 -0
- package/js/ln-http/src/ln-http.js +219 -0
- package/js/ln-icons/README.md +88 -0
- package/js/ln-icons/ln-icons.js +1 -0
- package/js/ln-icons/src/ln-icons.js +169 -0
- package/js/ln-link/README.md +303 -0
- package/js/ln-link/ln-link.js +1 -0
- package/js/ln-link/src/ln-link.js +196 -0
- package/js/ln-modal/README.md +154 -0
- package/js/ln-modal/ln-modal.js +1 -0
- package/js/ln-modal/ln-modal.scss +11 -0
- package/js/ln-modal/src/ln-modal.js +201 -0
- package/js/ln-nav/README.md +70 -0
- package/js/ln-nav/ln-nav.js +1 -0
- package/js/ln-nav/src/ln-nav.js +177 -0
- package/js/ln-number/README.md +122 -0
- package/js/ln-number/ln-number.js +1 -0
- package/js/ln-number/src/ln-number.js +302 -0
- package/js/ln-popover/README.md +127 -0
- package/js/ln-popover/ln-popover.js +1 -0
- package/js/ln-popover/src/ln-popover.js +288 -0
- package/js/ln-progress/README.md +442 -0
- package/js/ln-progress/ln-progress.js +1 -0
- package/js/ln-progress/src/ln-progress.js +150 -0
- package/js/ln-search/README.md +83 -0
- package/js/ln-search/ln-search.js +1 -0
- package/js/ln-search/ln-search.scss +7 -0
- package/js/ln-search/src/ln-search.js +114 -0
- package/js/ln-sortable/README.md +95 -0
- package/js/ln-sortable/ln-sortable.js +1 -0
- package/js/ln-sortable/src/ln-sortable.js +203 -0
- package/js/ln-table/README.md +101 -0
- package/js/ln-table/ln-table-sort.js +1 -0
- package/js/ln-table/ln-table.js +1 -0
- package/js/ln-table/ln-table.scss +11 -0
- package/js/ln-table/src/ln-table-sort.js +168 -0
- package/js/ln-table/src/ln-table.js +473 -0
- package/js/ln-tabs/README.md +137 -0
- package/js/ln-tabs/ln-tabs.js +1 -0
- package/js/ln-tabs/src/ln-tabs.js +171 -0
- package/js/ln-time/README.md +81 -0
- package/js/ln-time/ln-time.js +1 -0
- package/js/ln-time/src/ln-time.js +192 -0
- package/js/ln-toast/README.md +122 -0
- package/js/ln-toast/ln-toast.js +15 -0
- package/js/ln-toast/src/ln-toast.js +210 -0
- package/js/ln-toast/template.html +14 -0
- package/js/ln-toggle/README.md +137 -0
- package/js/ln-toggle/ln-toggle.js +1 -0
- package/js/ln-toggle/src/ln-toggle.js +139 -0
- package/js/ln-tooltip/README.md +58 -0
- package/js/ln-tooltip/ln-tooltip.js +1 -0
- package/js/ln-tooltip/ln-tooltip.scss +9 -0
- package/js/ln-tooltip/src/ln-tooltip.js +169 -0
- package/js/ln-translations/README.md +96 -0
- package/js/ln-translations/ln-translations.js +1 -0
- package/js/ln-translations/src/ln-translations.js +275 -0
- package/js/ln-upload/README.md +180 -0
- package/js/ln-upload/ln-upload.js +1 -0
- package/js/ln-upload/ln-upload.scss +20 -0
- package/js/ln-upload/src/ln-upload.js +407 -0
- package/js/ln-validate/README.md +108 -0
- package/js/ln-validate/ln-validate.js +1 -0
- package/js/ln-validate/src/ln-validate.js +160 -0
- package/package.json +55 -0
- package/scss/base/_global.scss +83 -0
- package/scss/base/_reset.scss +17 -0
- package/scss/base/_typography.scss +125 -0
- package/scss/components/_accordion.scss +34 -0
- package/scss/components/_ajax.scss +15 -0
- package/scss/components/_alert.scss +5 -0
- package/scss/components/_app-shell.scss +15 -0
- package/scss/components/_avatar.scss +6 -0
- package/scss/components/_breadcrumbs.scss +33 -0
- package/scss/components/_button.scss +20 -0
- package/scss/components/_card.scss +10 -0
- package/scss/components/_chip.scss +5 -0
- package/scss/components/_circular-progress.scss +29 -0
- package/scss/components/_confirm.scss +5 -0
- package/scss/components/_data-table.scss +83 -0
- package/scss/components/_dropdown.scss +25 -0
- package/scss/components/_empty-state.scss +22 -0
- package/scss/components/_form.scss +100 -0
- package/scss/components/_layout.scss +8 -0
- package/scss/components/_link.scss +11 -0
- package/scss/components/_ln-table.scss +60 -0
- package/scss/components/_loader.scss +6 -0
- package/scss/components/_modal.scss +20 -0
- package/scss/components/_nav.scss +9 -0
- package/scss/components/_page-header.scss +10 -0
- package/scss/components/_popover.scss +10 -0
- package/scss/components/_progress.scss +17 -0
- package/scss/components/_prose.scss +5 -0
- package/scss/components/_scrollbar.scss +32 -0
- package/scss/components/_sections.scss +12 -0
- package/scss/components/_sidebar.scss +5 -0
- package/scss/components/_stat-card.scss +5 -0
- package/scss/components/_status-badge.scss +4 -0
- package/scss/components/_stepper.scss +5 -0
- package/scss/components/_table.scss +19 -0
- package/scss/components/_tabs.scss +21 -0
- package/scss/components/_timeline.scss +14 -0
- package/scss/components/_toast.scss +41 -0
- package/scss/components/_toggle.scss +81 -0
- package/scss/components/_tooltip.scss +18 -0
- package/scss/components/_translations.scss +111 -0
- package/scss/components/_upload.scss +51 -0
- package/scss/config/_breakpoints.scss +72 -0
- package/scss/config/_density.scss +117 -0
- package/scss/config/_icons.scss +37 -0
- package/scss/config/_mixins.scss +13 -0
- package/scss/config/_theme.scss +216 -0
- package/scss/config/_tokens.scss +419 -0
- package/scss/config/mixins/_accordion.scss +52 -0
- package/scss/config/mixins/_ajax.scss +39 -0
- package/scss/config/mixins/_alert.scss +82 -0
- package/scss/config/mixins/_app-shell.scss +312 -0
- package/scss/config/mixins/_avatar.scss +109 -0
- package/scss/config/mixins/_borders.scss +36 -0
- package/scss/config/mixins/_breadcrumbs.scss +72 -0
- package/scss/config/mixins/_breakpoints.scss +62 -0
- package/scss/config/mixins/_btn.scss +179 -0
- package/scss/config/mixins/_card.scss +338 -0
- package/scss/config/mixins/_chip.scss +66 -0
- package/scss/config/mixins/_circular-progress.scss +71 -0
- package/scss/config/mixins/_collapsible.scss +24 -0
- package/scss/config/mixins/_colors.scss +46 -0
- package/scss/config/mixins/_confirm.scss +31 -0
- package/scss/config/mixins/_data-table.scss +346 -0
- package/scss/config/mixins/_display.scss +32 -0
- package/scss/config/mixins/_dropdown.scss +143 -0
- package/scss/config/mixins/_empty-state.scss +30 -0
- package/scss/config/mixins/_focus.scss +55 -0
- package/scss/config/mixins/_footer.scss +42 -0
- package/scss/config/mixins/_form.scss +601 -0
- package/scss/config/mixins/_index.scss +58 -0
- package/scss/config/mixins/_interaction.scss +15 -0
- package/scss/config/mixins/_kbd.scss +22 -0
- package/scss/config/mixins/_layout.scss +117 -0
- package/scss/config/mixins/_link.scss +55 -0
- package/scss/config/mixins/_ln-table.scss +420 -0
- package/scss/config/mixins/_loader.scss +26 -0
- package/scss/config/mixins/_modal.scss +66 -0
- package/scss/config/mixins/_motion.scss +19 -0
- package/scss/config/mixins/_nav.scss +273 -0
- package/scss/config/mixins/_page-header.scss +69 -0
- package/scss/config/mixins/_popover.scss +25 -0
- package/scss/config/mixins/_position.scss +32 -0
- package/scss/config/mixins/_progress.scss +56 -0
- package/scss/config/mixins/_prose.scss +127 -0
- package/scss/config/mixins/_shadows.scss +8 -0
- package/scss/config/mixins/_sidebar.scss +95 -0
- package/scss/config/mixins/_sizing.scss +6 -0
- package/scss/config/mixins/_spacing.scss +19 -0
- package/scss/config/mixins/_stat-card.scss +68 -0
- package/scss/config/mixins/_status-badge.scss +83 -0
- package/scss/config/mixins/_stepper.scss +78 -0
- package/scss/config/mixins/_table.scss +215 -0
- package/scss/config/mixins/_tabs.scss +64 -0
- package/scss/config/mixins/_timeline.scss +69 -0
- package/scss/config/mixins/_toast.scss +148 -0
- package/scss/config/mixins/_tooltip.scss +111 -0
- package/scss/config/mixins/_transitions.scss +10 -0
- package/scss/config/mixins/_translations.scss +124 -0
- package/scss/config/mixins/_typography.scss +57 -0
- package/scss/config/mixins/_upload.scss +168 -0
- package/scss/ln-ashlar.scss +62 -0
- package/scss/tabler-icons.txt +5039 -0
- package/scss/utilities/_animations.scss +83 -0
- package/scss/utilities/_utilities.scss +49 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
@use 'display' as *;
|
|
2
|
+
@use 'sizing' as *;
|
|
3
|
+
@use 'typography' as *;
|
|
4
|
+
@use 'colors' as *;
|
|
5
|
+
@use 'position' as *;
|
|
6
|
+
@use 'motion' as *;
|
|
7
|
+
|
|
8
|
+
// Circular progress ring — SVG-based progress indicator with center label.
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// [data-ln-circular-progress] { @include circular-progress; }
|
|
12
|
+
//
|
|
13
|
+
// Size variants: circular-progress-sm, circular-progress-lg, circular-progress-xl
|
|
14
|
+
// Color variants: override --color-primary on the element or a wrapper:
|
|
15
|
+
// [data-ln-circular-progress].success { --color-primary: var(--color-success); }
|
|
16
|
+
|
|
17
|
+
@mixin circular-progress {
|
|
18
|
+
@include relative;
|
|
19
|
+
@include inline-flex;
|
|
20
|
+
@include flex-center;
|
|
21
|
+
@include size(4rem);
|
|
22
|
+
|
|
23
|
+
svg {
|
|
24
|
+
@include size(100%);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.ln-circular-progress__track {
|
|
28
|
+
stroke: var(--color-border);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.ln-circular-progress__fill {
|
|
32
|
+
stroke: var(--color-accent);
|
|
33
|
+
@include motion-safe {
|
|
34
|
+
transition: stroke-dashoffset var(--transition-base);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.ln-circular-progress__label {
|
|
39
|
+
@include absolute;
|
|
40
|
+
@include text-xs;
|
|
41
|
+
@include font-semibold;
|
|
42
|
+
color: var(--color-fg);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─── Size variants ─────────────────────────────────────
|
|
47
|
+
@mixin circular-progress-sm {
|
|
48
|
+
@include size(2.5rem);
|
|
49
|
+
|
|
50
|
+
.ln-circular-progress__label {
|
|
51
|
+
font-size: 0.625rem;
|
|
52
|
+
line-height: 0.75rem;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@mixin circular-progress-lg {
|
|
57
|
+
@include size(6rem);
|
|
58
|
+
|
|
59
|
+
.ln-circular-progress__label {
|
|
60
|
+
@include text-base;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@mixin circular-progress-xl {
|
|
65
|
+
@include size(8rem);
|
|
66
|
+
|
|
67
|
+
.ln-circular-progress__label {
|
|
68
|
+
@include text-xl;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Collapsible — grid-template-rows animation for expand/collapse
|
|
2
|
+
//
|
|
3
|
+
// Usage:
|
|
4
|
+
// .my-panel { @include collapsible; }
|
|
5
|
+
// .my-panel > .inner { @include collapsible-content; }
|
|
6
|
+
|
|
7
|
+
@use 'transitions' as *;
|
|
8
|
+
@use 'motion' as *;
|
|
9
|
+
|
|
10
|
+
@mixin collapsible {
|
|
11
|
+
display: grid;
|
|
12
|
+
grid-template-rows: 0fr;
|
|
13
|
+
@include motion-safe {
|
|
14
|
+
transition: grid-template-rows var(--transition-base);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&.open {
|
|
18
|
+
grid-template-rows: 1fr;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@mixin collapsible-content {
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Colors — references --color-* tokens
|
|
2
|
+
|
|
3
|
+
@mixin text-primary { color: var(--color-fg); }
|
|
4
|
+
@mixin text-white { color: hsl(var(--color-white)); }
|
|
5
|
+
@mixin text-error { color: hsl(var(--color-error)); }
|
|
6
|
+
@mixin text-success { color: hsl(var(--color-success)); }
|
|
7
|
+
@mixin text-warning { color: hsl(var(--color-warning)); }
|
|
8
|
+
|
|
9
|
+
@mixin bg-primary { background-color: var(--color-bg); }
|
|
10
|
+
|
|
11
|
+
// Tinted surface — semi-transparent primary fill + solid primary text.
|
|
12
|
+
// Override --color-primary on the element/ancestor to restyle (e.g. error, success).
|
|
13
|
+
@mixin tinted-surface($opacity: 0.08) {
|
|
14
|
+
background-color: hsl(var(--color-primary) / #{$opacity});
|
|
15
|
+
color: hsl(var(--color-primary));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Semantic color recipe — re-binds the --color-primary family AND the
|
|
19
|
+
// --color-accent vocabulary to a status family (success / warning /
|
|
20
|
+
// error / info / secondary). Used by .success / .warning / .error /
|
|
21
|
+
// .info / .secondary classes in _utilities.scss. Cascading both families
|
|
22
|
+
// ensures every downstream consumer (btn, toast-side, card-accent,
|
|
23
|
+
// alert, badge, etc.) picks up the status color through inherited
|
|
24
|
+
// custom-property values — no per-mixin redeclare required.
|
|
25
|
+
@mixin semantic-color($family) {
|
|
26
|
+
// Re-bind the base color and the theme-controlled tint companions.
|
|
27
|
+
// --color-primary-hover / -focus are NOT rebound — consumers derive
|
|
28
|
+
// hover/focus from --color-accent locally via CSS relative color
|
|
29
|
+
// syntax. See _btn.scss / _form.scss / _data-table.scss / _global.scss
|
|
30
|
+
// for the derivation pattern. --color-primary-light / -lighter ARE
|
|
31
|
+
// rebound because their values are theme-controlled (light themes
|
|
32
|
+
// use near-white tints, dark themes use subtle dark surfaces) and
|
|
33
|
+
// cannot be derived from the base.
|
|
34
|
+
--color-primary: var(--color-#{$family});
|
|
35
|
+
--color-primary-light: var(--color-#{$family}-light);
|
|
36
|
+
--color-primary-lighter: var(--color-#{$family}-lighter);
|
|
37
|
+
|
|
38
|
+
// Re-declare accent vocabulary at this scope so var() resolves
|
|
39
|
+
// here instead of staying frozen at :root. Without this, descendants
|
|
40
|
+
// reading --color-accent get :root's primary blue regardless of the
|
|
41
|
+
// semantic class. --color-accent-fg stays at :root (white) — text on
|
|
42
|
+
// any colored accent surface should stay white.
|
|
43
|
+
--color-accent: hsl(var(--color-primary));
|
|
44
|
+
--color-accent-tint: hsl(var(--color-primary-lighter));
|
|
45
|
+
--color-accent-tint-strong: hsl(var(--color-primary-light));
|
|
46
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@use 'spacing' as *;
|
|
2
|
+
@use 'typography' as *;
|
|
3
|
+
@use 'shadows' as *;
|
|
4
|
+
@use 'tooltip' as *;
|
|
5
|
+
|
|
6
|
+
// Confirm tooltip — visual tooltip for icon-only confirm buttons.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// .ln-confirm-tooltip { @include confirm-tooltip; }
|
|
10
|
+
//
|
|
11
|
+
// JS adds .ln-confirm-tooltip class and data-tooltip-text attribute
|
|
12
|
+
// to icon-only buttons entering confirm state.
|
|
13
|
+
|
|
14
|
+
@mixin confirm-tooltip {
|
|
15
|
+
position: relative;
|
|
16
|
+
overflow: visible !important;
|
|
17
|
+
|
|
18
|
+
// Confirm icon inherits this color
|
|
19
|
+
color: hsl(var(--color-error)) !important;
|
|
20
|
+
|
|
21
|
+
&::after {
|
|
22
|
+
@include tooltip-bubble;
|
|
23
|
+
content: attr(data-tooltip-text);
|
|
24
|
+
position: absolute;
|
|
25
|
+
bottom: 100%;
|
|
26
|
+
left: 50%;
|
|
27
|
+
transform: translateX(-50%);
|
|
28
|
+
--margin-block: var(--size-sm);
|
|
29
|
+
margin-bottom: var(--margin-block);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
@use 'display' as *;
|
|
2
|
+
@use 'spacing' as *;
|
|
3
|
+
@use 'sizing' as *;
|
|
4
|
+
@use 'typography' as *;
|
|
5
|
+
@use 'colors' as *;
|
|
6
|
+
@use 'borders' as *;
|
|
7
|
+
@use 'shadows' as *;
|
|
8
|
+
@use 'position' as *;
|
|
9
|
+
@use 'transitions' as *;
|
|
10
|
+
@use 'interaction' as *;
|
|
11
|
+
@use 'motion' as *;
|
|
12
|
+
@use 'form' as *;
|
|
13
|
+
@use 'btn' as *;
|
|
14
|
+
@use 'card' as *;
|
|
15
|
+
@use 'ln-table';
|
|
16
|
+
|
|
17
|
+
// Data Table -- enhanced table with toolbar, sort, filter, virtual scroll.
|
|
18
|
+
//
|
|
19
|
+
// Usage:
|
|
20
|
+
// [data-ln-data-table] { @include data-table; }
|
|
21
|
+
//
|
|
22
|
+
// State-driven CSS (loading, selected, focused, spacer) stays in
|
|
23
|
+
// the co-located JS SCSS file.
|
|
24
|
+
|
|
25
|
+
@mixin data-table {
|
|
26
|
+
@include relative;
|
|
27
|
+
|
|
28
|
+
// Row hover token — local to data-table so chrome (toolbar, thead,
|
|
29
|
+
// footer) reading --bg-sunken stays visually distinct from the row
|
|
30
|
+
// the cursor is on. --bg-elevated is unsuitable: it equals --bg-base
|
|
31
|
+
// in light theme, making hover invisible. Derive a 4-point lightness
|
|
32
|
+
// step from --bg-base via CSS relative color syntax (number form, see
|
|
33
|
+
// _btn.scss header for percentage-vs-number rationale).
|
|
34
|
+
//
|
|
35
|
+
// Light theme: hsl(white) → ~hsl(0 0% 96%)
|
|
36
|
+
// Dark theme: hsl(220 16% 13%) → hsl(220 16% 17%) ≈ --bg-elevated dark
|
|
37
|
+
//
|
|
38
|
+
// The 4-point step is monotonically distinct from --bg-sunken
|
|
39
|
+
// (light: neutral-100 ≈ 95%; dark: 220 16% 20%) on both sides.
|
|
40
|
+
--bg-row-hover: hsl(from var(--bg-base) h s calc(l - 4));
|
|
41
|
+
|
|
42
|
+
// Override @mixin table-base's `tbody tr:hover` to use the local hover
|
|
43
|
+
// token instead of --bg-sunken. Default hover stays --bg-sunken for
|
|
44
|
+
// standalone tables; data-table re-binds so its hover disambiguates from
|
|
45
|
+
// chrome that also uses --bg-sunken.
|
|
46
|
+
> table tbody tr:hover {
|
|
47
|
+
--color-bg: var(--bg-row-hover);
|
|
48
|
+
background: var(--color-bg);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Inner table — `border-collapse: separate` + `border-spacing: 0` is
|
|
52
|
+
// required for `position: sticky` on `<thead> th` to actually pin in
|
|
53
|
+
// Chrome. With the `border-collapse: collapse` that `@mixin table-base`
|
|
54
|
+
// sets by default, Chrome silently drops sticky behavior on table cells —
|
|
55
|
+
// the same reason `@mixin ln-table` applies this exact override. Borders
|
|
56
|
+
// are restored cell-by-cell via table-base's `td { @include border-b }`,
|
|
57
|
+
// so no visual change.
|
|
58
|
+
> table {
|
|
59
|
+
border-collapse: separate;
|
|
60
|
+
border-spacing: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
// Sticky header - Pins the entire thead (including the in-thead toolbar) at top:0
|
|
66
|
+
thead {
|
|
67
|
+
@include sticky-top;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
thead th {
|
|
71
|
+
position: relative;
|
|
72
|
+
top: auto;
|
|
73
|
+
z-index: auto;
|
|
74
|
+
background: var(--bg-sunken);
|
|
75
|
+
|
|
76
|
+
&[data-ln-col] {
|
|
77
|
+
// Maintain standard table-cell layout so columns don't break or stack vertically
|
|
78
|
+
white-space: normal;
|
|
79
|
+
|
|
80
|
+
// Add padding-inline-end to prevent text from overlapping absolute buttons
|
|
81
|
+
|
|
82
|
+
// Case 1: Has both sort and filter buttons
|
|
83
|
+
&:has([data-ln-col-sort]):has([data-ln-col-filter]) {
|
|
84
|
+
padding-inline-end: calc(var(--padding-x) + 2.5rem);
|
|
85
|
+
|
|
86
|
+
[data-ln-col-filter] {
|
|
87
|
+
position: absolute;
|
|
88
|
+
top: 50%;
|
|
89
|
+
transform: translateY(-50%);
|
|
90
|
+
right: var(--padding-x);
|
|
91
|
+
margin: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
[data-ln-col-sort] {
|
|
95
|
+
position: absolute;
|
|
96
|
+
top: 50%;
|
|
97
|
+
transform: translateY(-50%);
|
|
98
|
+
right: calc(var(--padding-x) + 1.25rem);
|
|
99
|
+
margin: 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Case 2: Has only sort button
|
|
104
|
+
&:has([data-ln-col-sort]):not(:has([data-ln-col-filter])) {
|
|
105
|
+
padding-inline-end: calc(var(--padding-x) + 1.25rem);
|
|
106
|
+
|
|
107
|
+
[data-ln-col-sort] {
|
|
108
|
+
position: absolute;
|
|
109
|
+
top: 50%;
|
|
110
|
+
transform: translateY(-50%);
|
|
111
|
+
right: var(--padding-x);
|
|
112
|
+
margin: 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Case 3: Has only filter button
|
|
117
|
+
&:has([data-ln-col-filter]):not(:has([data-ln-col-sort])) {
|
|
118
|
+
padding-inline-end: calc(var(--padding-x) + 1.25rem);
|
|
119
|
+
|
|
120
|
+
[data-ln-col-filter] {
|
|
121
|
+
position: absolute;
|
|
122
|
+
top: 50%;
|
|
123
|
+
transform: translateY(-50%);
|
|
124
|
+
right: var(--padding-x);
|
|
125
|
+
margin: 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Support for the in-thead toolbar row inside ln-data-table
|
|
132
|
+
thead > tr:first-child:has(> th[colspan]) > th {
|
|
133
|
+
@include ln-table.ln-table-thead-toolbar;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Section footer — sibling of <table>, NOT inside it. Moved out of
|
|
137
|
+
// <tfoot> because Chromium drops sticky on table cells when a
|
|
138
|
+
// virtual-scroll spacer <tr> sits between the data and the tfoot row.
|
|
139
|
+
// As a section-level <footer> sticky works reliably and shares the
|
|
140
|
+
// same scroll container as the toolbar.
|
|
141
|
+
//
|
|
142
|
+
// Chrome surface visually symmetric with panel-header: --bg-sunken
|
|
143
|
+
// fill, muted fg, body-sm text, border-block-start for separation.
|
|
144
|
+
> footer {
|
|
145
|
+
padding: var(--padding-y) var(--padding-x);
|
|
146
|
+
border-block-start: var(--border-block-start, var(--border-width) solid var(--color-border));
|
|
147
|
+
--color-bg: var(--bg-sunken);
|
|
148
|
+
background: var(--color-bg);
|
|
149
|
+
--color-fg: var(--fg-muted);
|
|
150
|
+
color: var(--color-fg);
|
|
151
|
+
font-size: var(--text-body-sm);
|
|
152
|
+
position: sticky;
|
|
153
|
+
bottom: 0;
|
|
154
|
+
z-index: var(--z-sticky);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ─── Sort indicators ──────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
@mixin data-table-sort-button {
|
|
161
|
+
opacity: 0.4;
|
|
162
|
+
@include motion-safe {
|
|
163
|
+
transition: opacity 0.15s;
|
|
164
|
+
}
|
|
165
|
+
@include cursor-pointer;
|
|
166
|
+
background: none;
|
|
167
|
+
border: none;
|
|
168
|
+
--btn-padding-y: var(--size-2xs);
|
|
169
|
+
--btn-padding-x: var(--size-2xs);
|
|
170
|
+
--margin-inline: var(--size-xs);
|
|
171
|
+
margin-inline-start: var(--margin-inline);
|
|
172
|
+
vertical-align: middle;
|
|
173
|
+
|
|
174
|
+
// Icon-trio visibility — neutral state shows the dual-arrow icon.
|
|
175
|
+
// Active states (.ln-sort-asc / .ln-sort-desc on ancestor <th>)
|
|
176
|
+
// flip to a single directional icon. See component file for the
|
|
177
|
+
// active-state overrides.
|
|
178
|
+
[data-ln-sort-icon="asc"],
|
|
179
|
+
[data-ln-sort-icon="desc"] {
|
|
180
|
+
display: none;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@mixin data-table-sort-active {
|
|
185
|
+
opacity: 1;
|
|
186
|
+
color: var(--color-accent);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─── Filter button ────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
@mixin data-table-filter-button {
|
|
192
|
+
opacity: 0.4;
|
|
193
|
+
@include motion-safe {
|
|
194
|
+
transition: opacity 0.15s;
|
|
195
|
+
}
|
|
196
|
+
@include cursor-pointer;
|
|
197
|
+
background: none;
|
|
198
|
+
border: none;
|
|
199
|
+
--btn-padding-y: var(--size-2xs);
|
|
200
|
+
--btn-padding-x: var(--size-2xs);
|
|
201
|
+
--margin-inline: var(--size-2xs);
|
|
202
|
+
margin-inline-start: var(--margin-inline);
|
|
203
|
+
vertical-align: middle;
|
|
204
|
+
@include relative;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@mixin data-table-filter-active {
|
|
208
|
+
opacity: 1;
|
|
209
|
+
color: var(--color-accent);
|
|
210
|
+
|
|
211
|
+
&::after {
|
|
212
|
+
content: '';
|
|
213
|
+
@include absolute;
|
|
214
|
+
top: 0;
|
|
215
|
+
right: 0;
|
|
216
|
+
width: 0.375rem;
|
|
217
|
+
height: 0.375rem;
|
|
218
|
+
border-radius: var(--radius-full);
|
|
219
|
+
background: var(--color-accent);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ─── Row action buttons ───────────────────────────────────
|
|
224
|
+
//
|
|
225
|
+
// `[data-ln-row-action]` is the ln-data-table attribute for in-row
|
|
226
|
+
// action buttons (edit, delete, custom). They're conventionally
|
|
227
|
+
// icon-only and live in narrow `<td>` cells — full button chrome
|
|
228
|
+
// inflates row height and visually competes with the row content.
|
|
229
|
+
//
|
|
230
|
+
// Style is ghost: no bg, no border, no shadow, dim opacity at rest,
|
|
231
|
+
// full opacity on hover. Targets the BUTTON via its own attribute,
|
|
232
|
+
// not via ancestor selectors — a `data-ln-row-action` button looks
|
|
233
|
+
// the same anywhere.
|
|
234
|
+
|
|
235
|
+
@mixin data-table-row-action {
|
|
236
|
+
background: none;
|
|
237
|
+
border: none;
|
|
238
|
+
--shadow: none;
|
|
239
|
+
--btn-padding-y: var(--size-2xs);
|
|
240
|
+
--btn-padding-x: var(--size-2xs);
|
|
241
|
+
opacity: 0.5;
|
|
242
|
+
@include motion-safe {
|
|
243
|
+
transition: opacity 0.15s;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
&:hover:not(:disabled) {
|
|
247
|
+
background: none;
|
|
248
|
+
opacity: 1;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Destructive variant — semantic-color cascade via --color-primary
|
|
253
|
+
// rebind so the icon and any confirm-state overlay both read the
|
|
254
|
+
// same source. Applied on top of `data-table-row-action`.
|
|
255
|
+
@mixin data-table-row-action-delete {
|
|
256
|
+
--color-primary: var(--color-error);
|
|
257
|
+
color: hsl(var(--color-primary));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ─── Filter dropdown ──────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
@mixin data-table-filter-dropdown {
|
|
263
|
+
@include absolute;
|
|
264
|
+
top: 100%;
|
|
265
|
+
left: 0;
|
|
266
|
+
z-index: var(--z-dropdown);
|
|
267
|
+
--color-bg: var(--bg-elevated);
|
|
268
|
+
background: var(--color-bg);
|
|
269
|
+
--shadow: var(--shadow-floating);
|
|
270
|
+
border-block-start: var(--border-block-start, var(--border-width) solid var(--color-border));
|
|
271
|
+
border-block-end: var(--border-block-end, var(--border-width) solid var(--color-border));
|
|
272
|
+
border-inline-start: var(--border-inline-start, var(--border-width) solid var(--color-border));
|
|
273
|
+
border-inline-end: var(--border-inline-end, var(--border-width) solid var(--color-border));
|
|
274
|
+
border-radius: var(--radius);
|
|
275
|
+
box-shadow: var(--shadow);
|
|
276
|
+
--padding-y: var(--size-sm);
|
|
277
|
+
--padding-x: var(--size-sm);
|
|
278
|
+
padding: var(--padding-y) var(--padding-x);
|
|
279
|
+
min-width: 12rem;
|
|
280
|
+
max-height: 20rem;
|
|
281
|
+
overflow-y: auto;
|
|
282
|
+
|
|
283
|
+
[data-ln-filter-search] {
|
|
284
|
+
@include form-input;
|
|
285
|
+
width: 100%;
|
|
286
|
+
--margin-block: var(--size-sm);
|
|
287
|
+
margin-bottom: var(--margin-block);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
ul {
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
li {
|
|
294
|
+
--padding-y: var(--size-xs);
|
|
295
|
+
padding: var(--padding-y) 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
label {
|
|
299
|
+
display: flex;
|
|
300
|
+
align-items: center;
|
|
301
|
+
gap: var(--gap);
|
|
302
|
+
@include cursor-pointer;
|
|
303
|
+
font-size: var(--text-sm);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
[data-ln-filter-clear] {
|
|
307
|
+
@include btn;
|
|
308
|
+
width: 100%;
|
|
309
|
+
--margin-block: var(--size-sm);
|
|
310
|
+
margin-top: var(--margin-block);
|
|
311
|
+
font-size: var(--text-sm);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Row interaction state — applied to JS-marked clickable rows.
|
|
316
|
+
|
|
317
|
+
@mixin data-table-row {
|
|
318
|
+
cursor: pointer;
|
|
319
|
+
|
|
320
|
+
&:hover {
|
|
321
|
+
--color-bg: var(--bg-sunken);
|
|
322
|
+
background: var(--color-bg);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@mixin data-table-row-selected {
|
|
327
|
+
background: var(--color-accent-tint);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
@mixin data-table-row-focused {
|
|
331
|
+
// Derive focus from --color-accent at this scope (same hue/saturation,
|
|
332
|
+
// lightness +8). Number form (not percentage) — see _btn.scss for the
|
|
333
|
+
// syntax rationale.
|
|
334
|
+
--color-accent-focus: hsl(from var(--color-accent) h s calc(l + 8));
|
|
335
|
+
outline: 2px solid var(--color-accent-focus);
|
|
336
|
+
outline-offset: -2px;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Virtual-scroll spacer rows (JS-injected) — neutralize default cell padding/border.
|
|
340
|
+
|
|
341
|
+
@mixin data-table-spacer-row {
|
|
342
|
+
td {
|
|
343
|
+
padding: 0 !important;
|
|
344
|
+
border: none !important;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Display & Flex
|
|
2
|
+
|
|
3
|
+
@mixin flex { display: flex; }
|
|
4
|
+
@mixin inline-flex { display: inline-flex; }
|
|
5
|
+
@mixin block { display: block; }
|
|
6
|
+
@mixin inline-block { display: inline-block; }
|
|
7
|
+
@mixin hidden { display: none; }
|
|
8
|
+
|
|
9
|
+
@mixin flex-col { display: flex; flex-direction: column; }
|
|
10
|
+
@mixin flex-row { display: flex; flex-direction: row; }
|
|
11
|
+
@mixin flex-wrap { flex-wrap: wrap; }
|
|
12
|
+
@mixin flex-1 { flex: 1; }
|
|
13
|
+
@mixin flex-shrink-0 { flex-shrink: 0; }
|
|
14
|
+
|
|
15
|
+
@mixin items-center { align-items: center; }
|
|
16
|
+
@mixin items-start { align-items: flex-start; }
|
|
17
|
+
@mixin items-end { align-items: flex-end; }
|
|
18
|
+
@mixin justify-center { justify-content: center; }
|
|
19
|
+
@mixin justify-between { justify-content: space-between; }
|
|
20
|
+
@mixin justify-end { justify-content: flex-end; }
|
|
21
|
+
|
|
22
|
+
@mixin flex-center {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@mixin inline-flex-center {
|
|
29
|
+
display: inline-flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
@use 'display' as *;
|
|
2
|
+
@use 'spacing' as *;
|
|
3
|
+
@use 'colors' as *;
|
|
4
|
+
@use 'borders' as *;
|
|
5
|
+
@use 'shadows' as *;
|
|
6
|
+
@use 'typography' as *;
|
|
7
|
+
@use 'sizing' as *;
|
|
8
|
+
@use 'position' as *;
|
|
9
|
+
@use 'interaction' as *;
|
|
10
|
+
@use 'transitions' as *;
|
|
11
|
+
@use 'motion' as *;
|
|
12
|
+
@use 'card' as *;
|
|
13
|
+
|
|
14
|
+
// Dropdown menu — floating panel with menu items.
|
|
15
|
+
//
|
|
16
|
+
// Usage:
|
|
17
|
+
// [data-ln-dropdown] { @include dropdown; }
|
|
18
|
+
// [data-ln-dropdown-menu] { @include dropdown-menu; }
|
|
19
|
+
//
|
|
20
|
+
// Open/close state (display:none ↔ display:block + .open) is JS-driven
|
|
21
|
+
// and lives in the co-located JS SCSS, not here.
|
|
22
|
+
|
|
23
|
+
@mixin dropdown {
|
|
24
|
+
@include relative;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ─── Menu items — shared recipe ────────────────────────
|
|
28
|
+
// The "list of interactive menu items" recipe, without container
|
|
29
|
+
// chrome or positioning. Composes with any floating container
|
|
30
|
+
// (dropdown, popover-with-menu, other future surfaces).
|
|
31
|
+
//
|
|
32
|
+
// Expected HTML:
|
|
33
|
+
// <ul>
|
|
34
|
+
// <li><a>…</a></li>
|
|
35
|
+
// <li><button>…</button></li>
|
|
36
|
+
// <li><button aria-current="true">Active item</button></li>
|
|
37
|
+
// <li><hr></li>
|
|
38
|
+
// <li><form><button type="submit">Logout</button></form></li>
|
|
39
|
+
// </ul>
|
|
40
|
+
//
|
|
41
|
+
// Selection state — `aria-current="true"` (boolean flavor) marks the
|
|
42
|
+
// active item in single-select pickers (theme, language). NOT the
|
|
43
|
+
// `="page"` / `="step"` flavors — those belong to breadcrumbs /
|
|
44
|
+
// stepper and have their own styling in those mixins.
|
|
45
|
+
|
|
46
|
+
@mixin menu-items {
|
|
47
|
+
// ─── Row resets ────────────────────────────────────
|
|
48
|
+
li {
|
|
49
|
+
margin: 0;
|
|
50
|
+
padding: 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
form {
|
|
54
|
+
margin: 0;
|
|
55
|
+
padding: 0;
|
|
56
|
+
display: contents;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ─── Interactive items ─────────────────────────────
|
|
60
|
+
a,
|
|
61
|
+
button,
|
|
62
|
+
input[type="submit"],
|
|
63
|
+
input[type="reset"],
|
|
64
|
+
input[type="button"] {
|
|
65
|
+
@include w-full;
|
|
66
|
+
@include flex;
|
|
67
|
+
@include items-center;
|
|
68
|
+
justify-content: flex-start;
|
|
69
|
+
gap: var(--gap);
|
|
70
|
+
@include text-left;
|
|
71
|
+
@include text-sm;
|
|
72
|
+
@include cursor-pointer;
|
|
73
|
+
@include transition-fast;
|
|
74
|
+
background: none;
|
|
75
|
+
border: none;
|
|
76
|
+
border-radius: 0;
|
|
77
|
+
color: var(--color-fg);
|
|
78
|
+
text-decoration: none;
|
|
79
|
+
|
|
80
|
+
&:hover:not(:disabled),
|
|
81
|
+
&:active:not(:disabled) {
|
|
82
|
+
--color-bg: var(--bg-sunken);
|
|
83
|
+
background: var(--color-bg);
|
|
84
|
+
color: var(--color-fg);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
&:focus {
|
|
88
|
+
box-shadow: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Active item — single-select selection state.
|
|
92
|
+
// Scoped to the boolean flavor so we don't collide with
|
|
93
|
+
// breadcrumbs (`="page"`) or stepper (`="step"`).
|
|
94
|
+
&[aria-current="true"] {
|
|
95
|
+
background: var(--color-accent-tint);
|
|
96
|
+
color: var(--color-accent);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
a {
|
|
101
|
+
--padding-y: var(--size-xs);
|
|
102
|
+
--padding-x: var(--size-sm);
|
|
103
|
+
padding: var(--padding-y) var(--padding-x);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
:is(button, input[type="submit"], input[type="reset"], input[type="button"]) {
|
|
107
|
+
--btn-padding-y: var(--size-xs);
|
|
108
|
+
--btn-padding-x: var(--size-sm);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Inter-item divider — SOFT --border-block-start primitive read with
|
|
112
|
+
// `none` fallback. Default theme: no divider. Themes that opt in
|
|
113
|
+
// (Glass) rebind --border-block-start in scope.
|
|
114
|
+
li + li {
|
|
115
|
+
border-block-start: var(--border-block-start, none);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── Separator ─────────────────────────────────────
|
|
119
|
+
hr {
|
|
120
|
+
border: none;
|
|
121
|
+
border-block-start: var(--border-block-start, var(--border-width) solid var(--color-border));
|
|
122
|
+
--margin-block: var(--size-xs);
|
|
123
|
+
margin-block: var(--margin-block);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── Dropdown menu — floating panel + menu items ───────
|
|
128
|
+
// Dropdown-specific chrome (anchor-positioned absolute panel) plus
|
|
129
|
+
// the shared menu-items recipe.
|
|
130
|
+
|
|
131
|
+
@mixin dropdown-menu {
|
|
132
|
+
@include floating-panel;
|
|
133
|
+
@include absolute;
|
|
134
|
+
right: 0;
|
|
135
|
+
top: 100%;
|
|
136
|
+
--margin-block: var(--size-xs);
|
|
137
|
+
margin-top: var(--margin-block);
|
|
138
|
+
--padding-y: var(--size-xs);
|
|
139
|
+
padding-block: var(--padding-y);
|
|
140
|
+
min-width: 10rem;
|
|
141
|
+
|
|
142
|
+
@include menu-items;
|
|
143
|
+
}
|