@pure-ds/core 0.3.0
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/CSS-INTELLISENSE-LIMITATION.md +98 -0
- package/CSS-INTELLISENSE-QUICK-REF.md +238 -0
- package/INTELLISENSE.md +384 -0
- package/LICENSE +15 -0
- package/custom-elements-manifest.config.js +30 -0
- package/custom-elements.json +2003 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/packages/pds-configurator/src/figma-export.d.ts +13 -0
- package/dist/types/packages/pds-configurator/src/figma-export.d.ts.map +1 -0
- package/dist/types/packages/pds-configurator/src/pds-config-form.d.ts +2 -0
- package/dist/types/packages/pds-configurator/src/pds-config-form.d.ts.map +1 -0
- package/dist/types/packages/pds-configurator/src/pds-configurator.d.ts +2 -0
- package/dist/types/packages/pds-configurator/src/pds-configurator.d.ts.map +1 -0
- package/dist/types/packages/pds-configurator/src/pds-demo.d.ts +2 -0
- package/dist/types/packages/pds-configurator/src/pds-demo.d.ts.map +1 -0
- package/dist/types/pds.config.d.ts +13 -0
- package/dist/types/pds.config.d.ts.map +1 -0
- package/dist/types/pds.d.ts +408 -0
- package/dist/types/public/assets/js/app.d.ts +2 -0
- package/dist/types/public/assets/js/app.d.ts.map +1 -0
- package/dist/types/public/assets/js/pds.d.ts +23 -0
- package/dist/types/public/assets/js/pds.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-calendar.d.ts +23 -0
- package/dist/types/public/assets/pds/components/pds-calendar.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-drawer.d.ts +2 -0
- package/dist/types/public/assets/pds/components/pds-drawer.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-icon.d.ts +53 -0
- package/dist/types/public/assets/pds/components/pds-icon.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-jsonform.d.ts +104 -0
- package/dist/types/public/assets/pds/components/pds-jsonform.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-richtext.d.ts +121 -0
- package/dist/types/public/assets/pds/components/pds-richtext.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts +61 -0
- package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-splitpanel.d.ts +1 -0
- package/dist/types/public/assets/pds/components/pds-splitpanel.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-tabstrip.d.ts +39 -0
- package/dist/types/public/assets/pds/components/pds-tabstrip.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-toaster.d.ts +111 -0
- package/dist/types/public/assets/pds/components/pds-toaster.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-upload.d.ts +83 -0
- package/dist/types/public/assets/pds/components/pds-upload.d.ts.map +1 -0
- package/dist/types/src/js/app.d.ts +2 -0
- package/dist/types/src/js/app.d.ts.map +1 -0
- package/dist/types/src/js/common/ask.d.ts +22 -0
- package/dist/types/src/js/common/ask.d.ts.map +1 -0
- package/dist/types/src/js/common/common.d.ts +3 -0
- package/dist/types/src/js/common/common.d.ts.map +1 -0
- package/dist/types/src/js/common/font-loader.d.ts +24 -0
- package/dist/types/src/js/common/font-loader.d.ts.map +1 -0
- package/dist/types/src/js/common/msg.d.ts +3 -0
- package/dist/types/src/js/common/msg.d.ts.map +1 -0
- package/dist/types/src/js/lit.d.ts +25 -0
- package/dist/types/src/js/lit.d.ts.map +1 -0
- package/dist/types/src/js/pds-configurator/figma-export.d.ts +13 -0
- package/dist/types/src/js/pds-configurator/figma-export.d.ts.map +1 -0
- package/dist/types/src/js/pds-configurator/pds-config-form.d.ts +2 -0
- package/dist/types/src/js/pds-configurator/pds-config-form.d.ts.map +1 -0
- package/dist/types/src/js/pds-configurator/pds-configurator.d.ts +2 -0
- package/dist/types/src/js/pds-configurator/pds-configurator.d.ts.map +1 -0
- package/dist/types/src/js/pds-configurator/pds-demo.d.ts +2 -0
- package/dist/types/src/js/pds-configurator/pds-demo.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-config.d.ts +758 -0
- package/dist/types/src/js/pds-core/pds-config.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-enhancer-metadata.d.ts +6 -0
- package/dist/types/src/js/pds-core/pds-enhancer-metadata.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-enhancers.d.ts +14 -0
- package/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-enums.d.ts +87 -0
- package/dist/types/src/js/pds-core/pds-enums.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-generator.d.ts +741 -0
- package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-ontology.d.ts +48 -0
- package/dist/types/src/js/pds-core/pds-ontology.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-paths.d.ts +37 -0
- package/dist/types/src/js/pds-core/pds-paths.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-query.d.ts +102 -0
- package/dist/types/src/js/pds-core/pds-query.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-registry.d.ts +40 -0
- package/dist/types/src/js/pds-core/pds-registry.d.ts.map +1 -0
- package/dist/types/src/js/pds.d.ts +109 -0
- package/dist/types/src/js/pds.d.ts.map +1 -0
- package/dist/types/src/pds-core/pds-api.d.ts +31 -0
- package/dist/types/src/pds-core/pds-api.d.ts.map +1 -0
- package/package.json +104 -0
- package/packages/pds-cli/README.md +15 -0
- package/packages/pds-cli/bin/generate-css-data.js +565 -0
- package/packages/pds-cli/bin/generate-manifest.js +352 -0
- package/packages/pds-cli/bin/pds-build-icons.js +152 -0
- package/packages/pds-cli/bin/pds-dx.js +114 -0
- package/packages/pds-cli/bin/pds-static.js +556 -0
- package/packages/pds-cli/bin/pds.js +127 -0
- package/packages/pds-cli/bin/postinstall.js +380 -0
- package/packages/pds-cli/bin/sync-assets.js +252 -0
- package/packages/pds-cli/lib/asset-roots.js +47 -0
- package/packages/pds-cli/lib/fs-writer.js +75 -0
- package/pds.css-data.json +5 -0
- package/pds.html-data.json +5 -0
- package/public/assets/js/app.js +5719 -0
- package/public/assets/js/lit.js +131 -0
- package/public/assets/js/pds.js +3423 -0
- package/public/assets/pds/components/pds-calendar.js +837 -0
- package/public/assets/pds/components/pds-drawer.js +857 -0
- package/public/assets/pds/components/pds-icon.js +338 -0
- package/public/assets/pds/components/pds-jsonform.js +1775 -0
- package/public/assets/pds/components/pds-richtext.js +1035 -0
- package/public/assets/pds/components/pds-scrollrow.js +331 -0
- package/public/assets/pds/components/pds-splitpanel.js +401 -0
- package/public/assets/pds/components/pds-tabstrip.js +251 -0
- package/public/assets/pds/components/pds-toaster.js +446 -0
- package/public/assets/pds/components/pds-upload.js +657 -0
- package/public/assets/pds/custom-elements.json +2003 -0
- package/public/assets/pds/icons/pds-icons.svg +498 -0
- package/public/assets/pds/pds-css-complete.json +1861 -0
- package/public/assets/pds/pds.css-data.json +2152 -0
- package/public/assets/pds/vscode-custom-data.json +824 -0
- package/readme.md +1870 -0
- package/src/js/pds-core/pds-config.js +1162 -0
- package/src/js/pds-core/pds-enhancer-metadata.js +75 -0
- package/src/js/pds-core/pds-enhancers.js +357 -0
- package/src/js/pds-core/pds-enums.js +86 -0
- package/src/js/pds-core/pds-generator.js +5317 -0
- package/src/js/pds-core/pds-ontology.js +256 -0
- package/src/js/pds-core/pds-paths.js +109 -0
- package/src/js/pds-core/pds-query.js +571 -0
- package/src/js/pds-core/pds-registry.js +129 -0
- package/src/js/pds-core/pds.d.ts +129 -0
- package/src/js/pds.d.ts +408 -0
- package/src/js/pds.js +1579 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Horizontal scrolling row with optional heading and snap alignment controls.
|
|
3
|
+
*
|
|
4
|
+
* @element pds-scrollrow
|
|
5
|
+
* @slot default - Scrollable tile content
|
|
6
|
+
* @slot heading - Optional heading content rendered in the component header
|
|
7
|
+
* @csspart viewport - The scrollable container element
|
|
8
|
+
*
|
|
9
|
+
* @attr {string} label - Accessible label for the scroll region; also used as fallback heading copy
|
|
10
|
+
* @attr {"start"|"center"} snap - Snap alignment for tiles when scrolling (default: start)
|
|
11
|
+
*/
|
|
12
|
+
class PdsScrollrow extends HTMLElement {
|
|
13
|
+
#viewport;
|
|
14
|
+
#ro;
|
|
15
|
+
#rendered = false;
|
|
16
|
+
#adopted = false;
|
|
17
|
+
|
|
18
|
+
static get observedAttributes() {
|
|
19
|
+
return ["label", "snap"];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
this.attachShadow({ mode: "open" });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static #COMPONENT_CSS = /*css*/`
|
|
28
|
+
:host {
|
|
29
|
+
display: block;
|
|
30
|
+
position: relative;
|
|
31
|
+
--row-gap-def: 10px;
|
|
32
|
+
--tile-min-def: 60px;
|
|
33
|
+
--tile-max-def: 120px;
|
|
34
|
+
--edge-fade-def: 16px;
|
|
35
|
+
}
|
|
36
|
+
section { position: relative; }
|
|
37
|
+
header { display:flex; align-items:baseline; gap:.5rem; margin-bottom:.5rem; }
|
|
38
|
+
header h2 { margin:0; }
|
|
39
|
+
.viewport-wrap { position: relative; }
|
|
40
|
+
.viewport {
|
|
41
|
+
padding-top:10px; padding-bottom:10px; position:relative; overflow-x:auto; overflow-y:hidden;
|
|
42
|
+
scroll-behavior:smooth; scroll-snap-type:x mandatory; -webkit-overflow-scrolling:touch; padding-inline:0.5rem;
|
|
43
|
+
--mask-left:transparent; --mask-right:transparent;
|
|
44
|
+
-webkit-mask-image:linear-gradient(to right, var(--mask-left) 0, #000 var(--edge-fade), #000 calc(100% - var(--edge-fade)), var(--mask-right) 100%);
|
|
45
|
+
mask-image:linear-gradient(to right, var(--mask-left) 0, #000 var(--edge-fade), #000 calc(100% - var(--edge-fade)), var(--mask-right) 100%);
|
|
46
|
+
scrollbar-width:none;
|
|
47
|
+
}
|
|
48
|
+
.viewport::-webkit-scrollbar { display:none; }
|
|
49
|
+
:host(.can-scroll-left) .viewport { --mask-left: rgba(0,0,0,0); }
|
|
50
|
+
:host(.can-scroll-right) .viewport { --mask-right: rgba(0,0,0,0); }
|
|
51
|
+
ul.track { display:flex; gap:var(--row-gap, var(--row-gap-def)); list-style:none; padding:0; margin:0; }
|
|
52
|
+
::slotted(*) {
|
|
53
|
+
scroll-snap-align: var(--snap-align, start);
|
|
54
|
+
flex:0 0 auto;
|
|
55
|
+
min-inline-size: var(--tile-min, var(--tile-min-def));
|
|
56
|
+
max-inline-size: var(--tile-max, var(--tile-max-def));
|
|
57
|
+
inline-size: clamp(var(--tile-min, var(--tile-min-def)), 40vw, var(--tile-max, var(--tile-max-def)));
|
|
58
|
+
--tile-radius:14px; --tile-bg: var(--color-mostly-trans); --tile-shadow: 0 1px 0 rgba(0,0,0,.06), 0 6px 14px rgba(0,0,0,.12);
|
|
59
|
+
border-radius:var(--tile-radius); overflow:clip; background:var(--tile-bg); box-shadow:var(--tile-shadow); isolation:isolate; overflow:hidden;
|
|
60
|
+
}
|
|
61
|
+
::slotted(* :focus) { outline-offset:2px; }
|
|
62
|
+
.control { position:absolute; top:50%; transform:translateY(-50%); display:none; place-items:center; inline-size:auto; pointer-events:none; z-index:2; }
|
|
63
|
+
@media (hover:hover) and (pointer:fine) {
|
|
64
|
+
.control { display:grid; opacity:0; transition:opacity .18s ease; }
|
|
65
|
+
.control.left { left: var(--spacing-3, 12px); }
|
|
66
|
+
.control.right { right: var(--spacing-3, 12px); }
|
|
67
|
+
:host(.can-scroll-left) .control.left,
|
|
68
|
+
:host(.can-scroll-right) .control.right { opacity:1; pointer-events:auto; }
|
|
69
|
+
}
|
|
70
|
+
.control button { pointer-events:auto; }
|
|
71
|
+
.control button[disabled]{ opacity:.35; cursor:default; }
|
|
72
|
+
@media (prefers-reduced-motion:reduce){ .viewport { scroll-behavior:auto; } }
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
async #adopt() {
|
|
76
|
+
if (this.#adopted || !this.shadowRoot) return;
|
|
77
|
+
try {
|
|
78
|
+
if (window.PDS && typeof PDS.createStylesheet === 'function' && typeof PDS.adoptLayers === 'function') {
|
|
79
|
+
const componentSheet = PDS.createStylesheet(PdsScrollrow.#COMPONENT_CSS);
|
|
80
|
+
await PDS.adoptLayers(this.shadowRoot, ['primitives','components', 'utilities'], [componentSheet]);
|
|
81
|
+
this.#adopted = true;
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.warn('[pds-scrollrow] adoptLayers failed, falling back', e);
|
|
86
|
+
}
|
|
87
|
+
// Fallback: inline <style>
|
|
88
|
+
const style = document.createElement('style');
|
|
89
|
+
style.textContent = PdsScrollrow.#COMPONENT_CSS;
|
|
90
|
+
this.shadowRoot.prepend(style);
|
|
91
|
+
this.#adopted = true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Property <-> attribute reflection for ergonomic usage
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Accessible label applied to the scroll region.
|
|
98
|
+
* @returns {string|null}
|
|
99
|
+
*/
|
|
100
|
+
get label() {
|
|
101
|
+
// Return null when not set so caller can decide whether to render header
|
|
102
|
+
return this.getAttribute("label");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Update the accessible label and optional fallback heading text.
|
|
106
|
+
* @param {string|null} val
|
|
107
|
+
*/
|
|
108
|
+
set label(val) {
|
|
109
|
+
if (val == null) this.removeAttribute("label");
|
|
110
|
+
else this.setAttribute("label", String(val));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Current scroll snap alignment strategy.
|
|
114
|
+
* @returns {"start"|"center"}
|
|
115
|
+
*/
|
|
116
|
+
get snap() {
|
|
117
|
+
return this.getAttribute("snap") ?? "start";
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Adjust the scroll snap alignment.
|
|
121
|
+
* @param {string|null} val
|
|
122
|
+
*/
|
|
123
|
+
set snap(val) {
|
|
124
|
+
if (val == null) this.removeAttribute("snap");
|
|
125
|
+
else this.setAttribute("snap", String(val));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Lifecycle hook called when the element is inserted into the document.
|
|
130
|
+
*/
|
|
131
|
+
connectedCallback() {
|
|
132
|
+
if (!this.#rendered) {
|
|
133
|
+
this.render();
|
|
134
|
+
this.#postRender();
|
|
135
|
+
this.#rendered = true;
|
|
136
|
+
}
|
|
137
|
+
// In case content/size changed while disconnected
|
|
138
|
+
this.#updateControls();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Lifecycle hook called when the element is removed from the document.
|
|
143
|
+
*/
|
|
144
|
+
disconnectedCallback() {
|
|
145
|
+
this.#ro?.disconnect();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Respond to attribute mutations for `label` and `snap`.
|
|
150
|
+
* @param {string} name
|
|
151
|
+
* @param {string|null} oldValue
|
|
152
|
+
* @param {string|null} newValue
|
|
153
|
+
*/
|
|
154
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
155
|
+
if (oldValue === newValue) return;
|
|
156
|
+
if (!this.shadowRoot) return;
|
|
157
|
+
switch (name) {
|
|
158
|
+
case "label": {
|
|
159
|
+
const section = this.shadowRoot.querySelector("section");
|
|
160
|
+
// If label was removed, remove the header and aria-label
|
|
161
|
+
if (newValue == null) {
|
|
162
|
+
if (section) section.removeAttribute("aria-label");
|
|
163
|
+
const header = this.shadowRoot.querySelector("header");
|
|
164
|
+
if (header) header.remove();
|
|
165
|
+
} else {
|
|
166
|
+
// Add/update aria-label and header fallback text
|
|
167
|
+
if (section) section.setAttribute("aria-label", newValue);
|
|
168
|
+
const fallbackHeading = this.shadowRoot.querySelector(
|
|
169
|
+
"header h2 span[data-fallback]"
|
|
170
|
+
);
|
|
171
|
+
if (fallbackHeading) {
|
|
172
|
+
fallbackHeading.textContent = newValue;
|
|
173
|
+
} else {
|
|
174
|
+
// Header missing -> create and insert before viewport-wrap
|
|
175
|
+
const viewportWrap = this.shadowRoot.querySelector('.viewport-wrap');
|
|
176
|
+
if (viewportWrap) {
|
|
177
|
+
const header = document.createElement('header');
|
|
178
|
+
header.innerHTML = `<h2><slot name="heading"><span data-fallback>${newValue}</span></slot></h2>`;
|
|
179
|
+
viewportWrap.parentNode.insertBefore(header, viewportWrap);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
case "snap": {
|
|
186
|
+
this.#applySnap();
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Render or rerender the component shadow DOM.
|
|
194
|
+
*/
|
|
195
|
+
render() {
|
|
196
|
+
const label = this.label;
|
|
197
|
+
const headerHtml = label
|
|
198
|
+
? `<header>
|
|
199
|
+
<h2>
|
|
200
|
+
<slot name="heading">
|
|
201
|
+
<span data-fallback>${label}</span>
|
|
202
|
+
</slot>
|
|
203
|
+
</h2>
|
|
204
|
+
</header>`
|
|
205
|
+
: "";
|
|
206
|
+
|
|
207
|
+
const sectionOpen = label ? `<section role="region" aria-label="${label}">` : `<section role="region">`;
|
|
208
|
+
|
|
209
|
+
this.shadowRoot.innerHTML = /*html*/`
|
|
210
|
+
${sectionOpen}
|
|
211
|
+
${headerHtml}
|
|
212
|
+
<div class="viewport-wrap">
|
|
213
|
+
<div class="viewport" part="viewport" tabindex="0">
|
|
214
|
+
<ul class="track" role="list"><slot></slot></ul>
|
|
215
|
+
</div>
|
|
216
|
+
<div class="control left" aria-hidden="true">
|
|
217
|
+
<button class="btn btn-sm icon-only" part="prev" aria-label="Scroll left">
|
|
218
|
+
<pds-icon icon="arrow-left" size="sm"></pds-icon>
|
|
219
|
+
</button>
|
|
220
|
+
</div>
|
|
221
|
+
<div class="control right" aria-hidden="true">
|
|
222
|
+
<button class="btn btn-sm icon-only" part="next" aria-label="Scroll right">
|
|
223
|
+
<pds-icon icon="arrow-right" size="sm"></pds-icon>
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</section>`;
|
|
228
|
+
// Kick off style adoption after markup so elements exist
|
|
229
|
+
this.#adopt();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
#postRender() {
|
|
233
|
+
const root = this.shadowRoot;
|
|
234
|
+
this.#viewport = root.querySelector(".viewport");
|
|
235
|
+
|
|
236
|
+
// Events
|
|
237
|
+
this.#viewport.addEventListener("scroll", () => this.#onScroll());
|
|
238
|
+
this.#viewport.addEventListener("keydown", (e) => this.#onKeyDown(e));
|
|
239
|
+
// Update when images inside slotted content finish loading (capture because load doesn't bubble)
|
|
240
|
+
this.#viewport.addEventListener(
|
|
241
|
+
"load",
|
|
242
|
+
() => this.#updateControls(),
|
|
243
|
+
true
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const [prevBtn, nextBtn] = root.querySelectorAll(".control button");
|
|
247
|
+
if (prevBtn) prevBtn.addEventListener("click", (e) => this.doPage(e));
|
|
248
|
+
if (nextBtn) nextBtn.addEventListener("click", (e) => this.doPage(e));
|
|
249
|
+
|
|
250
|
+
// Slot content changes may affect scrollWidth
|
|
251
|
+
const defaultSlot = root.querySelector("slot:not([name])");
|
|
252
|
+
defaultSlot?.addEventListener("slotchange", () => this.#updateControls());
|
|
253
|
+
|
|
254
|
+
// Apply initial snap alignment
|
|
255
|
+
this.#applySnap();
|
|
256
|
+
|
|
257
|
+
// Observe size changes to refresh controls
|
|
258
|
+
this.#ro = new ResizeObserver(() => this.#updateControls());
|
|
259
|
+
if (this.#viewport) this.#ro.observe(this.#viewport);
|
|
260
|
+
|
|
261
|
+
// Initial state (layout can be delayed; schedule a couple of passes)
|
|
262
|
+
this.#updateControls();
|
|
263
|
+
queueMicrotask(() => this.#updateControls());
|
|
264
|
+
requestAnimationFrame(() => this.#updateControls());
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
#applySnap() {
|
|
268
|
+
const snapAlign = this.snap === "center" ? "center" : "start";
|
|
269
|
+
if (this.#viewport) {
|
|
270
|
+
this.#viewport.style.setProperty("--snap-align", snapAlign);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Scroll the viewport by roughly one page in the indicated direction.
|
|
276
|
+
* @param {Event} e
|
|
277
|
+
*/
|
|
278
|
+
doPage(e) {
|
|
279
|
+
const target = e.currentTarget || e.target;
|
|
280
|
+
const direction = target.getAttribute("part") === "prev" ? -1 : 1;
|
|
281
|
+
this.#page(direction);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
#onScroll() {
|
|
285
|
+
this.#updateControls();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
#updateControls() {
|
|
289
|
+
const el = this.#viewport;
|
|
290
|
+
if (!el) return;
|
|
291
|
+
const atStart = el.scrollLeft <= 2;
|
|
292
|
+
const atEnd = Math.ceil(el.scrollLeft + el.clientWidth) >= el.scrollWidth - 2;
|
|
293
|
+
this.classList.toggle("can-scroll-left", !atStart);
|
|
294
|
+
this.classList.toggle("can-scroll-right", !atEnd);
|
|
295
|
+
const buttons = this.shadowRoot.querySelectorAll(".control button");
|
|
296
|
+
const prevBtn = buttons[0];
|
|
297
|
+
const nextBtn = buttons[1];
|
|
298
|
+
if (prevBtn && nextBtn) {
|
|
299
|
+
prevBtn.disabled = atStart;
|
|
300
|
+
nextBtn.disabled = atEnd;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
#page(direction = 1) {
|
|
305
|
+
const el = this.#viewport;
|
|
306
|
+
if (!el) return;
|
|
307
|
+
const amount = Math.max(1, Math.floor(el.clientWidth * 0.9)) * direction;
|
|
308
|
+
el.scrollBy({ left: amount, behavior: "smooth" });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
#onKeyDown(e) {
|
|
312
|
+
if (e.key === "ArrowRight") {
|
|
313
|
+
e.preventDefault();
|
|
314
|
+
this.#page(1);
|
|
315
|
+
} else if (e.key === "ArrowLeft") {
|
|
316
|
+
e.preventDefault();
|
|
317
|
+
this.#page(-1);
|
|
318
|
+
} else if (e.key === "Home") {
|
|
319
|
+
e.preventDefault();
|
|
320
|
+
this.#viewport?.scrollTo({ left: 0, behavior: "smooth" });
|
|
321
|
+
} else if (e.key === "End") {
|
|
322
|
+
e.preventDefault();
|
|
323
|
+
this.#viewport?.scrollTo({ left: this.#viewport.scrollWidth, behavior: "smooth" });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Auto-register the component
|
|
329
|
+
if (!customElements.get("pds-scrollrow")) {
|
|
330
|
+
customElements.define("pds-scrollrow", PdsScrollrow);
|
|
331
|
+
}
|