@pure-ds/core 0.7.19 → 0.7.21
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/.cursorrules +10 -0
- package/.github/copilot-instructions.md +10 -0
- package/custom-elements.json +232 -25
- package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -1
- package/dist/types/public/assets/pds/components/pds-code.d.ts +19 -0
- package/dist/types/public/assets/pds/components/pds-code.d.ts.map +1 -0
- package/dist/types/public/assets/pds/components/pds-icon.d.ts +31 -1
- package/dist/types/public/assets/pds/components/pds-icon.d.ts.map +1 -1
- package/dist/types/public/assets/pds/components/pds-treeview.d.ts +271 -16
- package/dist/types/public/assets/pds/components/pds-treeview.d.ts.map +1 -1
- package/dist/types/src/js/components/pds-code.d.ts +19 -0
- package/dist/types/src/js/components/pds-code.d.ts.map +1 -0
- package/dist/types/src/js/external/shiki.d.ts +3 -0
- package/dist/types/src/js/external/shiki.d.ts.map +1 -0
- package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
- package/package.json +1 -1
- package/packages/pds-cli/bin/templates/bootstrap/public/assets/my/my-home.js +1 -1
- package/public/assets/js/app.js +1 -1
- package/public/assets/js/pds-manager.js +138 -148
- package/public/assets/pds/components/pds-calendar.js +504 -16
- package/public/assets/pds/components/pds-code.js +203 -0
- package/public/assets/pds/components/pds-icon.js +102 -27
- package/public/assets/pds/components/pds-live-importer.js +2 -2
- package/public/assets/pds/components/pds-scrollrow.js +27 -2
- package/public/assets/pds/components/pds-treeview.js +185 -0
- package/public/assets/pds/core/pds-manager.js +138 -148
- package/public/assets/pds/custom-elements.json +263 -18
- package/public/assets/pds/external/shiki.js +32 -0
- package/public/assets/pds/pds-css-complete.json +1 -1
- package/public/assets/pds/templates/feedback-ops-dashboard.html +1 -1
- package/public/assets/pds/templates/release-readiness-radar.html +2 -2
- package/public/assets/pds/templates/support-command-center.html +1 -1
- package/src/js/pds-core/pds-generator.js +142 -152
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
const SHIKI_CDN_URL = 'https://esm.sh/shiki@1.0.0';
|
|
2
|
+
|
|
3
|
+
const SHIKI_LANGS = ['html', 'css', 'javascript', 'typescript', 'json', 'bash', 'shell'];
|
|
4
|
+
const SHIKI_THEMES = ['github-dark', 'github-light'];
|
|
5
|
+
|
|
6
|
+
let shikiModulePromise = null;
|
|
7
|
+
let highlighterPromise = null;
|
|
8
|
+
|
|
9
|
+
const escapeHtml = (text) => {
|
|
10
|
+
if (text == null) return '';
|
|
11
|
+
const value = typeof text === 'string' ? text : String(text);
|
|
12
|
+
return value
|
|
13
|
+
.replace(/&/g, '&')
|
|
14
|
+
.replace(/</g, '<')
|
|
15
|
+
.replace(/>/g, '>')
|
|
16
|
+
.replace(/\"/g, '"')
|
|
17
|
+
.replace(/'/g, ''');
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const resolveTheme = (preferredTheme) => {
|
|
21
|
+
if (preferredTheme === 'github-dark' || preferredTheme === 'github-light') {
|
|
22
|
+
return preferredTheme;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const docTheme = document.documentElement.getAttribute('data-theme')
|
|
26
|
+
|| document.body?.getAttribute('data-theme');
|
|
27
|
+
|
|
28
|
+
if (docTheme === 'dark') return 'github-dark';
|
|
29
|
+
if (docTheme === 'light') return 'github-light';
|
|
30
|
+
|
|
31
|
+
const prefersDark = window.matchMedia?.('(prefers-color-scheme: dark)')?.matches;
|
|
32
|
+
return prefersDark ? 'github-dark' : 'github-light';
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const resolveLang = (lang) => {
|
|
36
|
+
const next = String(lang || 'html').trim().toLowerCase();
|
|
37
|
+
if (next === 'js') return 'javascript';
|
|
38
|
+
if (next === 'ts') return 'typescript';
|
|
39
|
+
if (next === 'sh') return 'shell';
|
|
40
|
+
return next;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
async function loadShikiModule() {
|
|
44
|
+
if (shikiModulePromise) return shikiModulePromise;
|
|
45
|
+
|
|
46
|
+
shikiModulePromise = (async () => {
|
|
47
|
+
try {
|
|
48
|
+
return await import('#shiki');
|
|
49
|
+
} catch {
|
|
50
|
+
return import(SHIKI_CDN_URL);
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
|
|
54
|
+
return shikiModulePromise;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function loadHighlighter() {
|
|
58
|
+
if (highlighterPromise) return highlighterPromise;
|
|
59
|
+
|
|
60
|
+
highlighterPromise = (async () => {
|
|
61
|
+
try {
|
|
62
|
+
const shiki = await loadShikiModule();
|
|
63
|
+
if (typeof shiki?.getHighlighter !== 'function') return null;
|
|
64
|
+
return shiki.getHighlighter({ themes: SHIKI_THEMES, langs: SHIKI_LANGS });
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
})();
|
|
69
|
+
|
|
70
|
+
return highlighterPromise;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class PDSCode extends HTMLElement {
|
|
74
|
+
static get observedAttributes() {
|
|
75
|
+
return ['code', 'lang', 'theme'];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
constructor() {
|
|
79
|
+
super();
|
|
80
|
+
this._code = null;
|
|
81
|
+
this._capturedInitialText = false;
|
|
82
|
+
this._renderToken = 0;
|
|
83
|
+
this._textObserver = null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
connectedCallback() {
|
|
87
|
+
this.captureInitialText();
|
|
88
|
+
this.startTextObserver();
|
|
89
|
+
this.render();
|
|
90
|
+
|
|
91
|
+
queueMicrotask(() => {
|
|
92
|
+
if (!this.isConnected || this.hasAttribute('code')) return;
|
|
93
|
+
if (this.ensureCodeFromContent()) {
|
|
94
|
+
this.render();
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
disconnectedCallback() {
|
|
100
|
+
this.stopTextObserver();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
attributeChangedCallback() {
|
|
104
|
+
this.render();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
set code(value) {
|
|
108
|
+
this._code = value == null ? '' : String(value);
|
|
109
|
+
this.render();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get code() {
|
|
113
|
+
if (this._code != null) return this._code;
|
|
114
|
+
if (this.hasAttribute('code')) return this.getAttribute('code') || '';
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
setCode(value, { lang, theme } = {}) {
|
|
119
|
+
this.code = value;
|
|
120
|
+
if (lang) this.setAttribute('lang', lang);
|
|
121
|
+
if (theme) this.setAttribute('theme', theme);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
captureInitialText() {
|
|
125
|
+
if (this._capturedInitialText || this._code != null || this.hasAttribute('code')) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this._code = this.textContent || '';
|
|
130
|
+
this._capturedInitialText = true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
ensureCodeFromContent() {
|
|
134
|
+
if (this.hasAttribute('code')) return false;
|
|
135
|
+
if (this._code && this._code.trim()) return false;
|
|
136
|
+
|
|
137
|
+
const text = this.textContent || '';
|
|
138
|
+
if (!text.trim()) return false;
|
|
139
|
+
|
|
140
|
+
this._code = text;
|
|
141
|
+
this._capturedInitialText = true;
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
startTextObserver() {
|
|
146
|
+
if (this._textObserver || this.hasAttribute('code')) return;
|
|
147
|
+
|
|
148
|
+
this._textObserver = new MutationObserver(() => {
|
|
149
|
+
if (!this.isConnected || this.hasAttribute('code')) return;
|
|
150
|
+
if (this.ensureCodeFromContent()) {
|
|
151
|
+
this.render();
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
this._textObserver.observe(this, {
|
|
156
|
+
childList: true,
|
|
157
|
+
characterData: true,
|
|
158
|
+
subtree: true,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
stopTextObserver() {
|
|
163
|
+
if (!this._textObserver) return;
|
|
164
|
+
this._textObserver.disconnect();
|
|
165
|
+
this._textObserver = null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async render() {
|
|
169
|
+
const renderToken = ++this._renderToken;
|
|
170
|
+
if (this.ensureCodeFromContent()) {
|
|
171
|
+
this.stopTextObserver();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const sourceCode = this.code;
|
|
175
|
+
const lang = resolveLang(this.getAttribute('lang') || 'html');
|
|
176
|
+
const theme = resolveTheme(this.getAttribute('theme'));
|
|
177
|
+
|
|
178
|
+
if (!sourceCode) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const highlighter = await loadHighlighter();
|
|
183
|
+
|
|
184
|
+
if (renderToken !== this._renderToken) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (highlighter) {
|
|
189
|
+
try {
|
|
190
|
+
this.innerHTML = highlighter.codeToHtml(sourceCode, { lang, theme });
|
|
191
|
+
return;
|
|
192
|
+
} catch {
|
|
193
|
+
// Fall through to escaped pre/code fallback
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.innerHTML = `<pre><code>${escapeHtml(sourceCode)}</code></pre>`;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!customElements.get('pds-code')) {
|
|
202
|
+
customElements.define('pds-code', PDSCode);
|
|
203
|
+
}
|
|
@@ -48,10 +48,12 @@ export class SvgIcon extends HTMLElement {
|
|
|
48
48
|
static spritePromises = new Map();
|
|
49
49
|
static inlineSprites = new Map();
|
|
50
50
|
|
|
51
|
-
// Cache for externally fetched SVG icons (
|
|
51
|
+
// Cache for externally fetched SVG icons (path-aware key -> { content, viewBox, loaded, error })
|
|
52
52
|
static externalIconCache = new Map();
|
|
53
|
-
// Promises for in-flight external icon fetches
|
|
53
|
+
// Promises for in-flight external icon fetches (path-aware key)
|
|
54
54
|
static externalIconPromises = new Map();
|
|
55
|
+
// Tracks base paths already logged for debug diagnostics
|
|
56
|
+
static externalPathDebugLogged = new Set();
|
|
55
57
|
|
|
56
58
|
static instances = new Set();
|
|
57
59
|
|
|
@@ -199,6 +201,8 @@ export class SvgIcon extends HTMLElement {
|
|
|
199
201
|
// Determine if we should use sprite or fallback
|
|
200
202
|
let useFallback = this.hasAttribute('no-sprite') || !this.spriteAvailable();
|
|
201
203
|
|
|
204
|
+
const externalIconBasePath = SvgIcon.normalizeExternalIconPath();
|
|
205
|
+
|
|
202
206
|
let effectiveHref = spriteHref ? `${spriteHref}#${icon}` : `#${icon}`;
|
|
203
207
|
let inlineSymbolContent = null;
|
|
204
208
|
let inlineSymbolViewBox = null;
|
|
@@ -241,7 +245,8 @@ export class SvgIcon extends HTMLElement {
|
|
|
241
245
|
// IMPORTANT: Don't try external icons while sprite is still loading
|
|
242
246
|
const hasFallback = SvgIcon.#fallbackIcons.hasOwnProperty(icon);
|
|
243
247
|
if (spriteIconNotFound && !hasFallback && !spriteStillLoading) {
|
|
244
|
-
const
|
|
248
|
+
const cacheKey = SvgIcon.getExternalIconCacheKey(icon, externalIconBasePath);
|
|
249
|
+
const cached = SvgIcon.externalIconCache.get(cacheKey);
|
|
245
250
|
if (cached) {
|
|
246
251
|
if (cached.loaded && cached.content) {
|
|
247
252
|
useExternalIcon = true;
|
|
@@ -252,7 +257,7 @@ export class SvgIcon extends HTMLElement {
|
|
|
252
257
|
}
|
|
253
258
|
} else {
|
|
254
259
|
// Trigger async fetch - will re-render when complete
|
|
255
|
-
SvgIcon.fetchExternalIcon(icon);
|
|
260
|
+
SvgIcon.fetchExternalIcon(icon, externalIconBasePath);
|
|
256
261
|
useFallback = true;
|
|
257
262
|
}
|
|
258
263
|
}
|
|
@@ -306,7 +311,8 @@ export class SvgIcon extends HTMLElement {
|
|
|
306
311
|
|
|
307
312
|
const hasFallbackLocal = SvgIcon.#fallbackIcons.hasOwnProperty(iconName);
|
|
308
313
|
if (spriteIconNotFoundLocal && !hasFallbackLocal && !spriteStillLoadingLocal) {
|
|
309
|
-
const
|
|
314
|
+
const cacheKey = SvgIcon.getExternalIconCacheKey(iconName, externalIconBasePath);
|
|
315
|
+
const cached = SvgIcon.externalIconCache.get(cacheKey);
|
|
310
316
|
if (cached) {
|
|
311
317
|
if (cached.loaded && cached.content) {
|
|
312
318
|
useExternalIconLocal = true;
|
|
@@ -315,7 +321,7 @@ export class SvgIcon extends HTMLElement {
|
|
|
315
321
|
useFallbackLocal = true;
|
|
316
322
|
}
|
|
317
323
|
} else {
|
|
318
|
-
SvgIcon.fetchExternalIcon(iconName);
|
|
324
|
+
SvgIcon.fetchExternalIcon(iconName, externalIconBasePath);
|
|
319
325
|
useFallbackLocal = true;
|
|
320
326
|
}
|
|
321
327
|
}
|
|
@@ -582,17 +588,20 @@ export class SvgIcon extends HTMLElement {
|
|
|
582
588
|
*/
|
|
583
589
|
static getExternalIconPath() {
|
|
584
590
|
try {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
591
|
+
const candidates = [
|
|
592
|
+
PDS?.compiled?.tokens?.icons?.externalPath,
|
|
593
|
+
PDS?.compiled?.config?.design?.icons?.externalPath,
|
|
594
|
+
PDS?.compiled?.config?.icons?.externalPath,
|
|
595
|
+
PDS?.compiled?.options?.design?.icons?.externalPath,
|
|
596
|
+
PDS?.compiled?.options?.icons?.externalPath,
|
|
597
|
+
PDS?.currentConfig?.design?.icons?.externalPath,
|
|
598
|
+
PDS?.currentConfig?.icons?.externalPath,
|
|
599
|
+
];
|
|
600
|
+
|
|
601
|
+
for (const value of candidates) {
|
|
602
|
+
if (typeof value === 'string' && value.trim()) {
|
|
603
|
+
return value;
|
|
604
|
+
}
|
|
596
605
|
}
|
|
597
606
|
} catch (e) {
|
|
598
607
|
// Ignore errors accessing config
|
|
@@ -601,29 +610,95 @@ export class SvgIcon extends HTMLElement {
|
|
|
601
610
|
return '/assets/img/icons/';
|
|
602
611
|
}
|
|
603
612
|
|
|
613
|
+
/**
|
|
614
|
+
* Normalize the external icon path to make cache keys and URL joining stable.
|
|
615
|
+
* @private
|
|
616
|
+
* @param {string} [basePath]
|
|
617
|
+
* @returns {string}
|
|
618
|
+
*/
|
|
619
|
+
static normalizeExternalIconPath(basePath) {
|
|
620
|
+
const rawBasePath = typeof basePath === 'string' ? basePath : SvgIcon.getExternalIconPath();
|
|
621
|
+
const trimmed = (rawBasePath || '/assets/img/icons/').trim();
|
|
622
|
+
if (!trimmed) {
|
|
623
|
+
return '/assets/img/icons/';
|
|
624
|
+
}
|
|
625
|
+
return trimmed.endsWith('/') ? trimmed : `${trimmed}/`;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Build a deterministic cache key for external icon content.
|
|
630
|
+
* @private
|
|
631
|
+
* @param {string} iconName
|
|
632
|
+
* @param {string} basePath
|
|
633
|
+
* @returns {string}
|
|
634
|
+
*/
|
|
635
|
+
static getExternalIconCacheKey(iconName, basePath) {
|
|
636
|
+
return `${SvgIcon.normalizeExternalIconPath(basePath)}::${iconName}`;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Resolve an external icon URL from icon name + base path.
|
|
641
|
+
* @private
|
|
642
|
+
* @param {string} iconName
|
|
643
|
+
* @param {string} basePath
|
|
644
|
+
* @returns {string}
|
|
645
|
+
*/
|
|
646
|
+
static getExternalIconURL(iconName, basePath) {
|
|
647
|
+
const normalizedBasePath = SvgIcon.normalizeExternalIconPath(basePath);
|
|
648
|
+
try {
|
|
649
|
+
if (typeof window !== 'undefined' && window.location?.href) {
|
|
650
|
+
return new URL(`${iconName}.svg`, normalizedBasePath).href;
|
|
651
|
+
}
|
|
652
|
+
} catch (error) {
|
|
653
|
+
// Ignore URL construction errors and fall back to string concatenation.
|
|
654
|
+
}
|
|
655
|
+
return `${normalizedBasePath}${iconName}.svg`;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Whether verbose icon diagnostics should be logged.
|
|
660
|
+
* @private
|
|
661
|
+
* @returns {boolean}
|
|
662
|
+
*/
|
|
663
|
+
static isDebugLoggingEnabled() {
|
|
664
|
+
return Boolean(
|
|
665
|
+
PDS?.debug === true ||
|
|
666
|
+
PDS?.compiled?.config?.design?.debug === true ||
|
|
667
|
+
PDS?.compiled?.options?.design?.debug === true ||
|
|
668
|
+
PDS?.currentConfig?.design?.debug === true
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
604
672
|
/**
|
|
605
673
|
* Fetch an external SVG icon and cache it
|
|
606
674
|
* @param {string} iconName - The icon name (without .svg extension)
|
|
607
675
|
* @returns {Promise<boolean>} True if successfully fetched
|
|
608
676
|
*/
|
|
609
|
-
static async fetchExternalIcon(iconName) {
|
|
677
|
+
static async fetchExternalIcon(iconName, basePath) {
|
|
610
678
|
if (!iconName || typeof document === 'undefined') {
|
|
611
679
|
return false;
|
|
612
680
|
}
|
|
613
681
|
|
|
682
|
+
const normalizedBasePath = SvgIcon.normalizeExternalIconPath(basePath);
|
|
683
|
+
const cacheKey = SvgIcon.getExternalIconCacheKey(iconName, normalizedBasePath);
|
|
684
|
+
|
|
614
685
|
// Check if already cached
|
|
615
|
-
const cached = SvgIcon.externalIconCache.get(
|
|
686
|
+
const cached = SvgIcon.externalIconCache.get(cacheKey);
|
|
616
687
|
if (cached) {
|
|
617
688
|
return cached.loaded;
|
|
618
689
|
}
|
|
619
690
|
|
|
620
691
|
// Check if fetch is already in progress
|
|
621
|
-
if (SvgIcon.externalIconPromises.has(
|
|
622
|
-
return SvgIcon.externalIconPromises.get(
|
|
692
|
+
if (SvgIcon.externalIconPromises.has(cacheKey)) {
|
|
693
|
+
return SvgIcon.externalIconPromises.get(cacheKey);
|
|
623
694
|
}
|
|
624
695
|
|
|
625
|
-
const
|
|
626
|
-
|
|
696
|
+
const iconUrl = SvgIcon.getExternalIconURL(iconName, normalizedBasePath);
|
|
697
|
+
|
|
698
|
+
if (SvgIcon.isDebugLoggingEnabled() && !SvgIcon.externalPathDebugLogged.has(normalizedBasePath)) {
|
|
699
|
+
SvgIcon.externalPathDebugLogged.add(normalizedBasePath);
|
|
700
|
+
console.debug('[pds-icon] Resolved external icon base path:', normalizedBasePath, 'example URL:', iconUrl);
|
|
701
|
+
}
|
|
627
702
|
|
|
628
703
|
const promise = fetch(iconUrl)
|
|
629
704
|
.then(async (response) => {
|
|
@@ -649,7 +724,7 @@ export class SvgIcon extends HTMLElement {
|
|
|
649
724
|
.map((node) => serializer.serializeToString(node))
|
|
650
725
|
.join('');
|
|
651
726
|
|
|
652
|
-
SvgIcon.externalIconCache.set(
|
|
727
|
+
SvgIcon.externalIconCache.set(cacheKey, {
|
|
653
728
|
loaded: true,
|
|
654
729
|
error: false,
|
|
655
730
|
content,
|
|
@@ -665,7 +740,7 @@ export class SvgIcon extends HTMLElement {
|
|
|
665
740
|
if (!error.message?.includes('404')) {
|
|
666
741
|
console.debug('[pds-icon] External icon not found:', iconName, error.message);
|
|
667
742
|
}
|
|
668
|
-
SvgIcon.externalIconCache.set(
|
|
743
|
+
SvgIcon.externalIconCache.set(cacheKey, {
|
|
669
744
|
loaded: false,
|
|
670
745
|
error: true,
|
|
671
746
|
content: null,
|
|
@@ -676,10 +751,10 @@ export class SvgIcon extends HTMLElement {
|
|
|
676
751
|
return false;
|
|
677
752
|
})
|
|
678
753
|
.finally(() => {
|
|
679
|
-
SvgIcon.externalIconPromises.delete(
|
|
754
|
+
SvgIcon.externalIconPromises.delete(cacheKey);
|
|
680
755
|
});
|
|
681
756
|
|
|
682
|
-
SvgIcon.externalIconPromises.set(
|
|
757
|
+
SvgIcon.externalIconPromises.set(cacheKey, promise);
|
|
683
758
|
return promise;
|
|
684
759
|
}
|
|
685
760
|
|
|
@@ -262,12 +262,12 @@ class PdsLiveImporter extends HTMLElement {
|
|
|
262
262
|
? entry.unknownTailwindTokens
|
|
263
263
|
: [];
|
|
264
264
|
const notesHtml = notes.length
|
|
265
|
-
? `<ul
|
|
265
|
+
? `<ul>${notes
|
|
266
266
|
.map((note) => `<li>${escapeHtml(note)}</li>`)
|
|
267
267
|
.join("")}</ul>`
|
|
268
268
|
: "none";
|
|
269
269
|
const issuesHtml = issueCount
|
|
270
|
-
? `<ul
|
|
270
|
+
? `<ul>${entry.issues
|
|
271
271
|
.map(
|
|
272
272
|
(issue) =>
|
|
273
273
|
`<li><strong>${escapeHtml(issue?.severity || "info")}</strong>: ${escapeHtml(issue?.message || "")}</li>`
|
|
@@ -348,8 +348,33 @@ class PdsScrollrow extends HTMLElement {
|
|
|
348
348
|
#updateControls() {
|
|
349
349
|
const el = this.#viewport;
|
|
350
350
|
if (!el) return;
|
|
351
|
-
|
|
352
|
-
const
|
|
351
|
+
|
|
352
|
+
const computed = getComputedStyle(el);
|
|
353
|
+
const padStart = parseFloat(computed.paddingInlineStart) || 0;
|
|
354
|
+
const padEnd = parseFloat(computed.paddingInlineEnd) || 0;
|
|
355
|
+
|
|
356
|
+
const maxScroll = Math.max(0, el.scrollWidth - el.clientWidth);
|
|
357
|
+
const dir = computed.direction;
|
|
358
|
+
let position = el.scrollLeft;
|
|
359
|
+
|
|
360
|
+
if (dir === "rtl") {
|
|
361
|
+
position = position < 0 ? -position : maxScroll - position;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
position = Math.min(maxScroll, Math.max(0, position));
|
|
365
|
+
|
|
366
|
+
const baseThreshold = 2;
|
|
367
|
+
const startThreshold = Math.max(baseThreshold, Math.ceil(padStart) + 1);
|
|
368
|
+
const endThreshold = Math.max(baseThreshold, Math.ceil(padEnd) + 1);
|
|
369
|
+
|
|
370
|
+
let atStart = position <= startThreshold;
|
|
371
|
+
let atEnd = maxScroll - position <= endThreshold;
|
|
372
|
+
|
|
373
|
+
if (maxScroll <= Math.max(startThreshold, endThreshold)) {
|
|
374
|
+
atStart = true;
|
|
375
|
+
atEnd = true;
|
|
376
|
+
}
|
|
377
|
+
|
|
353
378
|
this.classList.toggle("can-scroll-left", !atStart);
|
|
354
379
|
this.classList.toggle("can-scroll-right", !atEnd);
|
|
355
380
|
const buttons = this.shadowRoot.querySelectorAll(".control button");
|