@pure-ds/storybook 0.1.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/.storybook/addons/description/preview.js +15 -0
- package/.storybook/addons/description/register.js +60 -0
- package/.storybook/addons/html-preview/Panel.jsx +327 -0
- package/.storybook/addons/html-preview/constants.js +6 -0
- package/.storybook/addons/html-preview/preview.js +178 -0
- package/.storybook/addons/html-preview/register.js +16 -0
- package/.storybook/addons/pds-configurator/SearchTool.js +44 -0
- package/.storybook/addons/pds-configurator/Tool.js +30 -0
- package/.storybook/addons/pds-configurator/constants.js +9 -0
- package/.storybook/addons/pds-configurator/preview.js +159 -0
- package/.storybook/addons/pds-configurator/register.js +24 -0
- package/.storybook/docs.css +35 -0
- package/.storybook/htmlPreview.css +103 -0
- package/.storybook/htmlPreview.js +271 -0
- package/.storybook/main.js +160 -0
- package/.storybook/preview-body.html +48 -0
- package/.storybook/preview-head.html +11 -0
- package/.storybook/preview.js +1563 -0
- package/README.md +266 -0
- package/bin/index.js +40 -0
- package/dist/pds-reference.json +2101 -0
- package/package.json +45 -0
- package/pds.config.js +6 -0
- package/public/assets/css/app.css +1216 -0
- package/public/assets/data/auto-design-advanced.json +704 -0
- package/public/assets/data/auto-design-simple.json +123 -0
- package/public/assets/img/icon-512x512.png +0 -0
- package/public/assets/img/logo-trans.png +0 -0
- package/public/assets/img/logo.png +0 -0
- package/public/assets/js/app.js +15088 -0
- package/public/assets/js/app.js.map +7 -0
- package/public/assets/js/lit.js +1176 -0
- package/public/assets/js/lit.js.map +7 -0
- package/public/assets/js/pds.js +9801 -0
- package/public/assets/js/pds.js.map +7 -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-runtime-config.json +11 -0
- package/public/assets/pds/pds.css-data.json +2152 -0
- package/public/assets/pds/styles/pds-components.css +1944 -0
- package/public/assets/pds/styles/pds-components.css.js +3895 -0
- package/public/assets/pds/styles/pds-primitives.css +352 -0
- package/public/assets/pds/styles/pds-primitives.css.js +711 -0
- package/public/assets/pds/styles/pds-styles.css +3761 -0
- package/public/assets/pds/styles/pds-styles.css.js +7529 -0
- package/public/assets/pds/styles/pds-tokens.css +699 -0
- package/public/assets/pds/styles/pds-tokens.css.js +1405 -0
- package/public/assets/pds/styles/pds-utilities.css +763 -0
- package/public/assets/pds/styles/pds-utilities.css.js +1533 -0
- package/public/assets/pds/vscode-custom-data.json +824 -0
- package/scripts/build-pds-reference.mjs +807 -0
- package/scripts/generate-stories.js +542 -0
- package/scripts/package-build.js +86 -0
- package/src/js/app.js +17 -0
- package/src/js/common/ask.js +208 -0
- package/src/js/common/common.js +20 -0
- package/src/js/common/font-loader.js +200 -0
- package/src/js/common/msg.js +90 -0
- package/src/js/lit.js +40 -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
- package/src/pds-core/pds-api.js +105 -0
- package/stories/GettingStarted.md +96 -0
- package/stories/GettingStarted.stories.js +144 -0
- package/stories/WhatIsPDS.md +194 -0
- package/stories/WhatIsPDS.stories.js +144 -0
- package/stories/components/PdsCalendar.stories.js +263 -0
- package/stories/components/PdsDrawer.stories.js +623 -0
- package/stories/components/PdsIcon.stories.js +78 -0
- package/stories/components/PdsJsonform.stories.js +1444 -0
- package/stories/components/PdsRichtext.stories.js +367 -0
- package/stories/components/PdsScrollrow.stories.js +140 -0
- package/stories/components/PdsSplitpanel.stories.js +502 -0
- package/stories/components/PdsTabstrip.stories.js +442 -0
- package/stories/components/PdsToaster.stories.js +186 -0
- package/stories/components/PdsUpload.stories.js +66 -0
- package/stories/enhancements/Dropdowns.stories.js +185 -0
- package/stories/enhancements/InteractiveStates.stories.js +625 -0
- package/stories/enhancements/MeshGradients.stories.js +320 -0
- package/stories/enhancements/OpenGroups.stories.js +227 -0
- package/stories/enhancements/RangeSliders.stories.js +232 -0
- package/stories/enhancements/RequiredFields.stories.js +189 -0
- package/stories/enhancements/Toggles.stories.js +167 -0
- package/stories/foundations/Colors.stories.js +283 -0
- package/stories/foundations/Icons.stories.js +305 -0
- package/stories/foundations/SmartSurfaces.stories.js +367 -0
- package/stories/foundations/Spacing.stories.js +175 -0
- package/stories/foundations/Typography.stories.js +960 -0
- package/stories/foundations/ZIndex.stories.js +325 -0
- package/stories/patterns/BorderEffects.stories.js +72 -0
- package/stories/patterns/Layout.stories.js +99 -0
- package/stories/patterns/Utilities.stories.js +107 -0
- package/stories/primitives/Accordion.stories.js +359 -0
- package/stories/primitives/Alerts.stories.js +64 -0
- package/stories/primitives/Badges.stories.js +183 -0
- package/stories/primitives/Buttons.stories.js +229 -0
- package/stories/primitives/Cards.stories.js +353 -0
- package/stories/primitives/FormGroups.stories.js +569 -0
- package/stories/primitives/Forms.stories.js +131 -0
- package/stories/primitives/Media.stories.js +203 -0
- package/stories/primitives/Tables.stories.js +232 -0
- package/stories/reference/ReferenceCatalog.stories.js +28 -0
- package/stories/reference/reference-catalog.js +413 -0
- package/stories/reference/reference-docs.js +302 -0
- package/stories/reference/reference-helpers.js +310 -0
- package/stories/utilities/GridSystem.stories.js +208 -0
- package/stories/utils/PdsAsk.stories.js +420 -0
- package/stories/utils/toast-utils.js +148 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// Pure Design System Ontology (PDS)
|
|
2
|
+
// This file is the single source-of-truth metadata for primitives, components, tokens, themes and enhancements.
|
|
3
|
+
|
|
4
|
+
export const ontology = {
|
|
5
|
+
meta: { name: "Pure Design System Ontology", version: "0.1" },
|
|
6
|
+
|
|
7
|
+
tokens: {
|
|
8
|
+
colors: ["primary", "secondary", "accent", "success", "warning", "danger", "info"],
|
|
9
|
+
spacing: ["xs", "sm", "md", "lg", "xl"],
|
|
10
|
+
typography: ["heading", "body", "mono"],
|
|
11
|
+
themes: ["light", "dark"],
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
primitives: [
|
|
16
|
+
{ id: "badge", name: "Badge / Pill", selectors: [".badge", ".pill", ".tag", ".chip"] },
|
|
17
|
+
{ id: "card", name: "Card", selectors: [".card", ".card-basic", ".card-elevated", ".card-outlined", ".card-interactive"] },
|
|
18
|
+
{ id: "surface", name: "Surface", selectors: [".surface", ".surface-base", ".surface-raised", ".surface-overlay", ".surface-subtle", ".surface-elevated", ".surface-sunken"] },
|
|
19
|
+
{ id: "alert", name: "Alert", selectors: [".alert", ".alert-info", ".alert-success", ".alert-warning", ".alert-danger"] },
|
|
20
|
+
{ id: "dialog", name: "Dialog", selectors: ["dialog", ".dialog"] },
|
|
21
|
+
{ id: "table", name: "Table", selectors: ["table", ".table-responsive", ".data-table"] },
|
|
22
|
+
{ id: "button", name: "Button", selectors: ["button", "[class^='btn-']", ".icon-only"] },
|
|
23
|
+
{ id: "fieldset", name: "Fieldset Group", selectors: ["fieldset[role='group']", "fieldset[role='radiogroup']"] },
|
|
24
|
+
{ id: "label-field", name: "Label+Input", selectors: ["label"] },
|
|
25
|
+
{ id: "accordion", name: "Accordion", selectors: [".accordion", ".accordion-item", "details"] },
|
|
26
|
+
{ id: "icon", name: "Icon", selectors: ["pds-icon", ".icon", ".icon-*"] },
|
|
27
|
+
{ id: "figure", name: "Figure/Media", selectors: ["figure", "figure.media"] },
|
|
28
|
+
{ id: "gallery", name: "Gallery", selectors: [".gallery", ".gallery-grid"] },
|
|
29
|
+
],
|
|
30
|
+
|
|
31
|
+
components: [
|
|
32
|
+
{ id: "pds-tabstrip", name: "Tab Strip", selectors: ["pds-tabstrip"] },
|
|
33
|
+
{ id: "pds-drawer", name: "Drawer", selectors: ["pds-drawer"] },
|
|
34
|
+
{ id: "pds-upload", name: "Upload", selectors: ["pds-upload"] },
|
|
35
|
+
],
|
|
36
|
+
|
|
37
|
+
// Layout utilities - patterns for structuring content
|
|
38
|
+
layoutPatterns: [
|
|
39
|
+
{ id: "grid", name: "Grid Container", selectors: [".grid", ".demo-grid"], description: "CSS Grid layout container" },
|
|
40
|
+
{ id: "grid-auto", name: "Auto-fit Grid", selectors: [".grid-auto-sm", ".grid-auto-md", ".grid-auto-lg", ".grid-auto-xl"], description: "Responsive auto-fit grid" },
|
|
41
|
+
{ id: "grid-cols", name: "Grid Columns", selectors: [".grid-cols-1", ".grid-cols-2", ".grid-cols-3", ".grid-cols-4", ".grid-cols-6"], description: "Fixed column grid" },
|
|
42
|
+
{ id: "flex", name: "Flex Container", selectors: [".flex", ".flex-wrap"], description: "Flexbox layout container" },
|
|
43
|
+
{ id: "container", name: "Container", selectors: [".container"], description: "Centered max-width container" },
|
|
44
|
+
{ id: "media-grid", name: "Media Grid", selectors: [".media-grid"], description: "Grid for media elements" },
|
|
45
|
+
],
|
|
46
|
+
|
|
47
|
+
utilities: [
|
|
48
|
+
".btn-group",
|
|
49
|
+
".demo-grid",
|
|
50
|
+
".color-scale",
|
|
51
|
+
".gap-*",
|
|
52
|
+
".items-*",
|
|
53
|
+
".justify-*",
|
|
54
|
+
".border-gradient",
|
|
55
|
+
".border-gradient-primary",
|
|
56
|
+
".border-gradient-accent",
|
|
57
|
+
".border-gradient-secondary",
|
|
58
|
+
".border-gradient-soft",
|
|
59
|
+
".border-gradient-medium",
|
|
60
|
+
".border-gradient-strong",
|
|
61
|
+
".border-glow",
|
|
62
|
+
".border-glow-sm",
|
|
63
|
+
".border-glow-lg",
|
|
64
|
+
".border-gradient-glow",
|
|
65
|
+
".border-glow-*"
|
|
66
|
+
],
|
|
67
|
+
|
|
68
|
+
styles: {
|
|
69
|
+
typography: ["headings", "body", "code"],
|
|
70
|
+
icons: { source: "svg", sets: ["core", "brand"] },
|
|
71
|
+
interactive: ["focus", "hover", "active"],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Safe matches with try/catch for invalid selectors or environments without .matches
|
|
76
|
+
function tryMatches(el, selector) {
|
|
77
|
+
if (!el || !selector) return false;
|
|
78
|
+
try {
|
|
79
|
+
return el.matches(selector);
|
|
80
|
+
} catch (e) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function safeClosest(el, selector) {
|
|
86
|
+
if (!el || !selector || !el.closest) return null;
|
|
87
|
+
try {
|
|
88
|
+
return el.closest(selector);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Find component for an element using the ontology
|
|
95
|
+
export function findComponentForElement(startEl, { maxDepth = 5 } = {}) {
|
|
96
|
+
if (!startEl) return null;
|
|
97
|
+
if (startEl.closest && startEl.closest('.showcase-toc')) return null;
|
|
98
|
+
|
|
99
|
+
let current = startEl;
|
|
100
|
+
let depth = 0;
|
|
101
|
+
|
|
102
|
+
while (current && depth < maxDepth) {
|
|
103
|
+
depth++;
|
|
104
|
+
|
|
105
|
+
// never traverse past the showcase
|
|
106
|
+
if (current.tagName === 'DS-SHOWCASE') return null;
|
|
107
|
+
|
|
108
|
+
// skip the section wrapper and continue climbing
|
|
109
|
+
if (current.classList && current.classList.contains('showcase-section')) {
|
|
110
|
+
current = current.parentElement;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 1) progressive enhancements
|
|
115
|
+
for (const sel of PDS.ontology.enhancements) {
|
|
116
|
+
if (tryMatches(current, sel)) {
|
|
117
|
+
return { element: current, componentType: 'enhanced-component', displayName: sel };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 2) Fieldset role groups
|
|
122
|
+
if (current.tagName === 'FIELDSET') {
|
|
123
|
+
const role = current.getAttribute('role');
|
|
124
|
+
if (role === 'group' || role === 'radiogroup') {
|
|
125
|
+
return { element: current, componentType: 'form-group', displayName: role === 'radiogroup' ? 'radio group' : 'form group' };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 3) label+input
|
|
130
|
+
if (current.tagName === 'LABEL') {
|
|
131
|
+
if (current.querySelector && current.querySelector('input,select,textarea')) {
|
|
132
|
+
return { element: current, componentType: 'form-control', displayName: 'label with input' };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const labelAncestor = current.closest ? current.closest('label') : null;
|
|
136
|
+
if (labelAncestor && labelAncestor.querySelector && labelAncestor.querySelector('input,select,textarea')) {
|
|
137
|
+
return { element: labelAncestor, componentType: 'form-control', displayName: 'label with input' };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 4) primitives
|
|
141
|
+
for (const prim of PDS.ontology.primitives) {
|
|
142
|
+
// handle each selector safely, support wildcard class prefix like .icon-*
|
|
143
|
+
for (const sel of prim.selectors || []) {
|
|
144
|
+
const s = String(sel || '').trim();
|
|
145
|
+
|
|
146
|
+
// Wildcard class prefix handling (e.g., .icon-*)
|
|
147
|
+
if (s.includes('*')) {
|
|
148
|
+
// Only support simple class wildcard like .prefix-*
|
|
149
|
+
if (s.startsWith('.')) {
|
|
150
|
+
const prefix = s.slice(1).replace(/\*/g, '');
|
|
151
|
+
if (current.classList && Array.from(current.classList).some((c) => c.startsWith(prefix))) {
|
|
152
|
+
return { element: current, componentType: 'pds-primitive', displayName: prim.name || prim.id };
|
|
153
|
+
}
|
|
154
|
+
// Also try to find an ancestor with such a class (but do not use closest with wildcard)
|
|
155
|
+
let ancestor = current.parentElement;
|
|
156
|
+
let levels = 0;
|
|
157
|
+
while (ancestor && levels < maxDepth) {
|
|
158
|
+
if (ancestor.classList && Array.from(ancestor.classList).some((c) => c.startsWith(prefix)) && ancestor.tagName !== 'DS-SHOWCASE') {
|
|
159
|
+
return { element: ancestor, componentType: 'pds-primitive', displayName: prim.name || prim.id };
|
|
160
|
+
}
|
|
161
|
+
ancestor = ancestor.parentElement;
|
|
162
|
+
levels++;
|
|
163
|
+
}
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// unsupported wildcard pattern - skip
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Normal selector: try matches, then safeClosest
|
|
171
|
+
if (tryMatches(current, s)) {
|
|
172
|
+
return { element: current, componentType: 'pds-primitive', displayName: prim.name || prim.id };
|
|
173
|
+
}
|
|
174
|
+
const ancestor = safeClosest(current, s);
|
|
175
|
+
if (ancestor && ancestor.tagName !== 'DS-SHOWCASE') {
|
|
176
|
+
return { element: ancestor, componentType: 'pds-primitive', displayName: prim.name || prim.id };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// class prefix fallback for selectors that are like .icon-* written differently
|
|
181
|
+
if (current.classList) {
|
|
182
|
+
const clsList = Array.from(current.classList);
|
|
183
|
+
for (const s of prim.selectors || []) {
|
|
184
|
+
if (typeof s === 'string' && s.includes('*') && s.startsWith('.')) {
|
|
185
|
+
const prefix = s.slice(1).replace(/\*/g, '');
|
|
186
|
+
if (clsList.some((c) => c.startsWith(prefix))) {
|
|
187
|
+
return { element: current, componentType: 'pds-primitive', displayName: prim.name || prim.id };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 4.5) layout patterns - check before going higher in tree
|
|
195
|
+
for (const layout of PDS.ontology.layoutPatterns || []) {
|
|
196
|
+
for (const sel of layout.selectors || []) {
|
|
197
|
+
const s = String(sel || '').trim();
|
|
198
|
+
|
|
199
|
+
// Wildcard handling for gap-*, items-*, etc.
|
|
200
|
+
if (s.includes('*')) {
|
|
201
|
+
if (s.startsWith('.')) {
|
|
202
|
+
const prefix = s.slice(1).replace(/\*/g, '');
|
|
203
|
+
if (current.classList && Array.from(current.classList).some((c) => c.startsWith(prefix))) {
|
|
204
|
+
return { element: current, componentType: 'layout-pattern', displayName: layout.name || layout.id };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Normal selector
|
|
211
|
+
if (tryMatches(current, s)) {
|
|
212
|
+
return { element: current, componentType: 'layout-pattern', displayName: layout.name || layout.id };
|
|
213
|
+
}
|
|
214
|
+
const ancestor = safeClosest(current, s);
|
|
215
|
+
if (ancestor && ancestor.tagName !== 'DS-SHOWCASE') {
|
|
216
|
+
return { element: ancestor, componentType: 'layout-pattern', displayName: layout.name || layout.id };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// 5) web components
|
|
222
|
+
if (current.tagName && current.tagName.includes('-')) {
|
|
223
|
+
return { element: current, componentType: 'web-component', displayName: current.tagName.toLowerCase() };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 6) button/icon
|
|
227
|
+
if (current.tagName === 'BUTTON') {
|
|
228
|
+
const hasIcon = current.querySelector && current.querySelector('pds-icon');
|
|
229
|
+
return { element: current, componentType: 'button', displayName: hasIcon ? 'button with icon' : 'button' };
|
|
230
|
+
}
|
|
231
|
+
if (tryMatches(current, 'pds-icon') || (current.closest && current.closest('pds-icon'))) {
|
|
232
|
+
const el = tryMatches(current, 'pds-icon') ? current : current.closest('pds-icon');
|
|
233
|
+
return { element: el, componentType: 'icon', displayName: `pds-icon (${el.getAttribute && el.getAttribute('icon') || 'unknown'})` };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 7) nav dropdown
|
|
237
|
+
if (tryMatches(current, 'nav[data-dropdown]') || (current.closest && current.closest('nav[data-dropdown]'))) {
|
|
238
|
+
const el = tryMatches(current, 'nav[data-dropdown]') ? current : current.closest('nav[data-dropdown]');
|
|
239
|
+
return { element: el, componentType: 'navigation', displayName: 'dropdown menu' };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// climb
|
|
243
|
+
current = current.parentElement;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function getAllSelectors() {
|
|
250
|
+
const s = [];
|
|
251
|
+
for (const p of PDS.ontology.primitives) s.push(...(p.selectors || []));
|
|
252
|
+
for (const c of PDS.ontology.components) s.push(...(c.selectors || []));
|
|
253
|
+
return Array.from(new Set(s));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export default ontology;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const DEFAULT_SEGMENT = "pds";
|
|
2
|
+
const URL_PATTERN = /^([a-z][a-z0-9+\-.]*:)?\/\//i;
|
|
3
|
+
const DRIVE_PATTERN = /^[a-z]:/i;
|
|
4
|
+
|
|
5
|
+
function ensureTrailingSlash(value = "") {
|
|
6
|
+
return value.endsWith("/") ? value : `${value}/`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function appendSegmentIfMissing(input = "", segment = DEFAULT_SEGMENT) {
|
|
10
|
+
const trimmed = input.replace(/\/+$/, "");
|
|
11
|
+
const regex = new RegExp(`(?:^|\/)${segment}$`, "i");
|
|
12
|
+
if (regex.test(trimmed)) {
|
|
13
|
+
return trimmed;
|
|
14
|
+
}
|
|
15
|
+
return `${trimmed}/${segment}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function stripLeadingDotSlash(value) {
|
|
19
|
+
return value.replace(/^\.\/+/, "");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function stripDriveLetter(value) {
|
|
23
|
+
if (DRIVE_PATTERN.test(value)) {
|
|
24
|
+
return value.replace(DRIVE_PATTERN, "").replace(/^\/+/, "");
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function stripPublicPrefix(value) {
|
|
30
|
+
if (value.startsWith("public/")) {
|
|
31
|
+
return value.substring("public/".length);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the public asset root URL for PDS static assets based on the consumer config.
|
|
38
|
+
* Falls back to "/assets/pds/" when no root is provided. Automatically appends the
|
|
39
|
+
* "pds" segment when the provided root omits it.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} config - Unified PDS configuration.
|
|
42
|
+
* @param {{ defaultRoot?: string, segment?: string }} [options]
|
|
43
|
+
* @returns {string} Normalized URL (always ends with a trailing slash).
|
|
44
|
+
*/
|
|
45
|
+
export function resolvePublicAssetURL(config, options = {}) {
|
|
46
|
+
const segment = options.segment || DEFAULT_SEGMENT;
|
|
47
|
+
const defaultRoot = options.defaultRoot || `/assets/${segment}/`;
|
|
48
|
+
const candidate =
|
|
49
|
+
(config?.public && config.public?.root) ||
|
|
50
|
+
(config?.static && config.static?.root) ||
|
|
51
|
+
null;
|
|
52
|
+
|
|
53
|
+
if (!candidate || typeof candidate !== "string") {
|
|
54
|
+
return ensureTrailingSlash(defaultRoot);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let normalized = candidate.trim();
|
|
58
|
+
if (!normalized) {
|
|
59
|
+
return ensureTrailingSlash(defaultRoot);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
normalized = normalized.replace(/\\/g, "/");
|
|
63
|
+
normalized = appendSegmentIfMissing(normalized, segment);
|
|
64
|
+
normalized = ensureTrailingSlash(normalized);
|
|
65
|
+
|
|
66
|
+
if (URL_PATTERN.test(normalized)) {
|
|
67
|
+
return normalized;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
normalized = stripLeadingDotSlash(normalized);
|
|
71
|
+
normalized = stripDriveLetter(normalized);
|
|
72
|
+
|
|
73
|
+
if (normalized.startsWith("/")) {
|
|
74
|
+
return ensureTrailingSlash(normalized);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
normalized = stripPublicPrefix(normalized);
|
|
78
|
+
|
|
79
|
+
if (!normalized.startsWith("/")) {
|
|
80
|
+
normalized = `/${normalized}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
normalized = normalized.replace(/\/+/g, (match, offset) =>
|
|
84
|
+
offset === 0 ? match : "/"
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return ensureTrailingSlash(normalized);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Extract the raw public root candidate from config for reuse in Node-side helpers.
|
|
92
|
+
* @param {object} config
|
|
93
|
+
* @returns {string|null}
|
|
94
|
+
*/
|
|
95
|
+
export function getPublicRootCandidate(config) {
|
|
96
|
+
if (config?.public?.root) return config.public.root;
|
|
97
|
+
if (config?.static?.root) return config.static.root;
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const __internal = {
|
|
102
|
+
ensureTrailingSlash,
|
|
103
|
+
appendSegmentIfMissing,
|
|
104
|
+
stripLeadingDotSlash,
|
|
105
|
+
stripDriveLetter,
|
|
106
|
+
stripPublicPrefix,
|
|
107
|
+
URL_PATTERN,
|
|
108
|
+
DEFAULT_SEGMENT,
|
|
109
|
+
};
|