@astronox/core 0.3.1 → 0.4.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 +2 -2
- package/package.json +1 -1
- package/src/ads/adsense/AdsenseHead.astro +55 -0
- package/src/ads/adsense/AdsenseUnit.astro +45 -0
- package/src/ads/adsense/index.ts +3 -1
- package/src/ads/adsense/provider.ts +41 -0
- package/src/ads/adsense/runtime.ts +284 -0
- package/src/ads/contracts.ts +67 -5
- package/src/components/content/CardHeader/CardHeader.astro +20 -0
- package/src/components/content/CardHeader/CardHeader.css +21 -0
- package/src/components/feedback/Tooltip/tooltipController.ts +37 -5
- package/src/components/monetization/AdSlot/AdSlot.astro +0 -3
- package/src/components/monetization/AdSlot/AdSlot.css +5 -28
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.astro +2 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.css +12 -6
- package/src/components/navigation/Footer/Footer.astro +8 -0
- package/src/components/navigation/Footer/Footer.css +20 -6
- package/src/components/navigation/Footer/contracts.ts +1 -0
- package/src/composition/Grid/Grid.css +1 -1
- package/src/composition/SurfaceGroup/SurfaceGroup.astro +2 -1
- package/src/composition/SurfaceGroup/SurfaceGroup.css +18 -18
- package/src/composition/SurfaceGroup/stickySurfaceGroupController.ts +9 -0
- package/src/config/environment.ts +66 -2
- package/src/layouts/BaseLayout.astro +6 -1
- package/src/patterns/Combobox/comboboxController.ts +51 -12
- package/src/patterns/Dropdown/dropdownController.ts +17 -6
- package/src/patterns/Fullscreen/FullscreenControl.astro +3 -30
- package/src/patterns/Fullscreen/fullscreenController.ts +18 -22
- package/src/patterns/Table/Table.css +8 -2
- package/src/patterns/Table/TableFilterPanel.astro +84 -0
- package/src/patterns/Table/TableFilterPanel.css +139 -0
- package/src/patterns/Table/TableSkeleton.css +0 -3
- package/src/patterns/Table/tableFilterPanelController.ts +91 -0
- package/src/patterns/Table/tableProgressiveController.ts +78 -16
- package/src/patterns/Table/tableScrollPosition.ts +41 -0
- package/src/patterns/Table/tableSortController.ts +6 -3
- package/src/patterns/index.ts +2 -0
- package/src/styles/style.css +1 -0
- package/src/ads/adsense.ts +0 -112
|
@@ -352,6 +352,7 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
352
352
|
let activeOption: HTMLButtonElement | null = null;
|
|
353
353
|
let positionFrame: number | null = null;
|
|
354
354
|
let viewportListenersAttached = false;
|
|
355
|
+
let openAtPointerDown: boolean | null = null;
|
|
355
356
|
|
|
356
357
|
const isOpen = (): boolean => root.hasAttribute('data-combobox-open');
|
|
357
358
|
|
|
@@ -391,6 +392,18 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
391
392
|
}
|
|
392
393
|
};
|
|
393
394
|
|
|
395
|
+
const scrollOptionIntoView = (option: HTMLElement | null): void => {
|
|
396
|
+
if (!option) return;
|
|
397
|
+
const optionsRect = optionsRoot.getBoundingClientRect();
|
|
398
|
+
const optionRect = option.getBoundingClientRect();
|
|
399
|
+
|
|
400
|
+
if (optionRect.top < optionsRect.top) {
|
|
401
|
+
optionsRoot.scrollTop -= optionsRect.top - optionRect.top;
|
|
402
|
+
} else if (optionRect.bottom > optionsRect.bottom) {
|
|
403
|
+
optionsRoot.scrollTop += optionRect.bottom - optionsRect.bottom;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
394
407
|
const positionPanel = (): void => {
|
|
395
408
|
if (!isOpen()) return;
|
|
396
409
|
|
|
@@ -490,7 +503,13 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
490
503
|
const target = event.target;
|
|
491
504
|
if (target instanceof Node && panel.contains(target)) return;
|
|
492
505
|
|
|
493
|
-
|
|
506
|
+
const siteViewport = document.querySelector<HTMLElement>(
|
|
507
|
+
'[data-site-viewport]',
|
|
508
|
+
);
|
|
509
|
+
const isSiteScroll = siteViewport
|
|
510
|
+
? target === siteViewport
|
|
511
|
+
: target === document || target === window;
|
|
512
|
+
if (isSiteScroll && window.matchMedia(compactViewportQuery).matches) {
|
|
494
513
|
close();
|
|
495
514
|
blurFocusedDescendant();
|
|
496
515
|
return;
|
|
@@ -547,6 +566,10 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
547
566
|
resetFilter();
|
|
548
567
|
if (panel.matches(':popover-open')) panel.hidePopover();
|
|
549
568
|
panel.hidden = true;
|
|
569
|
+
panel.style.removeProperty('--combobox-panel-inline-start');
|
|
570
|
+
panel.style.removeProperty('--combobox-panel-block-start');
|
|
571
|
+
panel.style.removeProperty('--combobox-panel-inline-size');
|
|
572
|
+
panel.style.removeProperty('--combobox-panel-available-block-size');
|
|
550
573
|
|
|
551
574
|
if (restoreFocus) owner.focus();
|
|
552
575
|
};
|
|
@@ -573,7 +596,7 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
573
596
|
(option) => option.getAttribute('aria-selected') === 'true',
|
|
574
597
|
) ?? null;
|
|
575
598
|
setActiveOption(null);
|
|
576
|
-
selectedOption
|
|
599
|
+
scrollOptionIntoView(selectedOption);
|
|
577
600
|
|
|
578
601
|
if (focusSearch && search) {
|
|
579
602
|
window.requestAnimationFrame(() => {
|
|
@@ -601,7 +624,7 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
601
624
|
visibleOptions.length;
|
|
602
625
|
const nextOption = visibleOptions[nextIndex] ?? null;
|
|
603
626
|
setActiveOption(nextOption);
|
|
604
|
-
nextOption
|
|
627
|
+
scrollOptionIntoView(nextOption);
|
|
605
628
|
};
|
|
606
629
|
|
|
607
630
|
const selectOption = (
|
|
@@ -687,13 +710,23 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
687
710
|
};
|
|
688
711
|
|
|
689
712
|
const toggleControl = (restoreFocus = true): void => {
|
|
690
|
-
|
|
713
|
+
const wasOpen = openAtPointerDown ?? isOpen();
|
|
714
|
+
openAtPointerDown = null;
|
|
715
|
+
if (wasOpen) {
|
|
691
716
|
close(restoreFocus);
|
|
692
717
|
} else {
|
|
693
718
|
open();
|
|
694
719
|
}
|
|
695
720
|
};
|
|
696
721
|
|
|
722
|
+
const handleOwnerPointerDown = (): void => {
|
|
723
|
+
openAtPointerDown = isOpen();
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
const handleOwnerPointerCancel = (): void => {
|
|
727
|
+
openAtPointerDown = null;
|
|
728
|
+
};
|
|
729
|
+
|
|
697
730
|
const handleOptionsTriggerClick = (): void => {
|
|
698
731
|
toggleControl(false);
|
|
699
732
|
};
|
|
@@ -703,6 +736,7 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
703
736
|
};
|
|
704
737
|
|
|
705
738
|
const handleOwnerKeydown = (event: KeyboardEvent): void => {
|
|
739
|
+
openAtPointerDown = null;
|
|
706
740
|
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
707
741
|
event.preventDefault();
|
|
708
742
|
if (!isOpen()) {
|
|
@@ -799,12 +833,9 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
799
833
|
const handleNumberInputClick = (): void => {
|
|
800
834
|
if (!numberInput) return;
|
|
801
835
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
open(false);
|
|
806
|
-
numberInput.select();
|
|
807
|
-
}
|
|
836
|
+
const wasOpen = openAtPointerDown ?? isOpen();
|
|
837
|
+
toggleControl(false);
|
|
838
|
+
if (!wasOpen) numberInput.select();
|
|
808
839
|
};
|
|
809
840
|
|
|
810
841
|
const handleNumberInput = (): void => {
|
|
@@ -825,7 +856,7 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
825
856
|
});
|
|
826
857
|
}
|
|
827
858
|
setActiveOption(matchingOption ?? null);
|
|
828
|
-
matchingOption
|
|
859
|
+
scrollOptionIntoView(matchingOption ?? null);
|
|
829
860
|
};
|
|
830
861
|
|
|
831
862
|
const handleNumberInputChange = (): void => {
|
|
@@ -851,7 +882,7 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
851
882
|
(option) => option.getAttribute('aria-selected') === 'true',
|
|
852
883
|
) ?? null;
|
|
853
884
|
setActiveOption(selectedOption);
|
|
854
|
-
selectedOption
|
|
885
|
+
scrollOptionIntoView(selectedOption);
|
|
855
886
|
}
|
|
856
887
|
};
|
|
857
888
|
|
|
@@ -877,6 +908,8 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
877
908
|
|
|
878
909
|
if (mode === 'options' && trigger && search) {
|
|
879
910
|
control.addEventListener('click', handleOptionsControlClick);
|
|
911
|
+
trigger.addEventListener('pointerdown', handleOwnerPointerDown);
|
|
912
|
+
trigger.addEventListener('pointercancel', handleOwnerPointerCancel);
|
|
880
913
|
trigger.addEventListener('click', handleOptionsTriggerClick);
|
|
881
914
|
trigger.addEventListener('keydown', handleOwnerKeydown);
|
|
882
915
|
search.addEventListener('input', filterOptions);
|
|
@@ -884,6 +917,8 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
884
917
|
}
|
|
885
918
|
|
|
886
919
|
if (mode === 'number' && numberInput) {
|
|
920
|
+
numberInput.addEventListener('pointerdown', handleOwnerPointerDown);
|
|
921
|
+
numberInput.addEventListener('pointercancel', handleOwnerPointerCancel);
|
|
887
922
|
numberInput.addEventListener('click', handleNumberInputClick);
|
|
888
923
|
numberInput.addEventListener('input', handleNumberInput);
|
|
889
924
|
numberInput.addEventListener('change', handleNumberInputChange);
|
|
@@ -908,10 +943,14 @@ export function initCombobox(root: HTMLElement): () => void {
|
|
|
908
943
|
detachViewportListeners();
|
|
909
944
|
controlResizeObserver?.disconnect();
|
|
910
945
|
control.removeEventListener('click', handleOptionsControlClick);
|
|
946
|
+
trigger?.removeEventListener('pointerdown', handleOwnerPointerDown);
|
|
947
|
+
trigger?.removeEventListener('pointercancel', handleOwnerPointerCancel);
|
|
911
948
|
trigger?.removeEventListener('click', handleOptionsTriggerClick);
|
|
912
949
|
trigger?.removeEventListener('keydown', handleOwnerKeydown);
|
|
913
950
|
search?.removeEventListener('input', filterOptions);
|
|
914
951
|
search?.removeEventListener('keydown', handleSearchKeydown);
|
|
952
|
+
numberInput?.removeEventListener('pointerdown', handleOwnerPointerDown);
|
|
953
|
+
numberInput?.removeEventListener('pointercancel', handleOwnerPointerCancel);
|
|
915
954
|
numberInput?.removeEventListener('click', handleNumberInputClick);
|
|
916
955
|
numberInput?.removeEventListener('input', handleNumberInput);
|
|
917
956
|
numberInput?.removeEventListener('change', handleNumberInputChange);
|
|
@@ -25,6 +25,7 @@ export function initDropdown(root: HTMLElement): () => void {
|
|
|
25
25
|
const triggerMode = getTriggerMode(root);
|
|
26
26
|
const hoverMedia = window.matchMedia('(hover: hover)');
|
|
27
27
|
let isOpen = !content.hidden;
|
|
28
|
+
let openAtPointerDown: boolean | null = null;
|
|
28
29
|
|
|
29
30
|
const canOpenOnHover = (): boolean =>
|
|
30
31
|
triggerMode === 'hover' && hoverMedia.matches;
|
|
@@ -61,12 +62,17 @@ export function initDropdown(root: HTMLElement): () => void {
|
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
const handleTriggerClick = (): void => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
const wasOpen = openAtPointerDown ?? isOpen;
|
|
66
|
+
openAtPointerDown = null;
|
|
67
|
+
setOpen(!wasOpen);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const handleTriggerPointerDown = (): void => {
|
|
71
|
+
openAtPointerDown = isOpen;
|
|
72
|
+
};
|
|
68
73
|
|
|
69
|
-
|
|
74
|
+
const handleTriggerPointerCancel = (): void => {
|
|
75
|
+
openAtPointerDown = null;
|
|
70
76
|
};
|
|
71
77
|
|
|
72
78
|
const handleRootPointerEnter = (): void => {
|
|
@@ -88,12 +94,13 @@ export function initDropdown(root: HTMLElement): () => void {
|
|
|
88
94
|
};
|
|
89
95
|
|
|
90
96
|
const handleTriggerFocus = (): void => {
|
|
91
|
-
if (
|
|
97
|
+
if (canOpenOnHover()) {
|
|
92
98
|
setOpen(true);
|
|
93
99
|
}
|
|
94
100
|
};
|
|
95
101
|
|
|
96
102
|
const handleTriggerKeydown = (event: KeyboardEvent): void => {
|
|
103
|
+
openAtPointerDown = null;
|
|
97
104
|
if (event.key === 'ArrowDown') {
|
|
98
105
|
event.preventDefault();
|
|
99
106
|
openAndFocusFirstItem();
|
|
@@ -131,6 +138,8 @@ export function initDropdown(root: HTMLElement): () => void {
|
|
|
131
138
|
|
|
132
139
|
setOpen(isOpen);
|
|
133
140
|
|
|
141
|
+
trigger.addEventListener('pointerdown', handleTriggerPointerDown);
|
|
142
|
+
trigger.addEventListener('pointercancel', handleTriggerPointerCancel);
|
|
134
143
|
trigger.addEventListener('click', handleTriggerClick);
|
|
135
144
|
trigger.addEventListener('focus', handleTriggerFocus);
|
|
136
145
|
trigger.addEventListener('keydown', handleTriggerKeydown);
|
|
@@ -143,6 +152,8 @@ export function initDropdown(root: HTMLElement): () => void {
|
|
|
143
152
|
|
|
144
153
|
return () => {
|
|
145
154
|
close();
|
|
155
|
+
trigger.removeEventListener('pointerdown', handleTriggerPointerDown);
|
|
156
|
+
trigger.removeEventListener('pointercancel', handleTriggerPointerCancel);
|
|
146
157
|
trigger.removeEventListener('click', handleTriggerClick);
|
|
147
158
|
trigger.removeEventListener('focus', handleTriggerFocus);
|
|
148
159
|
trigger.removeEventListener('keydown', handleTriggerKeydown);
|
|
@@ -4,13 +4,6 @@ import type { Tone } from '../../theme/contracts';
|
|
|
4
4
|
import Button from '../../components/actions/Button/Button.astro';
|
|
5
5
|
import EnterFullscreenIcon from '../../components/icons/EnterFullscreenIcon.astro';
|
|
6
6
|
import ExitFullscreenIcon from '../../components/icons/ExitFullscreenIcon.astro';
|
|
7
|
-
import ExternalLinkIcon from '../../components/icons/ExternalLinkIcon.astro';
|
|
8
|
-
|
|
9
|
-
type FullscreenUnsupportedFallback = {
|
|
10
|
-
href: string;
|
|
11
|
-
label: string;
|
|
12
|
-
target?: '_blank' | '_self';
|
|
13
|
-
};
|
|
14
7
|
|
|
15
8
|
type Props = Omit<
|
|
16
9
|
HTMLAttributes<'button'>,
|
|
@@ -21,7 +14,7 @@ type Props = Omit<
|
|
|
21
14
|
exitLabel: string;
|
|
22
15
|
targetId: string;
|
|
23
16
|
tone?: Tone;
|
|
24
|
-
|
|
17
|
+
unavailableLabel?: string;
|
|
25
18
|
};
|
|
26
19
|
|
|
27
20
|
const {
|
|
@@ -30,7 +23,7 @@ const {
|
|
|
30
23
|
exitLabel,
|
|
31
24
|
targetId,
|
|
32
25
|
tone,
|
|
33
|
-
|
|
26
|
+
unavailableLabel = enterLabel,
|
|
34
27
|
...attrs
|
|
35
28
|
}: Props = Astro.props;
|
|
36
29
|
const resolvedClass = ['fullscreen-control', className]
|
|
@@ -54,6 +47,7 @@ const resolvedClass = ['fullscreen-control', className]
|
|
|
54
47
|
data-fullscreen-enter-label={enterLabel}
|
|
55
48
|
data-fullscreen-exit-label={exitLabel}
|
|
56
49
|
data-fullscreen-target={targetId}
|
|
50
|
+
data-fullscreen-unavailable-label={unavailableLabel}
|
|
57
51
|
>
|
|
58
52
|
<span class="fullscreen-control__icon" data-fullscreen-enter-icon>
|
|
59
53
|
<EnterFullscreenIcon />
|
|
@@ -63,27 +57,6 @@ const resolvedClass = ['fullscreen-control', className]
|
|
|
63
57
|
</span>
|
|
64
58
|
</Button>
|
|
65
59
|
|
|
66
|
-
{
|
|
67
|
-
unsupportedFallback ? (
|
|
68
|
-
<Button
|
|
69
|
-
href={unsupportedFallback.href}
|
|
70
|
-
target={unsupportedFallback.target ?? '_blank'}
|
|
71
|
-
borderless
|
|
72
|
-
full
|
|
73
|
-
iconOnly
|
|
74
|
-
tone={tone}
|
|
75
|
-
aria-label={unsupportedFallback.label}
|
|
76
|
-
title={unsupportedFallback.label}
|
|
77
|
-
hidden
|
|
78
|
-
data-fullscreen-unsupported-fallback
|
|
79
|
-
>
|
|
80
|
-
<span class="fullscreen-control__icon">
|
|
81
|
-
<ExternalLinkIcon />
|
|
82
|
-
</span>
|
|
83
|
-
</Button>
|
|
84
|
-
) : null
|
|
85
|
-
}
|
|
86
|
-
|
|
87
60
|
<script>
|
|
88
61
|
import { initFullscreenControl } from './fullscreenController';
|
|
89
62
|
|
|
@@ -6,32 +6,30 @@ export function initFullscreenControl(control: HTMLButtonElement): () => void {
|
|
|
6
6
|
|
|
7
7
|
const targetId = control.dataset.fullscreenTarget;
|
|
8
8
|
const target = targetId ? document.getElementById(targetId) : null;
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
control.
|
|
17
|
-
|
|
9
|
+
const initiallyDisabled = control.disabled;
|
|
10
|
+
const enterLabel = control.dataset.fullscreenEnterLabel ?? '';
|
|
11
|
+
const exitLabel = control.dataset.fullscreenExitLabel ?? '';
|
|
12
|
+
const unavailableLabel =
|
|
13
|
+
control.dataset.fullscreenUnavailableLabel ?? enterLabel;
|
|
14
|
+
const setUnavailable = (): void => {
|
|
15
|
+
control.disabled = true;
|
|
16
|
+
control.setAttribute('aria-disabled', 'true');
|
|
17
|
+
control.setAttribute('aria-label', unavailableLabel);
|
|
18
|
+
control.setAttribute('aria-pressed', 'false');
|
|
19
|
+
control.title = unavailableLabel;
|
|
20
|
+
control.setAttribute('data-fullscreen-unavailable', '');
|
|
18
21
|
};
|
|
19
22
|
if (
|
|
20
23
|
document.fullscreenEnabled !== true ||
|
|
21
24
|
!target ||
|
|
22
25
|
typeof target.requestFullscreen !== 'function'
|
|
23
26
|
) {
|
|
24
|
-
|
|
27
|
+
setUnavailable();
|
|
25
28
|
return noop;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
control.hidden = false;
|
|
29
|
-
if (fallback instanceof HTMLAnchorElement) fallback.hidden = true;
|
|
30
31
|
control.setAttribute(initializedAttribute, 'true');
|
|
31
32
|
|
|
32
|
-
const enterLabel = control.dataset.fullscreenEnterLabel ?? '';
|
|
33
|
-
const exitLabel = control.dataset.fullscreenExitLabel ?? '';
|
|
34
|
-
|
|
35
33
|
const sync = (): void => {
|
|
36
34
|
const active = document.fullscreenElement === target;
|
|
37
35
|
const label = active ? exitLabel : enterLabel;
|
|
@@ -49,11 +47,7 @@ export function initFullscreenControl(control: HTMLButtonElement): () => void {
|
|
|
49
47
|
await target.requestFullscreen();
|
|
50
48
|
}
|
|
51
49
|
} catch {
|
|
52
|
-
|
|
53
|
-
showUnsupportedFallback();
|
|
54
|
-
} else {
|
|
55
|
-
sync();
|
|
56
|
-
}
|
|
50
|
+
setUnavailable();
|
|
57
51
|
}
|
|
58
52
|
};
|
|
59
53
|
|
|
@@ -65,7 +59,9 @@ export function initFullscreenControl(control: HTMLButtonElement): () => void {
|
|
|
65
59
|
control.removeEventListener('click', toggle);
|
|
66
60
|
document.removeEventListener('fullscreenchange', sync);
|
|
67
61
|
control.removeAttribute(initializedAttribute);
|
|
68
|
-
control.
|
|
69
|
-
|
|
62
|
+
control.removeAttribute('data-fullscreen-unavailable');
|
|
63
|
+
control.removeAttribute('aria-disabled');
|
|
64
|
+
control.disabled = initiallyDisabled;
|
|
65
|
+
sync();
|
|
70
66
|
};
|
|
71
67
|
}
|
|
@@ -49,7 +49,10 @@
|
|
|
49
49
|
|
|
50
50
|
.table[data-table-sticky-header]:has([data-sticky-surface])
|
|
51
51
|
[data-table-header] {
|
|
52
|
-
top:
|
|
52
|
+
top: var(
|
|
53
|
+
--surface-group-sticky-occlusion-size,
|
|
54
|
+
calc(var(--nox-layout-gap) + var(--nox-section-responsive-block-size))
|
|
55
|
+
);
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
@container nox-table (width < 32rem) {
|
|
@@ -60,7 +63,10 @@
|
|
|
60
63
|
|
|
61
64
|
.table[data-table-sticky-header]:has([data-sticky-surface])
|
|
62
65
|
[data-table-header] {
|
|
63
|
-
top:
|
|
66
|
+
top: var(
|
|
67
|
+
--surface-group-sticky-occlusion-size,
|
|
68
|
+
calc(var(--nox-layout-gap) + var(--nox-section-compact-block-size))
|
|
69
|
+
);
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
72
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import type { DisplayMedia } from '../../display/contracts';
|
|
4
|
+
import type { Tone } from '../../theme/contracts';
|
|
5
|
+
import type {
|
|
6
|
+
AstroComponent,
|
|
7
|
+
HeadingLevel,
|
|
8
|
+
} from '../../components/widgetTypes';
|
|
9
|
+
import ArrowDownIcon from '../../components/icons/ArrowDownIcon.astro';
|
|
10
|
+
import CardBody from '../../components/content/CardBody/CardBody.astro';
|
|
11
|
+
import CardHeader from '../../components/content/CardHeader/CardHeader.astro';
|
|
12
|
+
import SurfaceGroup from '../../composition/SurfaceGroup/SurfaceGroup.astro';
|
|
13
|
+
|
|
14
|
+
type HeaderVisual =
|
|
15
|
+
| { icon?: AstroComponent; media?: never }
|
|
16
|
+
| { icon?: never; media: DisplayMedia };
|
|
17
|
+
|
|
18
|
+
type Props = Omit<HTMLAttributes<'div'>, 'id'> &
|
|
19
|
+
HeaderVisual & {
|
|
20
|
+
id: string;
|
|
21
|
+
title: string;
|
|
22
|
+
collapseAt?: 'sm' | 'md' | 'lg' | 'xl';
|
|
23
|
+
headingLevel?: HeadingLevel;
|
|
24
|
+
tone?: Tone;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
class: className,
|
|
29
|
+
collapseAt = 'lg',
|
|
30
|
+
headingLevel = 2,
|
|
31
|
+
icon,
|
|
32
|
+
id,
|
|
33
|
+
media,
|
|
34
|
+
title,
|
|
35
|
+
tone,
|
|
36
|
+
...attrs
|
|
37
|
+
}: Props = Astro.props;
|
|
38
|
+
const contentId = `${id}-content`;
|
|
39
|
+
const hasAction = Astro.slots.has('action');
|
|
40
|
+
const headerVisual: HeaderVisual = media ? { media } : { icon };
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
<SurfaceGroup
|
|
44
|
+
{...attrs}
|
|
45
|
+
id={id}
|
|
46
|
+
class:list={['table-filter-panel', className]}
|
|
47
|
+
data-table-filter-panel
|
|
48
|
+
data-table-filter-panel-collapse-at={collapseAt}
|
|
49
|
+
tone={tone}
|
|
50
|
+
>
|
|
51
|
+
<CardHeader
|
|
52
|
+
{...headerVisual}
|
|
53
|
+
title={title}
|
|
54
|
+
headingLevel={headingLevel}
|
|
55
|
+
tone={tone}
|
|
56
|
+
trigger={{
|
|
57
|
+
controls: contentId,
|
|
58
|
+
expanded: false,
|
|
59
|
+
label: title,
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
<span slot="end" class="table-filter-panel__indicator" aria-hidden="true">
|
|
63
|
+
<ArrowDownIcon />
|
|
64
|
+
</span>
|
|
65
|
+
{
|
|
66
|
+
hasAction ? (
|
|
67
|
+
<Fragment slot="action">
|
|
68
|
+
<slot name="action" />
|
|
69
|
+
</Fragment>
|
|
70
|
+
) : null
|
|
71
|
+
}
|
|
72
|
+
</CardHeader>
|
|
73
|
+
<CardBody id={contentId} overlayBoundary data-table-filter-panel-content>
|
|
74
|
+
<slot />
|
|
75
|
+
</CardBody>
|
|
76
|
+
</SurfaceGroup>
|
|
77
|
+
|
|
78
|
+
<script>
|
|
79
|
+
import { initTableFilterPanel } from './tableFilterPanelController';
|
|
80
|
+
|
|
81
|
+
for (const root of document.querySelectorAll('[data-table-filter-panel]')) {
|
|
82
|
+
if (root instanceof HTMLElement) initTableFilterPanel(root);
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
.table-filter-panel__indicator {
|
|
2
|
+
display: none;
|
|
3
|
+
inline-size: 1.25rem;
|
|
4
|
+
block-size: 1.25rem;
|
|
5
|
+
flex: none;
|
|
6
|
+
transition: transform var(--nox-motion-duration-sm)
|
|
7
|
+
var(--nox-motion-easing-standard);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.table-filter-panel .card-header__trigger {
|
|
11
|
+
display: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.table-filter-panel__indicator > svg {
|
|
15
|
+
display: block;
|
|
16
|
+
inline-size: 100%;
|
|
17
|
+
block-size: 100%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.table-filter-panel[data-table-filter-panel-compact]
|
|
21
|
+
.table-filter-panel__indicator {
|
|
22
|
+
display: block;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.table-filter-panel[data-table-filter-panel-compact] .card-header__trigger {
|
|
26
|
+
display: block;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.table-filter-panel[data-table-filter-panel-compact]:not(
|
|
30
|
+
[data-table-filter-panel-open]
|
|
31
|
+
) {
|
|
32
|
+
--surface-group-connected-border-end-radius: var(--nox-radius-md);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.table-filter-panel[data-table-filter-panel-compact]:not(
|
|
36
|
+
[data-table-filter-panel-open]
|
|
37
|
+
)
|
|
38
|
+
> [data-table-filter-panel-content] {
|
|
39
|
+
display: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.table-filter-panel[data-table-filter-panel-compact][data-table-filter-panel-open]
|
|
43
|
+
.table-filter-panel__indicator {
|
|
44
|
+
transform: rotate(180deg);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@container nox-grid (width < 24rem) {
|
|
48
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='sm']:not(
|
|
49
|
+
[data-table-filter-panel-open]
|
|
50
|
+
) {
|
|
51
|
+
--surface-group-connected-border-end-radius: var(--nox-radius-md);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='sm']
|
|
55
|
+
.table-filter-panel__indicator,
|
|
56
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='sm']
|
|
57
|
+
.card-header__trigger {
|
|
58
|
+
display: block;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='sm']:not(
|
|
62
|
+
[data-table-filter-panel-open]
|
|
63
|
+
)
|
|
64
|
+
> [data-table-filter-panel-content] {
|
|
65
|
+
display: none;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@container nox-grid (width < 36rem) {
|
|
70
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='md']:not(
|
|
71
|
+
[data-table-filter-panel-open]
|
|
72
|
+
) {
|
|
73
|
+
--surface-group-connected-border-end-radius: var(--nox-radius-md);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='md']
|
|
77
|
+
.table-filter-panel__indicator,
|
|
78
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='md']
|
|
79
|
+
.card-header__trigger {
|
|
80
|
+
display: block;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='md']:not(
|
|
84
|
+
[data-table-filter-panel-open]
|
|
85
|
+
)
|
|
86
|
+
> [data-table-filter-panel-content] {
|
|
87
|
+
display: none;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@container nox-grid (width < 44rem) {
|
|
92
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='lg']:not(
|
|
93
|
+
[data-table-filter-panel-open]
|
|
94
|
+
) {
|
|
95
|
+
--surface-group-connected-border-end-radius: var(--nox-radius-md);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='lg']
|
|
99
|
+
.table-filter-panel__indicator,
|
|
100
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='lg']
|
|
101
|
+
.card-header__trigger {
|
|
102
|
+
display: block;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='lg']:not(
|
|
106
|
+
[data-table-filter-panel-open]
|
|
107
|
+
)
|
|
108
|
+
> [data-table-filter-panel-content] {
|
|
109
|
+
display: none;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@container nox-grid (width < 54rem) {
|
|
114
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='xl']:not(
|
|
115
|
+
[data-table-filter-panel-open]
|
|
116
|
+
) {
|
|
117
|
+
--surface-group-connected-border-end-radius: var(--nox-radius-md);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='xl']
|
|
121
|
+
.table-filter-panel__indicator,
|
|
122
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='xl']
|
|
123
|
+
.card-header__trigger {
|
|
124
|
+
display: block;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.table-filter-panel[data-table-filter-panel-collapse-at='xl']:not(
|
|
128
|
+
[data-table-filter-panel-open]
|
|
129
|
+
)
|
|
130
|
+
> [data-table-filter-panel-content] {
|
|
131
|
+
display: none;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@media (prefers-reduced-motion: reduce) {
|
|
136
|
+
.table-filter-panel__indicator {
|
|
137
|
+
transition: none;
|
|
138
|
+
}
|
|
139
|
+
}
|