@stacksjs/stx 0.2.119 → 0.2.121
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/dist/builtins/attrs.d.ts +16 -0
- package/dist/builtins/attrs.js +16 -0
- package/dist/builtins/safe-image.js +2 -9
- package/dist/builtins/stx-image.js +2 -9
- package/dist/builtins/stx-link.js +2 -9
- package/dist/cli.js +1869 -863
- package/dist/client-script-bundler.d.ts +9 -7
- package/dist/client-script-bundler.js +43 -4
- package/dist/client-script.d.ts +24 -0
- package/dist/client-script.js +59 -5
- package/dist/component-library.js +30 -12
- package/dist/component-registry.d.ts +1 -0
- package/dist/component-renderer.js +13 -5
- package/dist/composables/index.d.ts +1 -0
- package/dist/composables/index.js +1 -0
- package/dist/composables/use-scroll-lock.d.ts +13 -0
- package/dist/composables/use-scroll-lock.js +55 -0
- package/dist/composition-api.d.ts +1 -1
- package/dist/composition-api.js +13 -4
- package/dist/craft-bridge.d.ts +80 -1
- package/dist/craft-bridge.js +80 -1
- package/dist/craft-components.d.ts +77 -3
- package/dist/craft-components.js +138 -9
- package/dist/craft.js +12708 -118
- package/dist/dev-server/crosswind.d.ts +15 -1
- package/dist/dev-server/crosswind.js +12 -3
- package/dist/dev-server/serve-app.js +3 -3
- package/dist/document-shell.d.ts +17 -0
- package/dist/document-shell.js +72 -2
- package/dist/expressions.js +14 -7
- package/dist/head.d.ts +8 -0
- package/dist/head.js +11 -8
- package/dist/html-masking.d.ts +12 -0
- package/dist/html-masking.js +50 -15
- package/dist/index.d.ts +9 -1
- package/dist/index.js +9 -1
- package/dist/native-build.d.ts +6 -0
- package/dist/native-build.js +77 -60
- package/dist/process.js +11 -3
- package/dist/pwa.js +1232 -508
- package/dist/render.js +6 -1
- package/dist/runtime-globals.d.ts +7 -4
- package/dist/runtime-globals.js +10 -2
- package/dist/runtime-injection.js +15 -1
- package/dist/signal-processing.js +19 -28
- package/dist/signals-api.d.ts +2 -0
- package/dist/signals-api.js +9 -0
- package/dist/signals.js +785 -62
- package/dist/ssg.js +1176 -452
- package/dist/state-management.d.ts +1 -1
- package/dist/state-management.js +1 -1
- package/dist/strict-lint.d.ts +1 -1
- package/dist/strict-lint.js +1 -1
- package/dist/stx.d.ts +6 -0
- package/dist/style-scoping.js +10 -0
- package/dist/utils.js +5 -3
- package/dist/variable-extractor.js +14 -22
- package/dist/vue-template.js +3 -2
- package/dist/x-element.d.ts +2 -2
- package/dist/x-element.js +9 -8
- package/package.json +5 -5
- package/stx.d.ts +6 -0
package/dist/craft-bridge.js
CHANGED
|
@@ -159,6 +159,12 @@ else if (data.type === 'event' && data.method) {
|
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
// Live component handles, keyed by id, so an event coming back from the host
|
|
163
|
+
// can be routed to the callbacks registered on the right one. Without this
|
|
164
|
+
// the onSelect/onSpaceChange callbacks were stored and never invoked.
|
|
165
|
+
const sidebars = new Map();
|
|
166
|
+
const spacesSidebars = new Map();
|
|
167
|
+
|
|
162
168
|
function createNativeUIFacade() {
|
|
163
169
|
class Sidebar {
|
|
164
170
|
constructor(id) {
|
|
@@ -178,10 +184,42 @@ else if (data.type === 'event' && data.method) {
|
|
|
178
184
|
return this;
|
|
179
185
|
}
|
|
180
186
|
destroy() {
|
|
187
|
+
sidebars.delete(this.id);
|
|
181
188
|
sendNativeUI('destroyComponent', { id: this.id, type: 'sidebar' });
|
|
182
189
|
}
|
|
183
190
|
}
|
|
184
191
|
|
|
192
|
+
/**
|
|
193
|
+
* An Arc-style sidebar of swipeable spaces.
|
|
194
|
+
*
|
|
195
|
+
* Separate from Sidebar rather than an option on it: a source list selects
|
|
196
|
+
* one row out of many, while a spaces sidebar selects one whole SCENE, and
|
|
197
|
+
* a host that renders the switcher natively (a segmented control in the
|
|
198
|
+
* toolbar, a Touch Bar strip) needs the space list, not the item list.
|
|
199
|
+
*/
|
|
200
|
+
class SpacesSidebar {
|
|
201
|
+
constructor(id) {
|
|
202
|
+
this.id = id;
|
|
203
|
+
this._spaceCallbacks = [];
|
|
204
|
+
}
|
|
205
|
+
setSpaces(spaces) {
|
|
206
|
+
sendNativeUI('setSidebarSpaces', { sidebarId: this.id, spaces });
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
setActiveSpace(spaceId) {
|
|
210
|
+
sendNativeUI('setActiveSpace', { sidebarId: this.id, spaceId });
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
onSpaceChange(callback) {
|
|
214
|
+
this._spaceCallbacks.push(callback);
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
destroy() {
|
|
218
|
+
spacesSidebars.delete(this.id);
|
|
219
|
+
sendNativeUI('destroyComponent', { id: this.id, type: 'spacesSidebar' });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
185
223
|
class FileBrowser {
|
|
186
224
|
constructor(id) {
|
|
187
225
|
this.id = id;
|
|
@@ -218,12 +256,33 @@ else if (data.type === 'event' && data.method) {
|
|
|
218
256
|
}
|
|
219
257
|
}
|
|
220
258
|
|
|
259
|
+
// Host -> page. The host names the component in the payload; we look up the
|
|
260
|
+
// handle and fan out to whatever the page registered on it.
|
|
261
|
+
on('sidebar.select', function(params) {
|
|
262
|
+
const sidebar = sidebars.get(params && params.sidebarId);
|
|
263
|
+
if (sidebar) sidebar._selectCallbacks.forEach(fn => fn(params.itemId));
|
|
264
|
+
});
|
|
265
|
+
on('sidebar.spaceChange', function(params) {
|
|
266
|
+
const sidebar = spacesSidebars.get(params && params.sidebarId);
|
|
267
|
+
if (sidebar) sidebar._spaceCallbacks.forEach(fn => fn(params.spaceId));
|
|
268
|
+
});
|
|
269
|
+
|
|
221
270
|
return {
|
|
222
271
|
createSidebar(options) {
|
|
223
272
|
const opts = options || {};
|
|
224
273
|
const id = opts.id || 'sidebar-' + Date.now() + '-' + Math.random().toString(36).slice(2);
|
|
225
274
|
sendNativeUI('createSidebar', Object.assign({}, opts, { id }));
|
|
226
|
-
|
|
275
|
+
const sidebar = new Sidebar(id);
|
|
276
|
+
sidebars.set(id, sidebar);
|
|
277
|
+
return sidebar;
|
|
278
|
+
},
|
|
279
|
+
createSpacesSidebar(options) {
|
|
280
|
+
const opts = options || {};
|
|
281
|
+
const id = opts.id || 'spaces-' + Date.now() + '-' + Math.random().toString(36).slice(2);
|
|
282
|
+
sendNativeUI('createSpacesSidebar', Object.assign({}, opts, { id }));
|
|
283
|
+
const sidebar = new SpacesSidebar(id);
|
|
284
|
+
spacesSidebars.set(id, sidebar);
|
|
285
|
+
return sidebar;
|
|
227
286
|
},
|
|
228
287
|
createFileBrowser(options) {
|
|
229
288
|
const opts = options || {};
|
|
@@ -372,6 +431,26 @@ else if (data.type === 'event' && data.method) {
|
|
|
372
431
|
platform: () => request('process.platform'),
|
|
373
432
|
},
|
|
374
433
|
|
|
434
|
+
// Gesture stream
|
|
435
|
+
//
|
|
436
|
+
// A webview's wheel events carry no phase information, so a page can only
|
|
437
|
+
// guess where a trackpad swipe begins and ends \u2014 typically with an idle
|
|
438
|
+
// timeout, which mistimes the release and cannot see momentum at all. The
|
|
439
|
+
// host holds the real NSEvent, so it forwards the phases instead:
|
|
440
|
+
//
|
|
441
|
+
// { axis: 'horizontal' | 'vertical',
|
|
442
|
+
// phase: 'begin' | 'change' | 'end',
|
|
443
|
+
// deltaX, deltaY, // points since the last event
|
|
444
|
+
// velocityX, velocityY, // points per second, at 'end'
|
|
445
|
+
// momentum: boolean } // the OS is coasting, not the finger
|
|
446
|
+
//
|
|
447
|
+
// Consumers must stay correct when nothing ever arrives: a host without
|
|
448
|
+
// gesture support simply never emits, so keep the wheel path bound and let
|
|
449
|
+
// a begin phase take over from it.
|
|
450
|
+
gestures: {
|
|
451
|
+
onSwipe: (callback) => on('gesture.swipe', callback),
|
|
452
|
+
},
|
|
453
|
+
|
|
375
454
|
// Native component helpers
|
|
376
455
|
nativeUI,
|
|
377
456
|
components: componentAPI
|
|
@@ -123,7 +123,14 @@ export declare const CRAFT_COMPONENTS: {
|
|
|
123
123
|
nativeType: 'craft-sidebar';
|
|
124
124
|
fallbackTag: 'aside';
|
|
125
125
|
fallbackClasses: 'craft-sidebar craft-sidebar-tahoe';
|
|
126
|
-
nativeProps: readonly ['id', 'variant', 'material', 'backgroundEffect', 'allowsVibrancy', 'materialOpacity', 'materialDarkOpacity', 'minWidth', 'maxWidth', 'canCollapse', 'searchPlaceholder', 'selectedItem', 'sections'];
|
|
126
|
+
nativeProps: readonly ['id', 'variant', 'material', 'backgroundEffect', 'allowsVibrancy', 'materialOpacity', 'materialDarkOpacity', 'minWidth', 'maxWidth', 'canCollapse', 'searchPlaceholder', 'selectedItem', 'sections', 'spaces', 'selectedSpace'];
|
|
127
|
+
render: unknown
|
|
128
|
+
};
|
|
129
|
+
'sidebar-space': {
|
|
130
|
+
nativeType: 'craft-sidebar-space';
|
|
131
|
+
fallbackTag: 'section';
|
|
132
|
+
fallbackClasses: 'craft-sidebar-space';
|
|
133
|
+
nativeProps: readonly ['id', 'label', 'icon', 'tint'];
|
|
127
134
|
render: unknown
|
|
128
135
|
};
|
|
129
136
|
'sidebar-section': {
|
|
@@ -503,6 +510,48 @@ export declare const CRAFT_COMPONENT_STYLES: `
|
|
|
503
510
|
background: rgba(255, 255, 255, 0.24);
|
|
504
511
|
}
|
|
505
512
|
|
|
513
|
+
/* Spaces fallback. Stacked in document order behind a rail — legible without
|
|
514
|
+
a native host, and deliberately not a second swipe implementation. */
|
|
515
|
+
.craft-sidebar-spaces {
|
|
516
|
+
position: relative;
|
|
517
|
+
z-index: 1;
|
|
518
|
+
flex: 1;
|
|
519
|
+
min-height: 0;
|
|
520
|
+
overflow-y: auto;
|
|
521
|
+
}
|
|
522
|
+
.craft-sidebar-space-title {
|
|
523
|
+
padding: 10px 8px 4px;
|
|
524
|
+
font-size: 13px;
|
|
525
|
+
font-weight: 600;
|
|
526
|
+
}
|
|
527
|
+
.craft-sidebar-space-switcher {
|
|
528
|
+
position: relative;
|
|
529
|
+
z-index: 1;
|
|
530
|
+
display: flex;
|
|
531
|
+
align-items: center;
|
|
532
|
+
gap: 1px;
|
|
533
|
+
padding-top: 6px;
|
|
534
|
+
}
|
|
535
|
+
.craft-sidebar-space-tab {
|
|
536
|
+
display: grid;
|
|
537
|
+
place-items: center;
|
|
538
|
+
width: 28px;
|
|
539
|
+
height: 26px;
|
|
540
|
+
border: 0;
|
|
541
|
+
border-radius: 7px;
|
|
542
|
+
background: transparent;
|
|
543
|
+
color: inherit;
|
|
544
|
+
font: inherit;
|
|
545
|
+
font-size: 11px;
|
|
546
|
+
font-weight: 700;
|
|
547
|
+
opacity: 0.4;
|
|
548
|
+
cursor: default;
|
|
549
|
+
}
|
|
550
|
+
.craft-sidebar-space-tab[data-selected="true"] {
|
|
551
|
+
opacity: 1;
|
|
552
|
+
background: rgba(255, 255, 255, 0.55);
|
|
553
|
+
}
|
|
554
|
+
|
|
506
555
|
.craft-code-editor {
|
|
507
556
|
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
508
557
|
font-size: 13px;
|
|
@@ -578,11 +627,36 @@ export declare const CRAFT_COMPONENT_RUNTIME: `
|
|
|
578
627
|
var config = parseConfig(sidebar);
|
|
579
628
|
if (!config) return;
|
|
580
629
|
|
|
630
|
+
var hasSpaces = !!(config.spaces && config.spaces.length);
|
|
631
|
+
// Spaces need a host that can swipe between whole scenes. Falling back to
|
|
632
|
+
// createSidebar would flatten every space into one list, which is worse
|
|
633
|
+
// than the web rendering already on the page — so leave that in place.
|
|
634
|
+
if (hasSpaces && typeof nativeUI.createSpacesSidebar !== 'function') return;
|
|
635
|
+
|
|
581
636
|
try {
|
|
582
637
|
var nativeConfig = Object.assign({}, config);
|
|
583
638
|
delete nativeConfig.sections;
|
|
584
|
-
|
|
585
|
-
|
|
639
|
+
delete nativeConfig.spaces;
|
|
640
|
+
|
|
641
|
+
var nativeSidebar = hasSpaces
|
|
642
|
+
? nativeUI.createSpacesSidebar(nativeConfig)
|
|
643
|
+
: nativeUI.createSidebar(nativeConfig);
|
|
644
|
+
|
|
645
|
+
if (nativeSidebar && hasSpaces && nativeSidebar.setSpaces) {
|
|
646
|
+
nativeSidebar.setSpaces(config.spaces);
|
|
647
|
+
}
|
|
648
|
+
if (nativeSidebar && hasSpaces && config.selectedSpace && nativeSidebar.setActiveSpace) {
|
|
649
|
+
nativeSidebar.setActiveSpace(config.selectedSpace);
|
|
650
|
+
}
|
|
651
|
+
if (nativeSidebar && hasSpaces && nativeSidebar.onSpaceChange) {
|
|
652
|
+
nativeSidebar.onSpaceChange(function(spaceId) {
|
|
653
|
+
sidebar.dispatchEvent(new CustomEvent('craft-sidebar:space', {
|
|
654
|
+
bubbles: true,
|
|
655
|
+
detail: { spaceId: spaceId, sidebarId: config.id }
|
|
656
|
+
}));
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
if (nativeSidebar && !hasSpaces && config.sections) {
|
|
586
660
|
config.sections.forEach(function(section) {
|
|
587
661
|
if (nativeSidebar.addSection) nativeSidebar.addSection(section);
|
|
588
662
|
});
|
package/dist/craft-components.js
CHANGED
|
@@ -218,10 +218,19 @@ export const CRAFT_COMPONENTS = {
|
|
|
218
218
|
"canCollapse",
|
|
219
219
|
"searchPlaceholder",
|
|
220
220
|
"selectedItem",
|
|
221
|
-
"sections"
|
|
221
|
+
"sections",
|
|
222
|
+
"spaces",
|
|
223
|
+
"selectedSpace"
|
|
222
224
|
],
|
|
223
225
|
render: renderCraftSidebar
|
|
224
226
|
},
|
|
227
|
+
"sidebar-space": {
|
|
228
|
+
nativeType: "craft-sidebar-space",
|
|
229
|
+
fallbackTag: "section",
|
|
230
|
+
fallbackClasses: "craft-sidebar-space",
|
|
231
|
+
nativeProps: ["id", "label", "icon", "tint"],
|
|
232
|
+
render: renderCraftSidebarSpace
|
|
233
|
+
},
|
|
225
234
|
"sidebar-section": {
|
|
226
235
|
nativeType: "craft-sidebar-section",
|
|
227
236
|
fallbackTag: "section",
|
|
@@ -620,6 +629,48 @@ export const CRAFT_COMPONENTS = {
|
|
|
620
629
|
background: rgba(255, 255, 255, 0.24);
|
|
621
630
|
}
|
|
622
631
|
|
|
632
|
+
/* Spaces fallback. Stacked in document order behind a rail \u2014 legible without
|
|
633
|
+
a native host, and deliberately not a second swipe implementation. */
|
|
634
|
+
.craft-sidebar-spaces {
|
|
635
|
+
position: relative;
|
|
636
|
+
z-index: 1;
|
|
637
|
+
flex: 1;
|
|
638
|
+
min-height: 0;
|
|
639
|
+
overflow-y: auto;
|
|
640
|
+
}
|
|
641
|
+
.craft-sidebar-space-title {
|
|
642
|
+
padding: 10px 8px 4px;
|
|
643
|
+
font-size: 13px;
|
|
644
|
+
font-weight: 600;
|
|
645
|
+
}
|
|
646
|
+
.craft-sidebar-space-switcher {
|
|
647
|
+
position: relative;
|
|
648
|
+
z-index: 1;
|
|
649
|
+
display: flex;
|
|
650
|
+
align-items: center;
|
|
651
|
+
gap: 1px;
|
|
652
|
+
padding-top: 6px;
|
|
653
|
+
}
|
|
654
|
+
.craft-sidebar-space-tab {
|
|
655
|
+
display: grid;
|
|
656
|
+
place-items: center;
|
|
657
|
+
width: 28px;
|
|
658
|
+
height: 26px;
|
|
659
|
+
border: 0;
|
|
660
|
+
border-radius: 7px;
|
|
661
|
+
background: transparent;
|
|
662
|
+
color: inherit;
|
|
663
|
+
font: inherit;
|
|
664
|
+
font-size: 11px;
|
|
665
|
+
font-weight: 700;
|
|
666
|
+
opacity: 0.4;
|
|
667
|
+
cursor: default;
|
|
668
|
+
}
|
|
669
|
+
.craft-sidebar-space-tab[data-selected="true"] {
|
|
670
|
+
opacity: 1;
|
|
671
|
+
background: rgba(255, 255, 255, 0.55);
|
|
672
|
+
}
|
|
673
|
+
|
|
623
674
|
.craft-code-editor {
|
|
624
675
|
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
625
676
|
font-size: 13px;
|
|
@@ -694,11 +745,36 @@ export const CRAFT_COMPONENTS = {
|
|
|
694
745
|
var config = parseConfig(sidebar);
|
|
695
746
|
if (!config) return;
|
|
696
747
|
|
|
748
|
+
var hasSpaces = !!(config.spaces && config.spaces.length);
|
|
749
|
+
// Spaces need a host that can swipe between whole scenes. Falling back to
|
|
750
|
+
// createSidebar would flatten every space into one list, which is worse
|
|
751
|
+
// than the web rendering already on the page \u2014 so leave that in place.
|
|
752
|
+
if (hasSpaces && typeof nativeUI.createSpacesSidebar !== 'function') return;
|
|
753
|
+
|
|
697
754
|
try {
|
|
698
755
|
var nativeConfig = Object.assign({}, config);
|
|
699
756
|
delete nativeConfig.sections;
|
|
700
|
-
|
|
701
|
-
|
|
757
|
+
delete nativeConfig.spaces;
|
|
758
|
+
|
|
759
|
+
var nativeSidebar = hasSpaces
|
|
760
|
+
? nativeUI.createSpacesSidebar(nativeConfig)
|
|
761
|
+
: nativeUI.createSidebar(nativeConfig);
|
|
762
|
+
|
|
763
|
+
if (nativeSidebar && hasSpaces && nativeSidebar.setSpaces) {
|
|
764
|
+
nativeSidebar.setSpaces(config.spaces);
|
|
765
|
+
}
|
|
766
|
+
if (nativeSidebar && hasSpaces && config.selectedSpace && nativeSidebar.setActiveSpace) {
|
|
767
|
+
nativeSidebar.setActiveSpace(config.selectedSpace);
|
|
768
|
+
}
|
|
769
|
+
if (nativeSidebar && hasSpaces && nativeSidebar.onSpaceChange) {
|
|
770
|
+
nativeSidebar.onSpaceChange(function(spaceId) {
|
|
771
|
+
sidebar.dispatchEvent(new CustomEvent('craft-sidebar:space', {
|
|
772
|
+
bubbles: true,
|
|
773
|
+
detail: { spaceId: spaceId, sidebarId: config.id }
|
|
774
|
+
}));
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
if (nativeSidebar && !hasSpaces && config.sections) {
|
|
702
778
|
config.sections.forEach(function(section) {
|
|
703
779
|
if (nativeSidebar.addSection) nativeSidebar.addSection(section);
|
|
704
780
|
});
|
|
@@ -755,10 +831,10 @@ export function processCraftComponents(content, config = {}) {
|
|
|
755
831
|
</body>`);
|
|
756
832
|
else
|
|
757
833
|
content += CRAFT_COMPONENT_RUNTIME;
|
|
758
|
-
content = content.replace(/<@craft-sidebar\s*([^>]*)>([\s\S]*?)<\/@craft-sidebar>/g, (match, propsStr, children) => {
|
|
834
|
+
content = content.replace(/<@craft-sidebar(?![-\w])\s*([^>]*)>([\s\S]*?)<\/@craft-sidebar>/g, (match, propsStr, children) => {
|
|
759
835
|
return renderCraftComponent("sidebar", propsStr, children.trim(), config, classPrefix);
|
|
760
836
|
});
|
|
761
|
-
content = content.replace(/<@craft-sidebar\s*([^>]*?)\/>/g, (match, propsStr) => {
|
|
837
|
+
content = content.replace(/<@craft-sidebar(?![-\w])\s*([^>]*?)\/>/g, (match, propsStr) => {
|
|
762
838
|
return renderCraftComponent("sidebar", propsStr, "", config, classPrefix);
|
|
763
839
|
});
|
|
764
840
|
content = content.replace(/<@craft-(\w+[-\w]*)\s*([^>]*?)\/>/g, (match, componentName, propsStr) => {
|
|
@@ -806,7 +882,7 @@ function parseProps(propsStr) {
|
|
|
806
882
|
return props;
|
|
807
883
|
}
|
|
808
884
|
function renderCraftSidebar(props, children) {
|
|
809
|
-
const id = normalizeId(String(props.id || `craft-sidebar-${hashString(children || JSON.stringify(props))}`)), sections = normalizeSidebarSections(props.sections) || extractSidebarSections(children), selectedItem = String(props.selectedItem || props.selected || sections[0]?.items[0]?.id || ""), config = {
|
|
885
|
+
const id = normalizeId(String(props.id || `craft-sidebar-${hashString(children || JSON.stringify(props))}`)), spaces = normalizeSidebarSpaces(props.spaces) || extractSidebarSpaces(children), sections = spaces.length ? [] : normalizeSidebarSections(props.sections) || extractSidebarSections(children), selectedSpace = String(props.selectedSpace || spaces[0]?.id || ""), openSpace = spaces.find((space) => space.id === selectedSpace) || spaces[0], selectedItem = String(props.selectedItem || props.selected || (spaces.length ? openSpace?.sections[0]?.items[0]?.id : sections[0]?.items[0]?.id) || ""), config = {
|
|
810
886
|
id,
|
|
811
887
|
variant: String(props.variant || "desktop"),
|
|
812
888
|
material: String(props.material || "sidebar"),
|
|
@@ -819,14 +895,30 @@ function renderCraftSidebar(props, children) {
|
|
|
819
895
|
canCollapse: props.canCollapse !== !1 && props.canCollapse !== "false",
|
|
820
896
|
searchPlaceholder: props.searchPlaceholder ? String(props.searchPlaceholder) : void 0,
|
|
821
897
|
selectedItem,
|
|
822
|
-
sections
|
|
823
|
-
|
|
898
|
+
sections,
|
|
899
|
+
spaces,
|
|
900
|
+
selectedSpace: selectedSpace || void 0
|
|
901
|
+
}, fallbackClasses = String(props.__fallbackClasses || "craft-sidebar craft-sidebar-tahoe"), width = Number(props.width || props.sidebarWidth || 286), native = props.native === !1 || props.native === "false" ? "false" : "true", search = config.searchPlaceholder ? `<div class="craft-sidebar-search"><input type="search" placeholder="${escapeHtml(config.searchPlaceholder)}"></div>` : "";
|
|
902
|
+
let body;
|
|
903
|
+
if (spaces.length)
|
|
904
|
+
body = renderSidebarSpacesFallback(spaces, selectedSpace, selectedItem);
|
|
905
|
+
else if (sections.length)
|
|
906
|
+
body = sections.map((section) => renderSidebarSectionFallback(section, selectedItem)).join("");
|
|
907
|
+
else
|
|
908
|
+
body = children;
|
|
824
909
|
return `<aside class="${escapeAttribute(fallbackClasses)}" data-craft="craft-sidebar" data-craft-native-sidebar data-craft-sidebar-id="${escapeAttribute(id)}" data-native="${native}" style="--craft-sidebar-width:${width}px">
|
|
825
910
|
${search}
|
|
826
|
-
${
|
|
911
|
+
${body}
|
|
827
912
|
</aside>
|
|
828
913
|
<script type="application/json" data-craft-sidebar-config="${escapeAttribute(id)}">${escapeScriptJson(config)}</script>`;
|
|
829
914
|
}
|
|
915
|
+
function renderCraftSidebarSpace(props, children) {
|
|
916
|
+
const id = normalizeId(String(props.id || props.label || "space"));
|
|
917
|
+
return `<section class="craft-sidebar-space" data-craft="craft-sidebar-space" data-space-id="${escapeAttribute(id)}">
|
|
918
|
+
${props.label ? `<div class="craft-sidebar-space-title">${escapeHtml(String(props.label))}</div>` : ""}
|
|
919
|
+
${children}
|
|
920
|
+
</section>`;
|
|
921
|
+
}
|
|
830
922
|
function renderCraftSidebarSection(props, children) {
|
|
831
923
|
const title = String(props.header || props.title || "");
|
|
832
924
|
return `<section class="craft-sidebar-section" data-craft="craft-sidebar-section" data-section-id="${escapeAttribute(String(props.id || "section"))}">
|
|
@@ -868,6 +960,43 @@ function extractSidebarItems(content) {
|
|
|
868
960
|
}
|
|
869
961
|
return items;
|
|
870
962
|
}
|
|
963
|
+
function extractSidebarSpaces(children) {
|
|
964
|
+
const spaces = [], spacePattern = /<@craft-sidebar-space\s*([^>]*?)(?:\/>|>([\s\S]*?)<\/@craft-sidebar-space>)/g;
|
|
965
|
+
let spaceMatch;
|
|
966
|
+
while ((spaceMatch = spacePattern.exec(children)) !== null) {
|
|
967
|
+
const props = parseProps(spaceMatch[1] || ""), body = spaceMatch[2] || "", label = String(props.label || props.title || `Space ${spaces.length + 1}`);
|
|
968
|
+
spaces.push({
|
|
969
|
+
id: String(props.id || normalizeId(label)),
|
|
970
|
+
label,
|
|
971
|
+
icon: props.icon ? String(props.icon) : void 0,
|
|
972
|
+
tint: props.tint ? String(props.tint) : void 0,
|
|
973
|
+
sections: extractSidebarSections(body)
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
return spaces;
|
|
977
|
+
}
|
|
978
|
+
function normalizeSidebarSpaces(value) {
|
|
979
|
+
if (!value)
|
|
980
|
+
return;
|
|
981
|
+
if (Array.isArray(value))
|
|
982
|
+
return value;
|
|
983
|
+
if (typeof value !== "string")
|
|
984
|
+
return;
|
|
985
|
+
try {
|
|
986
|
+
const parsed = JSON.parse(value);
|
|
987
|
+
return Array.isArray(parsed) ? parsed : void 0;
|
|
988
|
+
} catch {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
function renderSidebarSpacesFallback(spaces, selectedSpace, selectedItem) {
|
|
993
|
+
const rail = spaces.map((space) => `<button class="craft-sidebar-space-tab" type="button" data-space-target="${escapeAttribute(space.id)}" data-selected="${space.id === selectedSpace ? "true" : "false"}" title="${escapeAttribute(space.label || space.id)}">${space.icon ? `<span class="craft-sidebar-item-icon" data-icon="${escapeAttribute(space.icon)}"></span>` : escapeHtml((space.label || space.id).charAt(0))}</button>`).join("");
|
|
994
|
+
return `<div class="craft-sidebar-spaces">${spaces.map((space) => `<section class="craft-sidebar-space" data-space-id="${escapeAttribute(space.id)}" data-selected="${space.id === selectedSpace ? "true" : "false"}">
|
|
995
|
+
${space.label ? `<div class="craft-sidebar-space-title">${escapeHtml(space.label)}</div>` : ""}
|
|
996
|
+
${space.sections.map((section) => renderSidebarSectionFallback(section, selectedItem)).join("")}
|
|
997
|
+
</section>`).join("")}</div>
|
|
998
|
+
<div class="craft-sidebar-space-switcher">${rail}</div>`;
|
|
999
|
+
}
|
|
871
1000
|
function normalizeSidebarSections(value) {
|
|
872
1001
|
if (!value)
|
|
873
1002
|
return;
|