@jxsuite/studio 0.0.1
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/studio.css +3676 -0
- package/dist/studio.js +188743 -0
- package/dist/studio.js.map +1448 -0
- package/package.json +67 -0
- package/src/editor/context-menu.js +144 -0
- package/src/editor/inline-edit.js +597 -0
- package/src/editor/inline-format.js +572 -0
- package/src/editor/shortcuts.js +275 -0
- package/src/editor/slash-menu.js +167 -0
- package/src/files/components.js +40 -0
- package/src/files/file-ops.js +195 -0
- package/src/files/files.js +569 -0
- package/src/markdown/md-allowlist.js +101 -0
- package/src/markdown/md-convert.js +491 -0
- package/src/panels/activity-bar.js +69 -0
- package/src/panels/data-explorer.js +181 -0
- package/src/panels/events-panel.js +235 -0
- package/src/panels/imports-panel.js +427 -0
- package/src/panels/signals-panel.js +1093 -0
- package/src/panels/statusbar.js +56 -0
- package/src/platform.js +31 -0
- package/src/platforms/devserver.js +293 -0
- package/src/services/cem-export.js +130 -0
- package/src/services/code-services.js +98 -0
- package/src/site-context.js +122 -0
- package/src/state.js +744 -0
- package/src/store.js +332 -0
- package/src/studio.js +7692 -0
- package/src/ui/icons.js +83 -0
- package/src/ui/jx-styled-combobox.js +142 -0
- package/src/ui/spectrum.js +238 -0
- package/src/utils/studio-utils.js +185 -0
package/src/ui/icons.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// packages/studio/icons.js
|
|
2
|
+
// Icon templates for style sidebar button groups.
|
|
3
|
+
// Uses Spectrum workflow icons where available; custom SVGs for flex-specific concepts.
|
|
4
|
+
|
|
5
|
+
import { html } from "lit";
|
|
6
|
+
|
|
7
|
+
// Helper for custom filled-rect icons (alignment/justify diagrams) where no Spectrum match exists
|
|
8
|
+
const _R = (/** @type {any} */ d) =>
|
|
9
|
+
html`<svg
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
width="16"
|
|
12
|
+
height="16"
|
|
13
|
+
viewBox="0 0 24 24"
|
|
14
|
+
fill="currentColor"
|
|
15
|
+
stroke="none"
|
|
16
|
+
>
|
|
17
|
+
${d}
|
|
18
|
+
</svg>`;
|
|
19
|
+
|
|
20
|
+
// Helper for custom stroke icons
|
|
21
|
+
const _S = (/** @type {any} */ d) =>
|
|
22
|
+
html`<svg
|
|
23
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
24
|
+
width="16"
|
|
25
|
+
height="16"
|
|
26
|
+
viewBox="0 0 24 24"
|
|
27
|
+
fill="none"
|
|
28
|
+
stroke="currentColor"
|
|
29
|
+
stroke-width="2"
|
|
30
|
+
stroke-linecap="round"
|
|
31
|
+
stroke-linejoin="round"
|
|
32
|
+
>
|
|
33
|
+
${d}
|
|
34
|
+
</svg>`;
|
|
35
|
+
|
|
36
|
+
const icons = {
|
|
37
|
+
// ─── Arrows — flexDirection ───
|
|
38
|
+
"arrow-right": html`<sp-icon-arrow-right slot="icon"></sp-icon-arrow-right>`,
|
|
39
|
+
"arrow-left": html`<sp-icon-arrow-left slot="icon"></sp-icon-arrow-left>`,
|
|
40
|
+
"arrow-down": html`<sp-icon-arrow-down slot="icon"></sp-icon-arrow-down>`,
|
|
41
|
+
"arrow-up": html`<sp-icon-arrow-up slot="icon"></sp-icon-arrow-up>`,
|
|
42
|
+
|
|
43
|
+
// ─── Text align — textAlign ───
|
|
44
|
+
"text-align-left": html`<sp-icon-text-align-left slot="icon"></sp-icon-text-align-left>`,
|
|
45
|
+
"text-align-center": html`<sp-icon-text-align-center slot="icon"></sp-icon-text-align-center>`,
|
|
46
|
+
"text-align-right": html`<sp-icon-text-align-right slot="icon"></sp-icon-text-align-right>`,
|
|
47
|
+
"text-align-justify": html`<sp-icon-text-align-justify slot="icon"></sp-icon-text-align-justify>`,
|
|
48
|
+
|
|
49
|
+
// ─── flexWrap ───
|
|
50
|
+
"wrap-text": html`<sp-icon-flip-vertical slot="icon"></sp-icon-flip-vertical>`,
|
|
51
|
+
|
|
52
|
+
// ─── alignItems / alignSelf — vertical alignment within container ───
|
|
53
|
+
"align-start-v": html`<sp-icon-align-top slot="icon"></sp-icon-align-top>`,
|
|
54
|
+
"align-end-v": html`<sp-icon-align-bottom slot="icon"></sp-icon-align-bottom>`,
|
|
55
|
+
"align-center-v": html`<sp-icon-align-middle slot="icon"></sp-icon-align-middle>`,
|
|
56
|
+
"align-stretch-v": html`<sp-icon-distribute-vertically
|
|
57
|
+
slot="icon"
|
|
58
|
+
></sp-icon-distribute-vertically>`,
|
|
59
|
+
"align-baseline": html`<sp-icon-text-baseline-shift slot="icon"></sp-icon-text-baseline-shift>`,
|
|
60
|
+
|
|
61
|
+
// ─── justifyContent — horizontal distribution within container ───
|
|
62
|
+
"justify-start": html`<sp-icon-align-left slot="icon"></sp-icon-align-left>`,
|
|
63
|
+
"justify-end": html`<sp-icon-align-right slot="icon"></sp-icon-align-right>`,
|
|
64
|
+
"justify-center": html`<sp-icon-align-center slot="icon"></sp-icon-align-center>`,
|
|
65
|
+
"justify-between": html`<sp-icon-distribute-space-horiz
|
|
66
|
+
slot="icon"
|
|
67
|
+
></sp-icon-distribute-space-horiz>`,
|
|
68
|
+
"justify-around": html`<sp-icon-distribute-horizontally
|
|
69
|
+
slot="icon"
|
|
70
|
+
></sp-icon-distribute-horizontally>`,
|
|
71
|
+
"justify-evenly": html`<sp-icon-distribute-horizontal-center
|
|
72
|
+
slot="icon"
|
|
73
|
+
></sp-icon-distribute-horizontal-center>`,
|
|
74
|
+
|
|
75
|
+
// ─── display mode icons ───
|
|
76
|
+
"display-flex": html`<sp-icon-view-column slot="icon"></sp-icon-view-column>`,
|
|
77
|
+
"display-grid": html`<sp-icon-view-grid slot="icon"></sp-icon-view-grid>`,
|
|
78
|
+
"display-block": html`<sp-icon-box slot="icon"></sp-icon-box>`,
|
|
79
|
+
"display-inline": html`<sp-icon-remove slot="icon"></sp-icon-remove>`,
|
|
80
|
+
"display-none": html`<sp-icon-visibility-off slot="icon"></sp-icon-visibility-off>`,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default icons;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jx-styled-combobox — Dual-mode styled combobox custom element.
|
|
3
|
+
*
|
|
4
|
+
* Renders as sp-picker when the current value matches a predefined option, or as a textfield +
|
|
5
|
+
* dropdown overlay (manual combobox) when it doesn't. Both modes share identical styled menu items,
|
|
6
|
+
* ensuring visual consistency.
|
|
7
|
+
*
|
|
8
|
+
* Usage: html`<jx-styled-combobox size="s" .value=${"italic"} placeholder="normal" .options=${[{
|
|
9
|
+
* value: "italic", label: "Italic", style: "font-style: italic" }]} @change=${handler}
|
|
10
|
+
* @input=${handler}
|
|
11
|
+
*
|
|
12
|
+
* > </jx-styled-combobox>`
|
|
13
|
+
*
|
|
14
|
+
* Options format: { value: string, label: string, style?: string } — menu item { divider: true } —
|
|
15
|
+
* menu divider
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { LitElement, html } from "lit";
|
|
19
|
+
import { live } from "lit/directives/live.js";
|
|
20
|
+
|
|
21
|
+
/** @typedef {{ value: string; label: string; style?: string } | { divider: true }} ComboOption */
|
|
22
|
+
|
|
23
|
+
export class JxStyledCombobox extends LitElement {
|
|
24
|
+
static properties = {
|
|
25
|
+
value: { type: String },
|
|
26
|
+
placeholder: { type: String },
|
|
27
|
+
size: { type: String },
|
|
28
|
+
options: { attribute: false },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
constructor() {
|
|
32
|
+
super();
|
|
33
|
+
/** @type {string} */ this.value = "";
|
|
34
|
+
/** @type {string} */ this.placeholder = "";
|
|
35
|
+
/** @type {string} */ this.size = "s";
|
|
36
|
+
/** @type {ComboOption[]} */ this.options = [];
|
|
37
|
+
/** @type {string} */
|
|
38
|
+
this._menuId = "jx-combo-" + Math.random().toString(36).slice(2, 8);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** No shadow DOM — render directly into light DOM */
|
|
42
|
+
createRenderRoot() {
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Check if current value matches a predefined option */
|
|
47
|
+
get _isPicker() {
|
|
48
|
+
return (
|
|
49
|
+
!!this.value &&
|
|
50
|
+
this.options.some((/** @type {any} */ o) => !o.divider && o.value === this.value)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Get the selected option's style string for the picker button preview */
|
|
55
|
+
get _selectedStyle() {
|
|
56
|
+
if (!this._isPicker) return "";
|
|
57
|
+
const opt = this.options.find((/** @type {any} */ o) => !o.divider && o.value === this.value);
|
|
58
|
+
return /** @type {any} */ (opt)?.style || "";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Render menu items from options array */
|
|
62
|
+
_renderMenuItems() {
|
|
63
|
+
return this.options.map((/** @type {any} */ opt) =>
|
|
64
|
+
opt.divider
|
|
65
|
+
? html`<sp-menu-divider></sp-menu-divider>`
|
|
66
|
+
: html`<sp-menu-item value=${opt.value} style=${opt.style || ""}
|
|
67
|
+
>${opt.label}</sp-menu-item
|
|
68
|
+
>`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Picker mode: sp-picker @change handler */
|
|
73
|
+
_handlePickerChange(/** @type {any} */ e) {
|
|
74
|
+
e.stopPropagation(); // prevent sp-picker's raw event from reaching consumer
|
|
75
|
+
this.value = e.target.value;
|
|
76
|
+
this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Combobox mode: sp-menu @change handler */
|
|
80
|
+
_handleMenuChange(/** @type {any} */ e) {
|
|
81
|
+
e.stopPropagation(); // prevent sp-menu's raw event from reaching consumer
|
|
82
|
+
if (!e.target.value) return;
|
|
83
|
+
this.value = e.target.value;
|
|
84
|
+
this.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Combobox mode: textfield @input handler */
|
|
88
|
+
_handleInput(/** @type {any} */ e) {
|
|
89
|
+
e.stopPropagation(); // prevent sp-textfield's raw event from reaching consumer
|
|
90
|
+
this.value = e.target.value;
|
|
91
|
+
this.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Set popover min-width to match trigger width (replicates sp-picker behavior) */
|
|
95
|
+
_setPopoverWidth(/** @type {any} */ e) {
|
|
96
|
+
const group = this.querySelector(".jx-combobox-group");
|
|
97
|
+
const w = group ? /** @type {HTMLElement} */ (group).offsetWidth : 0;
|
|
98
|
+
const popover = e.target.querySelector("sp-popover");
|
|
99
|
+
if (popover && w) popover.style.minWidth = `${w}px`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
render() {
|
|
103
|
+
if (this._isPicker) {
|
|
104
|
+
return html`
|
|
105
|
+
<sp-picker
|
|
106
|
+
class="jx-combobox-picker"
|
|
107
|
+
size=${this.size}
|
|
108
|
+
style=${this._selectedStyle}
|
|
109
|
+
.value=${live(this.value)}
|
|
110
|
+
@change=${this._handlePickerChange}
|
|
111
|
+
>
|
|
112
|
+
${this._renderMenuItems()}
|
|
113
|
+
</sp-picker>
|
|
114
|
+
`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return html`
|
|
118
|
+
<div class="jx-combobox-group" id=${this._menuId}>
|
|
119
|
+
<sp-textfield
|
|
120
|
+
size=${this.size}
|
|
121
|
+
placeholder=${this.placeholder}
|
|
122
|
+
.value=${live(this.value || "")}
|
|
123
|
+
@input=${this._handleInput}
|
|
124
|
+
@click=${(/** @type {Event} */ e) => e.stopPropagation()}
|
|
125
|
+
></sp-textfield>
|
|
126
|
+
<sp-picker-button size=${this.size}></sp-picker-button>
|
|
127
|
+
<sp-overlay
|
|
128
|
+
trigger="${this._menuId}@click"
|
|
129
|
+
placement="bottom-start"
|
|
130
|
+
type="auto"
|
|
131
|
+
@sp-opened=${this._setPopoverWidth}
|
|
132
|
+
>
|
|
133
|
+
<sp-popover class="jx-combobox-popover">
|
|
134
|
+
<sp-menu size=${this.size} @change=${this._handleMenuChange}>
|
|
135
|
+
${this._renderMenuItems()}
|
|
136
|
+
</sp-menu>
|
|
137
|
+
</sp-popover>
|
|
138
|
+
</sp-overlay>
|
|
139
|
+
</div>
|
|
140
|
+
`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectrum.js — Explicit Spectrum Web Component registration
|
|
3
|
+
*
|
|
4
|
+
* Bun's bundler tree-shakes bare side-effect imports (`import "..."`) because the Spectrum
|
|
5
|
+
* `sp-*.js` entry files export nothing — they only call `customElements.define()` as a side effect.
|
|
6
|
+
* To prevent the bundler from dropping them, we import the class constructors and collect them into
|
|
7
|
+
* an exported array that the main module references.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Theme } from "@spectrum-web-components/theme/src/Theme.js";
|
|
11
|
+
import themeSpectrumCSS from "@spectrum-web-components/theme/src/theme.css.js";
|
|
12
|
+
import themeDarkCSS from "@spectrum-web-components/theme/src/theme-dark.css.js";
|
|
13
|
+
import scaleMediumCSS from "@spectrum-web-components/theme/src/scale-medium.css.js";
|
|
14
|
+
import { Tabs } from "@spectrum-web-components/tabs/src/Tabs.js";
|
|
15
|
+
import { Tab } from "@spectrum-web-components/tabs/src/Tab.js";
|
|
16
|
+
import { TabPanel } from "@spectrum-web-components/tabs/src/TabPanel.js";
|
|
17
|
+
import { ActionButton } from "@spectrum-web-components/action-button/src/ActionButton.js";
|
|
18
|
+
import { ActionGroup } from "@spectrum-web-components/action-group/src/ActionGroup.js";
|
|
19
|
+
import { Search } from "@spectrum-web-components/search/src/Search.js";
|
|
20
|
+
import { Popover } from "@spectrum-web-components/popover/src/Popover.js";
|
|
21
|
+
import { Menu } from "@spectrum-web-components/menu/src/Menu.js";
|
|
22
|
+
import { MenuItem } from "@spectrum-web-components/menu/src/MenuItem.js";
|
|
23
|
+
import { MenuDivider } from "@spectrum-web-components/menu/src/MenuDivider.js";
|
|
24
|
+
import { MenuGroup } from "@spectrum-web-components/menu/src/MenuGroup.js";
|
|
25
|
+
import { Textfield } from "@spectrum-web-components/textfield/src/Textfield.js";
|
|
26
|
+
import { Swatch } from "@spectrum-web-components/swatch/src/Swatch.js";
|
|
27
|
+
import { SwatchGroup } from "@spectrum-web-components/swatch/src/SwatchGroup.js";
|
|
28
|
+
import { ColorArea } from "@spectrum-web-components/color-area/src/ColorArea.js";
|
|
29
|
+
import { ColorSlider } from "@spectrum-web-components/color-slider/src/ColorSlider.js";
|
|
30
|
+
import { ColorHandle } from "@spectrum-web-components/color-handle/src/ColorHandle.js";
|
|
31
|
+
import { NumberField } from "@spectrum-web-components/number-field/src/NumberField.js";
|
|
32
|
+
import { Picker } from "@spectrum-web-components/picker/src/Picker.js";
|
|
33
|
+
import { Combobox } from "@spectrum-web-components/combobox/src/Combobox.js";
|
|
34
|
+
import { FieldLabel } from "@spectrum-web-components/field-label/src/FieldLabel.js";
|
|
35
|
+
import { Checkbox } from "@spectrum-web-components/checkbox/src/Checkbox.js";
|
|
36
|
+
import { Switch as SpSwitch } from "@spectrum-web-components/switch/src/Switch.js";
|
|
37
|
+
import { Divider } from "@spectrum-web-components/divider/src/Divider.js";
|
|
38
|
+
import { Tooltip } from "@spectrum-web-components/tooltip/src/Tooltip.js";
|
|
39
|
+
import { Overlay } from "@spectrum-web-components/overlay/src/Overlay.js";
|
|
40
|
+
import { PickerButton } from "@spectrum-web-components/picker-button/src/PickerButton.js";
|
|
41
|
+
import { Accordion } from "@spectrum-web-components/accordion/src/Accordion.js";
|
|
42
|
+
import { AccordionItem } from "@spectrum-web-components/accordion/src/AccordionItem.js";
|
|
43
|
+
import { ActionBar } from "@spectrum-web-components/action-bar/src/ActionBar.js";
|
|
44
|
+
|
|
45
|
+
// Icons
|
|
46
|
+
import { IconFolder } from "@spectrum-web-components/icons-workflow/src/elements/IconFolder.js";
|
|
47
|
+
import { IconFolderOpen } from "@spectrum-web-components/icons-workflow/src/elements/IconFolderOpen.js";
|
|
48
|
+
import { IconDocument } from "@spectrum-web-components/icons-workflow/src/elements/IconDocument.js";
|
|
49
|
+
import { IconFileCode } from "@spectrum-web-components/icons-workflow/src/elements/IconFileCode.js";
|
|
50
|
+
import { IconFileTxt } from "@spectrum-web-components/icons-workflow/src/elements/IconFileTxt.js";
|
|
51
|
+
import { IconImage } from "@spectrum-web-components/icons-workflow/src/elements/IconImage.js";
|
|
52
|
+
import { IconRefresh } from "@spectrum-web-components/icons-workflow/src/elements/IconRefresh.js";
|
|
53
|
+
import { IconAdd } from "@spectrum-web-components/icons-workflow/src/elements/IconAdd.js";
|
|
54
|
+
import { IconLayers } from "@spectrum-web-components/icons-workflow/src/elements/IconLayers.js";
|
|
55
|
+
import { IconViewGrid } from "@spectrum-web-components/icons-workflow/src/elements/IconViewGrid.js";
|
|
56
|
+
import { IconBrackets } from "@spectrum-web-components/icons-workflow/src/elements/IconBrackets.js";
|
|
57
|
+
import { IconData } from "@spectrum-web-components/icons-workflow/src/elements/IconData.js";
|
|
58
|
+
import { IconChevronDown } from "@spectrum-web-components/icons-workflow/src/elements/IconChevronDown.js";
|
|
59
|
+
import { IconDelete } from "@spectrum-web-components/icons-workflow/src/elements/IconDelete.js";
|
|
60
|
+
import { IconClose } from "@spectrum-web-components/icons-workflow/src/elements/IconClose.js";
|
|
61
|
+
import { IconChevronRight } from "@spectrum-web-components/icons-workflow/src/elements/IconChevronRight.js";
|
|
62
|
+
import { IconEdit } from "@spectrum-web-components/icons-workflow/src/elements/IconEdit.js";
|
|
63
|
+
import { IconSaveFloppy } from "@spectrum-web-components/icons-workflow/src/elements/IconSaveFloppy.js";
|
|
64
|
+
import { IconUndo } from "@spectrum-web-components/icons-workflow/src/elements/IconUndo.js";
|
|
65
|
+
import { IconRedo } from "@spectrum-web-components/icons-workflow/src/elements/IconRedo.js";
|
|
66
|
+
import { IconDuplicate } from "@spectrum-web-components/icons-workflow/src/elements/IconDuplicate.js";
|
|
67
|
+
import { IconCopy } from "@spectrum-web-components/icons-workflow/src/elements/IconCopy.js";
|
|
68
|
+
import { IconExport } from "@spectrum-web-components/icons-workflow/src/elements/IconExport.js";
|
|
69
|
+
import { IconPreview } from "@spectrum-web-components/icons-workflow/src/elements/IconPreview.js";
|
|
70
|
+
import { IconCode } from "@spectrum-web-components/icons-workflow/src/elements/IconCode.js";
|
|
71
|
+
import { IconBrush } from "@spectrum-web-components/icons-workflow/src/elements/IconBrush.js";
|
|
72
|
+
import { IconBack } from "@spectrum-web-components/icons-workflow/src/elements/IconBack.js";
|
|
73
|
+
import { IconProperties } from "@spectrum-web-components/icons-workflow/src/elements/IconProperties.js";
|
|
74
|
+
import { IconEvent } from "@spectrum-web-components/icons-workflow/src/elements/IconEvent.js";
|
|
75
|
+
|
|
76
|
+
// Layout / alignment icons
|
|
77
|
+
import { IconArrowRight } from "@spectrum-web-components/icons-workflow/src/elements/IconArrowRight.js";
|
|
78
|
+
import { IconArrowLeft } from "@spectrum-web-components/icons-workflow/src/elements/IconArrowLeft.js";
|
|
79
|
+
import { IconArrowDown } from "@spectrum-web-components/icons-workflow/src/elements/IconArrowDown.js";
|
|
80
|
+
import { IconArrowUp } from "@spectrum-web-components/icons-workflow/src/elements/IconArrowUp.js";
|
|
81
|
+
import { IconTextAlignLeft } from "@spectrum-web-components/icons-workflow/src/elements/IconTextAlignLeft.js";
|
|
82
|
+
import { IconTextAlignCenter } from "@spectrum-web-components/icons-workflow/src/elements/IconTextAlignCenter.js";
|
|
83
|
+
import { IconTextAlignRight } from "@spectrum-web-components/icons-workflow/src/elements/IconTextAlignRight.js";
|
|
84
|
+
import { IconTextAlignJustify } from "@spectrum-web-components/icons-workflow/src/elements/IconTextAlignJustify.js";
|
|
85
|
+
import { IconAlignTop } from "@spectrum-web-components/icons-workflow/src/elements/IconAlignTop.js";
|
|
86
|
+
import { IconAlignBottom } from "@spectrum-web-components/icons-workflow/src/elements/IconAlignBottom.js";
|
|
87
|
+
import { IconAlignMiddle } from "@spectrum-web-components/icons-workflow/src/elements/IconAlignMiddle.js";
|
|
88
|
+
import { IconAlignLeft } from "@spectrum-web-components/icons-workflow/src/elements/IconAlignLeft.js";
|
|
89
|
+
import { IconAlignRight } from "@spectrum-web-components/icons-workflow/src/elements/IconAlignRight.js";
|
|
90
|
+
import { IconAlignCenter } from "@spectrum-web-components/icons-workflow/src/elements/IconAlignCenter.js";
|
|
91
|
+
import { IconDistributeSpaceHoriz } from "@spectrum-web-components/icons-workflow/src/elements/IconDistributeSpaceHoriz.js";
|
|
92
|
+
import { IconDistributeSpaceVert } from "@spectrum-web-components/icons-workflow/src/elements/IconDistributeSpaceVert.js";
|
|
93
|
+
import { IconDistributeHorizontally } from "@spectrum-web-components/icons-workflow/src/elements/IconDistributeHorizontally.js";
|
|
94
|
+
import { IconDistributeVertically } from "@spectrum-web-components/icons-workflow/src/elements/IconDistributeVertically.js";
|
|
95
|
+
import { IconDistributeBottomEdge } from "@spectrum-web-components/icons-workflow/src/elements/IconDistributeBottomEdge.js";
|
|
96
|
+
import { IconDistributeTopEdge } from "@spectrum-web-components/icons-workflow/src/elements/IconDistributeTopEdge.js";
|
|
97
|
+
import { IconDistributeHorizontalCenter } from "@spectrum-web-components/icons-workflow/src/elements/IconDistributeHorizontalCenter.js";
|
|
98
|
+
import { IconTextBaselineShift } from "@spectrum-web-components/icons-workflow/src/elements/IconTextBaselineShift.js";
|
|
99
|
+
import { IconFlipVertical } from "@spectrum-web-components/icons-workflow/src/elements/IconFlipVertical.js";
|
|
100
|
+
import { IconRemove } from "@spectrum-web-components/icons-workflow/src/elements/IconRemove.js";
|
|
101
|
+
import { IconViewColumn } from "@spectrum-web-components/icons-workflow/src/elements/IconViewColumn.js";
|
|
102
|
+
import { IconBox } from "@spectrum-web-components/icons-workflow/src/elements/IconBox.js";
|
|
103
|
+
import { IconVisibility } from "@spectrum-web-components/icons-workflow/src/elements/IconVisibility.js";
|
|
104
|
+
import { IconVisibilityOff } from "@spectrum-web-components/icons-workflow/src/elements/IconVisibilityOff.js";
|
|
105
|
+
import { IconArtboard } from "@spectrum-web-components/icons-workflow/src/elements/IconArtboard.js";
|
|
106
|
+
|
|
107
|
+
// Inline formatting icons
|
|
108
|
+
import { IconTextBold } from "@spectrum-web-components/icons-workflow/src/elements/IconTextBold.js";
|
|
109
|
+
import { IconTextItalic } from "@spectrum-web-components/icons-workflow/src/elements/IconTextItalic.js";
|
|
110
|
+
import { IconTextUnderline } from "@spectrum-web-components/icons-workflow/src/elements/IconTextUnderline.js";
|
|
111
|
+
import { IconTextStrikethrough } from "@spectrum-web-components/icons-workflow/src/elements/IconTextStrikethrough.js";
|
|
112
|
+
import { IconTextSuperscript } from "@spectrum-web-components/icons-workflow/src/elements/IconTextSuperscript.js";
|
|
113
|
+
import { IconTextSubscript } from "@spectrum-web-components/icons-workflow/src/elements/IconTextSubscript.js";
|
|
114
|
+
import { IconLink } from "@spectrum-web-components/icons-workflow/src/elements/IconLink.js";
|
|
115
|
+
|
|
116
|
+
// Custom studio components
|
|
117
|
+
import { JxStyledCombobox } from "./jx-styled-combobox.js";
|
|
118
|
+
|
|
119
|
+
// UI icons (used internally by Spectrum components like accordion, picker, combobox)
|
|
120
|
+
import { IconChevron100 } from "@spectrum-web-components/icons-ui/src/elements/IconChevron100.js";
|
|
121
|
+
|
|
122
|
+
// Register all components. Using defineElement from Spectrum's base package
|
|
123
|
+
// ensures duplicate registration is handled gracefully.
|
|
124
|
+
import { defineElement } from "@spectrum-web-components/base/src/define-element.js";
|
|
125
|
+
|
|
126
|
+
const components = [
|
|
127
|
+
["sp-theme", Theme],
|
|
128
|
+
["sp-tabs", Tabs],
|
|
129
|
+
["sp-tab", Tab],
|
|
130
|
+
["sp-tab-panel", TabPanel],
|
|
131
|
+
["sp-action-button", ActionButton],
|
|
132
|
+
["sp-action-group", ActionGroup],
|
|
133
|
+
["sp-search", Search],
|
|
134
|
+
["sp-popover", Popover],
|
|
135
|
+
["sp-menu", Menu],
|
|
136
|
+
["sp-menu-item", MenuItem],
|
|
137
|
+
["sp-menu-divider", MenuDivider],
|
|
138
|
+
["sp-menu-group", MenuGroup],
|
|
139
|
+
["sp-textfield", Textfield],
|
|
140
|
+
["sp-swatch", Swatch],
|
|
141
|
+
["sp-swatch-group", SwatchGroup],
|
|
142
|
+
["sp-color-area", ColorArea],
|
|
143
|
+
["sp-color-slider", ColorSlider],
|
|
144
|
+
["sp-color-handle", ColorHandle],
|
|
145
|
+
["sp-number-field", NumberField],
|
|
146
|
+
["sp-picker", Picker],
|
|
147
|
+
["sp-combobox", Combobox],
|
|
148
|
+
["sp-field-label", FieldLabel],
|
|
149
|
+
["sp-checkbox", Checkbox],
|
|
150
|
+
["sp-switch", SpSwitch],
|
|
151
|
+
["sp-divider", Divider],
|
|
152
|
+
["sp-tooltip", Tooltip],
|
|
153
|
+
["sp-overlay", Overlay],
|
|
154
|
+
["sp-picker-button", PickerButton],
|
|
155
|
+
["sp-accordion", Accordion],
|
|
156
|
+
["sp-accordion-item", AccordionItem],
|
|
157
|
+
["sp-action-bar", ActionBar],
|
|
158
|
+
["sp-icon-folder", IconFolder],
|
|
159
|
+
["sp-icon-folder-open", IconFolderOpen],
|
|
160
|
+
["sp-icon-document", IconDocument],
|
|
161
|
+
["sp-icon-file-code", IconFileCode],
|
|
162
|
+
["sp-icon-file-txt", IconFileTxt],
|
|
163
|
+
["sp-icon-image", IconImage],
|
|
164
|
+
["sp-icon-refresh", IconRefresh],
|
|
165
|
+
["sp-icon-add", IconAdd],
|
|
166
|
+
["sp-icon-layers", IconLayers],
|
|
167
|
+
["sp-icon-view-grid", IconViewGrid],
|
|
168
|
+
["sp-icon-brackets", IconBrackets],
|
|
169
|
+
["sp-icon-data", IconData],
|
|
170
|
+
["sp-icon-chevron-down", IconChevronDown],
|
|
171
|
+
["sp-icon-chevron-right", IconChevronRight],
|
|
172
|
+
["sp-icon-delete", IconDelete],
|
|
173
|
+
["sp-icon-close", IconClose],
|
|
174
|
+
["sp-icon-edit", IconEdit],
|
|
175
|
+
["sp-icon-save-floppy", IconSaveFloppy],
|
|
176
|
+
["sp-icon-undo", IconUndo],
|
|
177
|
+
["sp-icon-redo", IconRedo],
|
|
178
|
+
["sp-icon-duplicate", IconDuplicate],
|
|
179
|
+
["sp-icon-copy", IconCopy],
|
|
180
|
+
["sp-icon-export", IconExport],
|
|
181
|
+
["sp-icon-preview", IconPreview],
|
|
182
|
+
["sp-icon-code", IconCode],
|
|
183
|
+
["sp-icon-brush", IconBrush],
|
|
184
|
+
["sp-icon-back", IconBack],
|
|
185
|
+
["sp-icon-properties", IconProperties],
|
|
186
|
+
["sp-icon-event", IconEvent],
|
|
187
|
+
["sp-icon-arrow-right", IconArrowRight],
|
|
188
|
+
["sp-icon-arrow-left", IconArrowLeft],
|
|
189
|
+
["sp-icon-arrow-down", IconArrowDown],
|
|
190
|
+
["sp-icon-arrow-up", IconArrowUp],
|
|
191
|
+
["sp-icon-text-align-left", IconTextAlignLeft],
|
|
192
|
+
["sp-icon-text-align-center", IconTextAlignCenter],
|
|
193
|
+
["sp-icon-text-align-right", IconTextAlignRight],
|
|
194
|
+
["sp-icon-text-align-justify", IconTextAlignJustify],
|
|
195
|
+
["sp-icon-align-top", IconAlignTop],
|
|
196
|
+
["sp-icon-align-bottom", IconAlignBottom],
|
|
197
|
+
["sp-icon-align-middle", IconAlignMiddle],
|
|
198
|
+
["sp-icon-align-left", IconAlignLeft],
|
|
199
|
+
["sp-icon-align-right", IconAlignRight],
|
|
200
|
+
["sp-icon-align-center", IconAlignCenter],
|
|
201
|
+
["sp-icon-distribute-space-horiz", IconDistributeSpaceHoriz],
|
|
202
|
+
["sp-icon-distribute-space-vert", IconDistributeSpaceVert],
|
|
203
|
+
["sp-icon-distribute-horizontally", IconDistributeHorizontally],
|
|
204
|
+
["sp-icon-distribute-vertically", IconDistributeVertically],
|
|
205
|
+
["sp-icon-distribute-bottom-edge", IconDistributeBottomEdge],
|
|
206
|
+
["sp-icon-distribute-top-edge", IconDistributeTopEdge],
|
|
207
|
+
["sp-icon-distribute-horizontal-center", IconDistributeHorizontalCenter],
|
|
208
|
+
["sp-icon-text-baseline-shift", IconTextBaselineShift],
|
|
209
|
+
["sp-icon-flip-vertical", IconFlipVertical],
|
|
210
|
+
["sp-icon-remove", IconRemove],
|
|
211
|
+
["sp-icon-view-column", IconViewColumn],
|
|
212
|
+
["sp-icon-box", IconBox],
|
|
213
|
+
["sp-icon-visibility", IconVisibility],
|
|
214
|
+
["sp-icon-visibility-off", IconVisibilityOff],
|
|
215
|
+
["sp-icon-artboard", IconArtboard],
|
|
216
|
+
["sp-icon-text-bold", IconTextBold],
|
|
217
|
+
["sp-icon-text-italic", IconTextItalic],
|
|
218
|
+
["sp-icon-text-underline", IconTextUnderline],
|
|
219
|
+
["sp-icon-text-strikethrough", IconTextStrikethrough],
|
|
220
|
+
["sp-icon-text-superscript", IconTextSuperscript],
|
|
221
|
+
["sp-icon-text-subscript", IconTextSubscript],
|
|
222
|
+
["sp-icon-link", IconLink],
|
|
223
|
+
// UI icons (internal component chrome)
|
|
224
|
+
["sp-icon-chevron100", IconChevron100],
|
|
225
|
+
// Custom studio components
|
|
226
|
+
["jx-styled-combobox", JxStyledCombobox],
|
|
227
|
+
];
|
|
228
|
+
|
|
229
|
+
for (const [tag, ctor] of /** @type {[string, CustomElementConstructor][]} */ (components)) {
|
|
230
|
+
if (!customElements.get(tag)) defineElement(tag, /** @type {any} */ (ctor));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Register theme fragments (these are also side-effect-only in the original modules)
|
|
234
|
+
Theme.registerThemeFragment("spectrum", "system", themeSpectrumCSS);
|
|
235
|
+
Theme.registerThemeFragment("dark", "color", themeDarkCSS);
|
|
236
|
+
Theme.registerThemeFragment("medium", "scale", scaleMediumCSS);
|
|
237
|
+
|
|
238
|
+
export { components };
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio-utils.js — Pure utility functions extracted from studio.js
|
|
3
|
+
*
|
|
4
|
+
* These are all side-effect-free functions used by style/properties/events panels.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* CamelCase → kebab-case for inline style attributes
|
|
9
|
+
*
|
|
10
|
+
* @param {string} str
|
|
11
|
+
* @returns {string}
|
|
12
|
+
*/
|
|
13
|
+
export function camelToKebab(str) {
|
|
14
|
+
return str.replace(/[A-Z]/g, (/** @type {string} */ c) => "-" + c.toLowerCase());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Convert camelCase property name to "Title Case" label (e.g. "backgroundColor" → "Background
|
|
19
|
+
* Color")
|
|
20
|
+
*
|
|
21
|
+
* @param {string} prop
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
export function camelToLabel(prop) {
|
|
25
|
+
return prop
|
|
26
|
+
.replace(/([A-Z])/g, " $1")
|
|
27
|
+
.replace(/^./, (/** @type {string} */ c) => c.toUpperCase());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Convert a kebab-case CSS value to Title Case for picker display (e.g. "border-box" → "Border
|
|
32
|
+
* Box")
|
|
33
|
+
*
|
|
34
|
+
* @param {string} val
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
export function kebabToLabel(val) {
|
|
38
|
+
return val.replace(
|
|
39
|
+
/(^|-)(\w)/g,
|
|
40
|
+
(/** @type {string} */ _, /** @type {string} */ sep, /** @type {string} */ c) =>
|
|
41
|
+
(sep ? " " : "") + c.toUpperCase(),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get display label from metadata entry or prop name
|
|
47
|
+
*
|
|
48
|
+
* @param {any} entry
|
|
49
|
+
* @param {string} prop
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
export function propLabel(entry, prop) {
|
|
53
|
+
return entry?.$label || camelToLabel(prop);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Label for HTML attributes — handles kebab-case (aria-label → "Aria Label")
|
|
58
|
+
*
|
|
59
|
+
* @param {any} entry
|
|
60
|
+
* @param {string} attr
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
export function attrLabel(entry, attr) {
|
|
64
|
+
if (entry?.$label) return entry.$label;
|
|
65
|
+
if (attr.includes("-"))
|
|
66
|
+
return attr.replace(
|
|
67
|
+
/(^|-)(\w)/g,
|
|
68
|
+
(/** @type {string} */ _, /** @type {string} */ sep, /** @type {string} */ c) =>
|
|
69
|
+
(sep ? " " : "") + c.toUpperCase(),
|
|
70
|
+
);
|
|
71
|
+
return camelToLabel(attr);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Abbreviate a CSS value for button-group display
|
|
76
|
+
*
|
|
77
|
+
* @param {string} val
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
export function abbreviateValue(val) {
|
|
81
|
+
/** @type {Record<string, string>} */
|
|
82
|
+
const map = {
|
|
83
|
+
inline: "inl",
|
|
84
|
+
"inline-block": "i-blk",
|
|
85
|
+
"inline-flex": "i-flx",
|
|
86
|
+
"inline-grid": "i-grd",
|
|
87
|
+
contents: "cnt",
|
|
88
|
+
"flow-root": "flow",
|
|
89
|
+
nowrap: "no-wr",
|
|
90
|
+
"wrap-reverse": "wr-rev",
|
|
91
|
+
"flex-start": "start",
|
|
92
|
+
"flex-end": "end",
|
|
93
|
+
"space-between": "betw",
|
|
94
|
+
"space-around": "arnd",
|
|
95
|
+
"space-evenly": "even",
|
|
96
|
+
stretch: "str",
|
|
97
|
+
baseline: "base",
|
|
98
|
+
normal: "norm",
|
|
99
|
+
"row-reverse": "row-r",
|
|
100
|
+
"column-reverse": "col-r",
|
|
101
|
+
column: "col",
|
|
102
|
+
};
|
|
103
|
+
return map[val] || val;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Determine input widget type from a css-meta entry
|
|
108
|
+
*
|
|
109
|
+
* @param {any} entry
|
|
110
|
+
* @returns {string}
|
|
111
|
+
*/
|
|
112
|
+
export function inferInputType(entry) {
|
|
113
|
+
if (entry.$shorthand === true) return "shorthand";
|
|
114
|
+
if (entry.$input === "button-group") return "button-group";
|
|
115
|
+
if (entry.format === "color") return "color";
|
|
116
|
+
if (entry.$units !== undefined) return "number-unit";
|
|
117
|
+
if (entry.type === "number") return "number";
|
|
118
|
+
if (Array.isArray(entry.enum)) return "select";
|
|
119
|
+
if (Array.isArray(entry.examples) || Array.isArray(entry.presets)) return "combobox";
|
|
120
|
+
return "text";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Convert a human-readable name to a CSS variable name. E.g. "Geometric Humanist" →
|
|
125
|
+
* "--font-geometric-humanist"
|
|
126
|
+
*
|
|
127
|
+
* @param {string} name
|
|
128
|
+
* @param {string} prefix - E.g. "--font-"
|
|
129
|
+
* @returns {string}
|
|
130
|
+
*/
|
|
131
|
+
export function friendlyNameToVar(name, prefix) {
|
|
132
|
+
const slug = name
|
|
133
|
+
.trim()
|
|
134
|
+
.toLowerCase()
|
|
135
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
136
|
+
.replace(/\s+/g, "-")
|
|
137
|
+
.replace(/-+/g, "-")
|
|
138
|
+
.replace(/^-|-$/g, "");
|
|
139
|
+
if (!slug) return "";
|
|
140
|
+
return `${prefix}${slug}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Convert a CSS variable name back to a display name. E.g. "--font-geometric-humanist" with prefix
|
|
145
|
+
* "--font-" → "Geometric Humanist"
|
|
146
|
+
*
|
|
147
|
+
* @param {string} varName
|
|
148
|
+
* @param {string} prefix
|
|
149
|
+
* @returns {string}
|
|
150
|
+
*/
|
|
151
|
+
export function varDisplayName(varName, prefix) {
|
|
152
|
+
return (
|
|
153
|
+
varName
|
|
154
|
+
.replace(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), "")
|
|
155
|
+
.replace(/^--/, "")
|
|
156
|
+
.replace(/-/g, " ")
|
|
157
|
+
.replace(/\b\w/g, (/** @type {any} */ c) => c.toUpperCase()) || varName
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Parse a CEM type.text string into a structured descriptor.
|
|
163
|
+
*
|
|
164
|
+
* @param {string | undefined | null} typeText
|
|
165
|
+
* @returns {{ kind: "combobox"; options: string[] }
|
|
166
|
+
* | { kind: "boolean" }
|
|
167
|
+
* | { kind: "number" }
|
|
168
|
+
* | { kind: "text" }}
|
|
169
|
+
*/
|
|
170
|
+
export function parseCemType(typeText) {
|
|
171
|
+
if (!typeText) return { kind: "text" };
|
|
172
|
+
const t = typeText
|
|
173
|
+
.trim()
|
|
174
|
+
.replace(/\s*\|\s*undefined\b/g, "")
|
|
175
|
+
.trim();
|
|
176
|
+
if (t === "boolean") return { kind: "boolean" };
|
|
177
|
+
if (t === "number") return { kind: "number" };
|
|
178
|
+
// Detect enum: "'a' | 'b' | 'c'" — pipe-separated quoted literals
|
|
179
|
+
const enumMatch = t.match(/^'[^']*'(\s*\|\s*'[^']*')+$/);
|
|
180
|
+
if (enumMatch) {
|
|
181
|
+
const options = [...t.matchAll(/'([^']*)'/g)].map((m) => m[1]);
|
|
182
|
+
return { kind: "combobox", options };
|
|
183
|
+
}
|
|
184
|
+
return { kind: "text" };
|
|
185
|
+
}
|