@timmo001/groundwork-ui 0.9.14 → 0.9.16
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/custom-elements.json +44 -1
- package/package.json +1 -1
- package/src/element.ts +4 -1
- package/src/gw-sheet.ts +0 -1
- package/src/index.ts +2 -0
- package/src/scrollbars.ts +148 -0
package/custom-elements.json
CHANGED
|
@@ -16675,7 +16675,7 @@
|
|
|
16675
16675
|
"text": "readonly CSSStyleSheet[]"
|
|
16676
16676
|
},
|
|
16677
16677
|
"static": true,
|
|
16678
|
-
"default": "[ base, css` [popover] { position: fixed; box-sizing: border-box; flex-direction: column; padding: 0; margin: 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: 0 solid var(--gw-color-border-normal); box-shadow: var(--gw-shadow-l); transition: translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; translate: 0 0; } /* The page dims as the sheet slides in, and back as it leaves */ [popover]::backdrop { background: rgb(0 0 0 / 0%); transition: background-color var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open::backdrop { background: rgb(0 0 0 / 40%); } @starting-style { [popover]:popover-open::backdrop { background: rgb(0 0 0 / 0%); } } /* Full height from the start or end edge */ [data-side=\"start\"], [data-side=\"end\"] { inset: 0 auto; width: min(var(--gw-sheet-width, 20rem), 100vw); height: 100%; max-height: none; } [data-side=\"start\"] { inset-inline-start: 0; border-inline-end-width: max(var(--gw-border-width), 1px); translate: -100% 0; } [data-side=\"end\"] { inset-inline-end: 0; border-inline-start-width: max(var(--gw-border-width), 1px); translate: 100% 0; } :host(:dir(rtl)) [data-side=\"start\"] { translate: 100% 0; } :host(:dir(rtl)) [data-side=\"end\"] { translate: -100% 0; } /* Up from the bottom, with room for the phone's home bar */ [data-side=\"bottom\"] { inset: auto 0 0; width: 100%; max-width: none; max-height: var(--gw-sheet-height, 85vh); padding-block-end: env(safe-area-inset-bottom); border-block-start-width: max(var(--gw-border-width), 1px); border-start-start-radius: var(--gw-radius-panel); border-start-end-radius: var(--gw-radius-panel); translate: 0 100%; } @starting-style { [data-side=\"start\"]:popover-open { translate: -100% 0; } [data-side=\"end\"]:popover-open { translate: 100% 0; } [data-side=\"bottom\"]:popover-open { translate: 0 100%; } } header { display: flex; flex: none; gap: var(--gw-space-2); align-items: center; padding: var(--gw-space-2) var(--gw-space-3); border-block-end: var(--gw-border-width) solid var(--gw-color-border-quiet); } h2 { flex: 1; min-width: 0; margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .body { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-3); min-height: 0; padding: var(--gw-space-4); /* It scrolls down only, so content reaching into its padding never makes it scroll sideways */ overflow: hidden auto; overscroll-behavior: contain;
|
|
16678
|
+
"default": "[ base, css` [popover] { position: fixed; box-sizing: border-box; flex-direction: column; padding: 0; margin: 0; overflow: hidden; color: var(--gw-color-text-primary); background: var(--gw-color-surface-overlay); border: 0 solid var(--gw-color-border-normal); box-shadow: var(--gw-shadow-l); transition: translate var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open { display: flex; translate: 0 0; } /* The page dims as the sheet slides in, and back as it leaves */ [popover]::backdrop { background: rgb(0 0 0 / 0%); transition: background-color var(--gw-motion-normal) var(--gw-motion-easing), display var(--gw-motion-normal) allow-discrete, overlay var(--gw-motion-normal) allow-discrete; } [popover]:popover-open::backdrop { background: rgb(0 0 0 / 40%); } @starting-style { [popover]:popover-open::backdrop { background: rgb(0 0 0 / 0%); } } /* Full height from the start or end edge */ [data-side=\"start\"], [data-side=\"end\"] { inset: 0 auto; width: min(var(--gw-sheet-width, 20rem), 100vw); height: 100%; max-height: none; } [data-side=\"start\"] { inset-inline-start: 0; border-inline-end-width: max(var(--gw-border-width), 1px); translate: -100% 0; } [data-side=\"end\"] { inset-inline-end: 0; border-inline-start-width: max(var(--gw-border-width), 1px); translate: 100% 0; } :host(:dir(rtl)) [data-side=\"start\"] { translate: 100% 0; } :host(:dir(rtl)) [data-side=\"end\"] { translate: -100% 0; } /* Up from the bottom, with room for the phone's home bar */ [data-side=\"bottom\"] { inset: auto 0 0; width: 100%; max-width: none; max-height: var(--gw-sheet-height, 85vh); padding-block-end: env(safe-area-inset-bottom); border-block-start-width: max(var(--gw-border-width), 1px); border-start-start-radius: var(--gw-radius-panel); border-start-end-radius: var(--gw-radius-panel); translate: 0 100%; } @starting-style { [data-side=\"start\"]:popover-open { translate: -100% 0; } [data-side=\"end\"]:popover-open { translate: 100% 0; } [data-side=\"bottom\"]:popover-open { translate: 0 100%; } } header { display: flex; flex: none; gap: var(--gw-space-2); align-items: center; padding: var(--gw-space-2) var(--gw-space-3); border-block-end: var(--gw-border-width) solid var(--gw-color-border-quiet); } h2 { flex: 1; min-width: 0; margin: 0; font-size: var(--gw-font-size-m); font-weight: var(--gw-font-weight-strong); color: var(--gw-color-text-strong); } .body { display: flex; flex: 1; flex-direction: column; gap: var(--gw-space-3); min-height: 0; padding: var(--gw-space-4); /* It scrolls down only, so content reaching into its padding never makes it scroll sideways */ overflow: hidden auto; overscroll-behavior: contain; } `, ]",
|
|
16679
16679
|
"inheritedFrom": {
|
|
16680
16680
|
"name": "GwElement",
|
|
16681
16681
|
"module": "src/element.ts"
|
|
@@ -20762,6 +20762,14 @@
|
|
|
20762
20762
|
"module": "src/styles.js"
|
|
20763
20763
|
}
|
|
20764
20764
|
},
|
|
20765
|
+
{
|
|
20766
|
+
"kind": "js",
|
|
20767
|
+
"name": "*",
|
|
20768
|
+
"declaration": {
|
|
20769
|
+
"name": "*",
|
|
20770
|
+
"module": "src/scrollbars.js"
|
|
20771
|
+
}
|
|
20772
|
+
},
|
|
20765
20773
|
{
|
|
20766
20774
|
"kind": "js",
|
|
20767
20775
|
"name": "*",
|
|
@@ -22839,6 +22847,41 @@
|
|
|
22839
22847
|
}
|
|
22840
22848
|
]
|
|
22841
22849
|
},
|
|
22850
|
+
{
|
|
22851
|
+
"kind": "javascript-module",
|
|
22852
|
+
"path": "src/scrollbars.ts",
|
|
22853
|
+
"declarations": [
|
|
22854
|
+
{
|
|
22855
|
+
"kind": "variable",
|
|
22856
|
+
"name": "scrollbars",
|
|
22857
|
+
"default": "new CSSStyleSheet()",
|
|
22858
|
+
"description": "The page's scrollbars for a shadow root: a small rounded thumb on a clear\ntrack with no arrow buttons, squaring off at the ends of its track. Every\n`gw-` element adopts it; add it to an app's own components' styles to match.\nThe `--gw-scrollbar-*` properties restyle it as they do the page's."
|
|
22859
|
+
},
|
|
22860
|
+
{
|
|
22861
|
+
"kind": "function",
|
|
22862
|
+
"name": "followPageScroll",
|
|
22863
|
+
"description": "Sets `--gw-scrollbar-top`, `-bottom`, `-left` and `-right` on the root as\nthe page scrolls, 0 against that end and 1 elsewhere, so the window's\nscrollbar thumb squares off at the ends like the others. The window's\nscrollbar doesn't repaint for the scroll-driven animation the themes give\nother scrollers. Every `gw-` element starts it when it's defined."
|
|
22864
|
+
}
|
|
22865
|
+
],
|
|
22866
|
+
"exports": [
|
|
22867
|
+
{
|
|
22868
|
+
"kind": "js",
|
|
22869
|
+
"name": "scrollbars",
|
|
22870
|
+
"declaration": {
|
|
22871
|
+
"name": "scrollbars",
|
|
22872
|
+
"module": "src/scrollbars.ts"
|
|
22873
|
+
}
|
|
22874
|
+
},
|
|
22875
|
+
{
|
|
22876
|
+
"kind": "js",
|
|
22877
|
+
"name": "followPageScroll",
|
|
22878
|
+
"declaration": {
|
|
22879
|
+
"name": "followPageScroll",
|
|
22880
|
+
"module": "src/scrollbars.ts"
|
|
22881
|
+
}
|
|
22882
|
+
}
|
|
22883
|
+
]
|
|
22884
|
+
},
|
|
22842
22885
|
{
|
|
22843
22886
|
"kind": "javascript-module",
|
|
22844
22887
|
"path": "src/styles.ts",
|
package/package.json
CHANGED
package/src/element.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
8
|
+
import { followPageScroll, scrollbars } from "./scrollbars.js";
|
|
8
9
|
|
|
9
10
|
/** How a property reads its attribute and writes it back. */
|
|
10
11
|
export interface PropertyOptions {
|
|
@@ -182,6 +183,8 @@ export class GwElement extends HTMLElement {
|
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
if (!customElements.get(tag)) customElements.define(tag, this);
|
|
186
|
+
|
|
187
|
+
followPageScroll();
|
|
185
188
|
}
|
|
186
189
|
|
|
187
190
|
readonly #values = new Map<string, PropertyValue>();
|
|
@@ -224,7 +227,7 @@ export class GwElement extends HTMLElement {
|
|
|
224
227
|
if (own.shadow) {
|
|
225
228
|
const root = this.attachShadow(own.shadowRootOptions);
|
|
226
229
|
|
|
227
|
-
root.adoptedStyleSheets = [...own.styles];
|
|
230
|
+
root.adoptedStyleSheets = [scrollbars, ...own.styles];
|
|
228
231
|
this.renderRoot = root;
|
|
229
232
|
} else {
|
|
230
233
|
this.renderRoot = this;
|
package/src/gw-sheet.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The small scrollbars `themes.css` gives the page, as a sheet for shadow
|
|
3
|
+
* roots, which the page's rules can't reach, and the page scroll tracking
|
|
4
|
+
* that squares the window's thumb off at its ends.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The page's scrollbars for a shadow root: a small rounded thumb on a clear
|
|
11
|
+
* track with no arrow buttons, squaring off at the ends of its track. Every
|
|
12
|
+
* `gw-` element adopts it; add it to an app's own components' styles to match.
|
|
13
|
+
* The `--gw-scrollbar-*` properties restyle it as they do the page's.
|
|
14
|
+
*/
|
|
15
|
+
export const scrollbars = new CSSStyleSheet();
|
|
16
|
+
|
|
17
|
+
scrollbars.replaceSync(`
|
|
18
|
+
@supports not selector(::-webkit-scrollbar) {
|
|
19
|
+
* {
|
|
20
|
+
scrollbar-width: thin;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
* {
|
|
25
|
+
animation:
|
|
26
|
+
gw-scrollbar-edges-y linear both,
|
|
27
|
+
gw-scrollbar-edges-x linear both;
|
|
28
|
+
animation-timeline: scroll(self y), scroll(self x);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@keyframes gw-scrollbar-edges-y {
|
|
32
|
+
0% {
|
|
33
|
+
--gw-scrollbar-top: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
1% {
|
|
37
|
+
--gw-scrollbar-top: 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
99% {
|
|
41
|
+
--gw-scrollbar-bottom: 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
100% {
|
|
45
|
+
--gw-scrollbar-bottom: 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@keyframes gw-scrollbar-edges-x {
|
|
50
|
+
0% {
|
|
51
|
+
--gw-scrollbar-left: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
1% {
|
|
55
|
+
--gw-scrollbar-left: 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
99% {
|
|
59
|
+
--gw-scrollbar-right: 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
100% {
|
|
63
|
+
--gw-scrollbar-right: 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
::-webkit-scrollbar {
|
|
68
|
+
width: var(--gw-scrollbar-size, 0.4rem);
|
|
69
|
+
height: var(--gw-scrollbar-size, 0.4rem);
|
|
70
|
+
background: var(--gw-scrollbar-track, transparent);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
::-webkit-scrollbar-thumb {
|
|
74
|
+
background: var(
|
|
75
|
+
--gw-scrollbar-thumb,
|
|
76
|
+
color-mix(
|
|
77
|
+
in oklab,
|
|
78
|
+
var(--gw-color-text-secondary, currentColor) 45%,
|
|
79
|
+
transparent
|
|
80
|
+
)
|
|
81
|
+
);
|
|
82
|
+
border-radius:
|
|
83
|
+
calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-top, 1))
|
|
84
|
+
calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-top, 1))
|
|
85
|
+
calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-bottom, 1))
|
|
86
|
+
calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-bottom, 1));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
::-webkit-scrollbar-thumb:horizontal {
|
|
90
|
+
border-radius:
|
|
91
|
+
calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-left, 1))
|
|
92
|
+
calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-right, 1))
|
|
93
|
+
calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-right, 1))
|
|
94
|
+
calc(var(--gw-scrollbar-size, 0.4rem) * var(--gw-scrollbar-left, 1));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
::-webkit-scrollbar-thumb:hover {
|
|
98
|
+
background: var(
|
|
99
|
+
--gw-scrollbar-thumb,
|
|
100
|
+
color-mix(
|
|
101
|
+
in oklab,
|
|
102
|
+
var(--gw-color-text-secondary, currentColor) 70%,
|
|
103
|
+
transparent
|
|
104
|
+
)
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
::-webkit-scrollbar-corner {
|
|
109
|
+
background: transparent;
|
|
110
|
+
}
|
|
111
|
+
`);
|
|
112
|
+
|
|
113
|
+
let followingPageScroll = false;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Sets `--gw-scrollbar-top`, `-bottom`, `-left` and `-right` on the root as
|
|
117
|
+
* the page scrolls, 0 against that end and 1 elsewhere, so the window's
|
|
118
|
+
* scrollbar thumb squares off at the ends like the others. The window's
|
|
119
|
+
* scrollbar doesn't repaint for the scroll-driven animation the themes give
|
|
120
|
+
* other scrollers. Every `gw-` element starts it when it's defined.
|
|
121
|
+
*/
|
|
122
|
+
export const followPageScroll = () => {
|
|
123
|
+
if (followingPageScroll) return;
|
|
124
|
+
|
|
125
|
+
followingPageScroll = true;
|
|
126
|
+
|
|
127
|
+
const root = document.documentElement;
|
|
128
|
+
|
|
129
|
+
const set = (edge: string, against: boolean) => {
|
|
130
|
+
const value = against ? "0" : "1";
|
|
131
|
+
|
|
132
|
+
if (root.style.getPropertyValue(`--gw-scrollbar-${edge}`) !== value)
|
|
133
|
+
root.style.setProperty(`--gw-scrollbar-${edge}`, value);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const update = () => {
|
|
137
|
+
const page = document.scrollingElement ?? root;
|
|
138
|
+
|
|
139
|
+
set("top", page.scrollTop <= 0);
|
|
140
|
+
set("bottom", page.scrollTop + page.clientHeight >= page.scrollHeight - 1);
|
|
141
|
+
set("left", page.scrollLeft <= 0);
|
|
142
|
+
set("right", page.scrollLeft + page.clientWidth >= page.scrollWidth - 1);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
update();
|
|
146
|
+
addEventListener("scroll", update, { passive: true });
|
|
147
|
+
new ResizeObserver(update).observe(root);
|
|
148
|
+
};
|