@poveste/controls 0.5.3 → 0.6.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.
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `JSON.stringify` for values that were never promised to be JSON.
|
|
3
|
+
*
|
|
4
|
+
* State reaches this control straight out of a story's own scope, so it holds
|
|
5
|
+
* whatever a `<script setup>` holds: a function, a DOM node, an object that
|
|
6
|
+
* refers back to itself. `JSON.stringify` throws on the cycle, and it throws
|
|
7
|
+
* from inside a lifecycle hook — which takes the rest of that flush with it, so
|
|
8
|
+
* the panel and the toolbar beside it never mount. A marker in the editor is
|
|
9
|
+
* the smaller loss, and it is what the story events pane already does.
|
|
10
|
+
*
|
|
11
|
+
* Nodes and windows are named rather than walked. Both are cyclic, so the guard
|
|
12
|
+
* below would terminate on them, but only after walking a few thousand
|
|
13
|
+
* properties of `window` on every keystroke.
|
|
14
|
+
*/
|
|
15
|
+
export declare function stringifyState(value: unknown, space?: number): string;
|
package/dist/index.es.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.bind-col-size{grid-template-columns:repeat(auto-fill, minmax(var(--poveste-col-size), 1fr))}.__poveste-json-code[data-v-
|
|
1
|
+
.bind-col-size{grid-template-columns:repeat(auto-fill, minmax(var(--poveste-col-size), 1fr))}.__poveste-json-code[data-v-e4c89a1b] .cm-editor{min-width:280px;height:100%}.v-popper{line-height:0}.v-popper--theme-dropdown .v-popper__inner:where(.ptw-dark,.ptw-dark *){border-color:var(--color-gray-850,#1f1f21);background-color:var(--color-gray-700,var(--color-zinc-700));color:var(--color-gray-100,var(--color-zinc-100))}.v-popper--theme-dropdown .v-popper__arrow-inner:where(.ptw-dark,.ptw-dark *){border-color:var(--color-gray-700,var(--color-zinc-700))}.v-popper--theme-dropdown .v-popper__arrow-outer:where(.ptw-dark,.ptw-dark *){border-color:var(--color-gray-850,#1f1f21)}.v-popper--theme-dropdown.v-popper__popper--show-from .v-popper__wrapper{transform:scale(.75)}.v-popper--theme-dropdown.v-popper__popper--show-to .v-popper__wrapper{transition:transform .15s cubic-bezier(0,1,.5,1);transform:none}.v-popper__popper:focus-visible{outline:none}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid}}}.range-input::-webkit-slider-thumb{height:calc(var(--spacing,.25rem) * 3);width:calc(var(--spacing,.25rem) * 3);appearance:none;border-style:var(--tw-border-style);--tw-border-style:solid;border:1px solid #00000040;border-radius:2147483647px}@supports (color:color-mix(in lab, red, red)){.range-input::-webkit-slider-thumb{border-color:color-mix(in oklab, var(--color-black,#000) 25%, transparent)}}.range-input::-webkit-slider-thumb{background-color:var(--color-white,#fff)}.range-input::-webkit-slider-thumb:where(.ptw-dark,.ptw-dark *){border-color:#ffffff40}@supports (color:color-mix(in lab, red, red)){.range-input::-webkit-slider-thumb:where(.ptw-dark,.ptw-dark *){border-color:color-mix(in oklab, var(--color-white,#fff) 25%, transparent)}}.range-input::-webkit-slider-thumb:where(.ptw-dark,.ptw-dark *){background-color:var(--color-gray-700,var(--color-zinc-700))}.range-input:hover::-webkit-slider-thumb{border-color:var(--color-primary-500,var(--color-emerald-500))!important;background-color:var(--color-primary-500,var(--color-emerald-500))!important}.range-input::-moz-range-thumb{height:calc(var(--spacing,.25rem) * 3);width:calc(var(--spacing,.25rem) * 3);appearance:none;border-style:var(--tw-border-style);--tw-border-style:solid;border:1px solid #00000040;border-radius:2147483647px}@supports (color:color-mix(in lab, red, red)){.range-input::-moz-range-thumb{border-color:color-mix(in oklab, var(--color-black,#000) 25%, transparent)}}.range-input::-moz-range-thumb{background-color:var(--color-white,#fff)}.range-input::-moz-range-thumb:where(.ptw-dark,.ptw-dark *){border-color:#ffffff40}@supports (color:color-mix(in lab, red, red)){.range-input::-moz-range-thumb:where(.ptw-dark,.ptw-dark *){border-color:color-mix(in oklab, var(--color-white,#fff) 25%, transparent)}}.range-input::-moz-range-thumb:where(.ptw-dark,.ptw-dark *){background-color:var(--color-gray-700,var(--color-zinc-700))}.range-input:hover::-moz-range-thumb{border-color:var(--color-primary-500,var(--color-emerald-500))!important;background-color:var(--color-primary-500,var(--color-emerald-500))!important}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}
|
|
2
2
|
/*$vite$:1*/
|
package/dist/index.es.js
CHANGED
|
@@ -12836,8 +12836,22 @@ var mh = [
|
|
|
12836
12836
|
]), Nh = h(!1);
|
|
12837
12837
|
window.__pvt_controls_dark || (window.__pvt_controls_dark = []), window.__pvt_controls_dark.push(Nh), window.__pvt_controls_dark_ready?.();
|
|
12838
12838
|
//#endregion
|
|
12839
|
+
//#region src/components/json/serialize.ts
|
|
12840
|
+
function Ph(e, t) {
|
|
12841
|
+
let n = [];
|
|
12842
|
+
return JSON.stringify(e, function(e, t) {
|
|
12843
|
+
if (typeof t == "function") return "[Function]";
|
|
12844
|
+
if (typeof t == "bigint") return t.toString();
|
|
12845
|
+
if (typeof t != "object" || !t) return t;
|
|
12846
|
+
if (typeof Node < "u" && t instanceof Node) return "[Node]";
|
|
12847
|
+
if (typeof Window < "u" && t instanceof Window) return "[Window]";
|
|
12848
|
+
for (; n.length > 0 && n[n.length - 1] !== this;) n.pop();
|
|
12849
|
+
return n.includes(t) ? "[Circular]" : (n.push(t), t);
|
|
12850
|
+
}, t);
|
|
12851
|
+
}
|
|
12852
|
+
//#endregion
|
|
12839
12853
|
//#region src/components/json/HstJson.vue
|
|
12840
|
-
var
|
|
12854
|
+
var Fh = /*#__PURE__*/ ((e, t) => {
|
|
12841
12855
|
let n = e.__vccOpts || e;
|
|
12842
12856
|
for (let [e, r] of t) n[e] = r;
|
|
12843
12857
|
return n;
|
|
@@ -12850,10 +12864,10 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
12850
12864
|
},
|
|
12851
12865
|
emits: { "update:modelValue": (e) => !0 },
|
|
12852
12866
|
setup(e, { emit: t }) {
|
|
12853
|
-
let i = e, o = t, s, c = h(""),
|
|
12867
|
+
let i = e, o = t, s, c, p = h(""), g = h(!1), v = h(), y = {
|
|
12854
12868
|
light: [W.baseTheme({}), Od(jd)],
|
|
12855
12869
|
dark: [jh, Od(Mh)]
|
|
12856
|
-
},
|
|
12870
|
+
}, x = new Rt(), te = [
|
|
12857
12871
|
cl(),
|
|
12858
12872
|
_c(),
|
|
12859
12873
|
uc(),
|
|
@@ -12867,50 +12881,52 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
12867
12881
|
...th
|
|
12868
12882
|
]),
|
|
12869
12883
|
W.updateListener.of((e) => {
|
|
12870
|
-
|
|
12884
|
+
p.value = e.view.state.doc.toString();
|
|
12871
12885
|
}),
|
|
12872
|
-
|
|
12886
|
+
x.of(y.light)
|
|
12873
12887
|
];
|
|
12874
12888
|
return f(() => {
|
|
12875
|
-
s = new W({
|
|
12876
|
-
doc:
|
|
12877
|
-
extensions:
|
|
12878
|
-
parent:
|
|
12889
|
+
c = Ph(i.modelValue, 2), s = new W({
|
|
12890
|
+
doc: c,
|
|
12891
|
+
extensions: te,
|
|
12892
|
+
parent: v.value
|
|
12879
12893
|
}), ee(() => {
|
|
12880
|
-
s.dispatch({ effects: [
|
|
12894
|
+
s.dispatch({ effects: [x.reconfigure(y[Nh.value ? "dark" : "light"])] });
|
|
12881
12895
|
});
|
|
12882
12896
|
}), S(() => i.modelValue, () => {
|
|
12883
12897
|
let e;
|
|
12884
12898
|
try {
|
|
12885
|
-
e =
|
|
12899
|
+
e = Ph(JSON.parse(p.value)) === Ph(i.modelValue);
|
|
12886
12900
|
} catch {
|
|
12887
12901
|
e = !1;
|
|
12888
12902
|
}
|
|
12889
|
-
e || s.dispatch({ changes: [{
|
|
12903
|
+
e || (c = Ph(i.modelValue, 2), s.dispatch({ changes: [{
|
|
12890
12904
|
from: 0,
|
|
12891
12905
|
to: s.state.doc.length,
|
|
12892
|
-
insert:
|
|
12893
|
-
}] });
|
|
12894
|
-
}, { deep: !0 }), S(() =>
|
|
12895
|
-
p.value
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12906
|
+
insert: c
|
|
12907
|
+
}] }));
|
|
12908
|
+
}, { deep: !0 }), S(() => p.value, () => {
|
|
12909
|
+
if (p.value !== c) {
|
|
12910
|
+
g.value = !1;
|
|
12911
|
+
try {
|
|
12912
|
+
o("update:modelValue", JSON.parse(p.value));
|
|
12913
|
+
} catch {
|
|
12914
|
+
g.value = !0;
|
|
12915
|
+
}
|
|
12900
12916
|
}
|
|
12901
12917
|
}), (t, i) => (m(), n(ue, {
|
|
12902
12918
|
title: e.title,
|
|
12903
12919
|
class: u(["poveste-json cursor-text", t.$attrs.class]),
|
|
12904
12920
|
style: d(t.$attrs.style)
|
|
12905
12921
|
}, {
|
|
12906
|
-
actions: C(() => [
|
|
12922
|
+
actions: C(() => [g.value ? w((m(), n(b(ie), {
|
|
12907
12923
|
key: 0,
|
|
12908
12924
|
icon: "carbon:warning-alt",
|
|
12909
12925
|
class: "text-orange-500"
|
|
12910
12926
|
}, null, 512)), [[b(T), "JSON error"]]) : r("", !0), _(t.$slots, "actions", {}, void 0, !0)]),
|
|
12911
12927
|
default: C(() => [a("div", l({
|
|
12912
12928
|
ref_key: "editorElement",
|
|
12913
|
-
ref:
|
|
12929
|
+
ref: v,
|
|
12914
12930
|
class: "__poveste-json-code w-full border border-solid border-black/25 dark:border-white/25 focus-within:border-primary-500 dark:focus-within:border-primary-500 rounded-sm box-border overflow-auto resize-y min-h-32 h-48 relative"
|
|
12915
12931
|
}, {
|
|
12916
12932
|
...t.$attrs,
|
|
@@ -12924,7 +12940,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
12924
12940
|
"style"
|
|
12925
12941
|
]));
|
|
12926
12942
|
}
|
|
12927
|
-
}), [["__scopeId", "data-v-
|
|
12943
|
+
}), [["__scopeId", "data-v-e4c89a1b"]]), Ih = /* @__PURE__ */ c({
|
|
12928
12944
|
name: "HstNumber",
|
|
12929
12945
|
inheritAttrs: !1,
|
|
12930
12946
|
props: {
|
|
@@ -12987,13 +13003,13 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
12987
13003
|
"style"
|
|
12988
13004
|
]));
|
|
12989
13005
|
}
|
|
12990
|
-
}),
|
|
13006
|
+
}), Lh = { class: "-my-1" }, Rh = [
|
|
12991
13007
|
"id",
|
|
12992
13008
|
"name",
|
|
12993
13009
|
"value",
|
|
12994
13010
|
"checked",
|
|
12995
13011
|
"onChange"
|
|
12996
|
-
],
|
|
13012
|
+
], zh = ["for", "onKeydown"], Bh = /* @__PURE__ */ c({
|
|
12997
13013
|
name: "HstRadio",
|
|
12998
13014
|
props: {
|
|
12999
13015
|
title: {},
|
|
@@ -13014,7 +13030,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13014
13030
|
style: d(t.$attrs.style)
|
|
13015
13031
|
}, {
|
|
13016
13032
|
actions: C(() => [_(t.$slots, "actions")]),
|
|
13017
|
-
default: C(() => [a("div",
|
|
13033
|
+
default: C(() => [a("div", Lh, [(m(!0), i(e, null, g(f.value, (t, n) => (m(), i(e, { key: n }, [a("input", {
|
|
13018
13034
|
id: `${n}-radio_${r.title}`,
|
|
13019
13035
|
type: "radio",
|
|
13020
13036
|
name: `${n}-radio_${r.title}`,
|
|
@@ -13022,7 +13038,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13022
13038
|
checked: n === r.modelValue,
|
|
13023
13039
|
class: "hidden!",
|
|
13024
13040
|
onChange: (e) => p(n)
|
|
13025
|
-
}, null, 40,
|
|
13041
|
+
}, null, 40, Rh), a("label", {
|
|
13026
13042
|
tabindex: "0",
|
|
13027
13043
|
for: `${n}-radio_${r.title}`,
|
|
13028
13044
|
class: "cursor-pointer flex items-center relative py-1 group",
|
|
@@ -13039,7 +13055,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13039
13055
|
{ "delay-150": r.modelValue === n },
|
|
13040
13056
|
r.modelValue === n ? "fill-primary-500" : "fill-transparent scale-0"
|
|
13041
13057
|
]])
|
|
13042
|
-
}, null, 2)], 2)), o(" " + y(t), 1)], 40,
|
|
13058
|
+
}, null, 2)], 2)), o(" " + y(t), 1)], 40, zh)], 64))), 128))])]),
|
|
13043
13059
|
_: 3
|
|
13044
13060
|
}, 8, [
|
|
13045
13061
|
"title",
|
|
@@ -13047,7 +13063,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13047
13063
|
"style"
|
|
13048
13064
|
]));
|
|
13049
13065
|
}
|
|
13050
|
-
}),
|
|
13066
|
+
}), Vh = { class: "cursor-pointer w-full outline-none px-2 h-[27px] -my-1 border border-solid border-black/25 dark:border-white/25 hover:border-primary-500 dark:hover:border-primary-500 rounded-sm flex gap-2 items-center leading-normal" }, Hh = { class: "flex-1 truncate" }, Uh = { class: "flex flex-col bg-gray-50 dark:bg-gray-700" }, Wh = ["onClick"], Gh = /* @__PURE__ */ c({
|
|
13051
13067
|
name: "CustomSelect",
|
|
13052
13068
|
props: {
|
|
13053
13069
|
modelValue: {},
|
|
@@ -13063,7 +13079,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13063
13079
|
"auto-size": "",
|
|
13064
13080
|
"auto-boundary-max-size": ""
|
|
13065
13081
|
}, {
|
|
13066
|
-
popper: C(({ hide: n }) => [a("div",
|
|
13082
|
+
popper: C(({ hide: n }) => [a("div", Uh, [(m(!0), i(e, null, g(f.value, ([e, r]) => (m(), i("div", l({ ref_for: !0 }, {
|
|
13067
13083
|
...t.$attrs,
|
|
13068
13084
|
class: null,
|
|
13069
13085
|
style: null
|
|
@@ -13071,15 +13087,15 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13071
13087
|
key: r,
|
|
13072
13088
|
class: ["px-2 py-1 cursor-pointer hover:bg-primary-100 dark:hover:bg-primary-700", { "bg-primary-200 dark:bg-primary-800": u.modelValue === e }],
|
|
13073
13089
|
onClick: (t) => h(e, n)
|
|
13074
|
-
}), y(r), 17,
|
|
13075
|
-
default: C(() => [a("div",
|
|
13090
|
+
}), y(r), 17, Wh))), 128))])]),
|
|
13091
|
+
default: C(() => [a("div", Vh, [a("div", Hh, [_(t.$slots, "default", { label: p.value }, () => [o(y(p.value), 1)])]), s(b(ie), {
|
|
13076
13092
|
icon: "carbon:chevron-sort",
|
|
13077
13093
|
class: "w-4 h-4 flex-none ml-auto"
|
|
13078
13094
|
})])]),
|
|
13079
13095
|
_: 3
|
|
13080
13096
|
}));
|
|
13081
13097
|
}
|
|
13082
|
-
}),
|
|
13098
|
+
}), Kh = /* @__PURE__ */ c({
|
|
13083
13099
|
name: "HstSelect",
|
|
13084
13100
|
props: {
|
|
13085
13101
|
title: {},
|
|
@@ -13095,7 +13111,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13095
13111
|
style: d(t.$attrs.style)
|
|
13096
13112
|
}, {
|
|
13097
13113
|
actions: C(() => [_(t.$slots, "actions")]),
|
|
13098
|
-
default: C(() => [s(
|
|
13114
|
+
default: C(() => [s(Gh, {
|
|
13099
13115
|
options: e.options,
|
|
13100
13116
|
"model-value": e.modelValue,
|
|
13101
13117
|
"onUpdate:modelValue": i[0] ||= (e) => r("update:modelValue", e)
|
|
@@ -13107,7 +13123,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13107
13123
|
"style"
|
|
13108
13124
|
]));
|
|
13109
13125
|
}
|
|
13110
|
-
}),
|
|
13126
|
+
}), qh = { class: "relative w-full flex items-center" }, Jh = /* @__PURE__ */ c({
|
|
13111
13127
|
name: "HstSlider",
|
|
13112
13128
|
inheritAttrs: !1,
|
|
13113
13129
|
props: {
|
|
@@ -13129,7 +13145,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13129
13145
|
title: e.title,
|
|
13130
13146
|
style: d(t.$attrs.style)
|
|
13131
13147
|
}, {
|
|
13132
|
-
default: C(() => [a("div",
|
|
13148
|
+
default: C(() => [a("div", qh, [
|
|
13133
13149
|
o[3] ||= a("div", { class: "absolute inset-0 flex items-center" }, [a("div", { class: "border border-black/25 dark:border-white/25 h-1 w-full rounded-full" })], -1),
|
|
13134
13150
|
w(a("input", l({
|
|
13135
13151
|
ref_key: "input",
|
|
@@ -13170,7 +13186,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13170
13186
|
"style"
|
|
13171
13187
|
]));
|
|
13172
13188
|
}
|
|
13173
|
-
}),
|
|
13189
|
+
}), Yh = ["value"], Xh = /* @__PURE__ */ c({
|
|
13174
13190
|
name: "HstText",
|
|
13175
13191
|
props: {
|
|
13176
13192
|
title: {},
|
|
@@ -13198,7 +13214,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13198
13214
|
value: e.modelValue,
|
|
13199
13215
|
class: "text-inherit bg-transparent w-full outline-none px-2 py-1 -my-1 border border-solid border-black/25 dark:border-white/25 focus:border-primary-500 dark:focus:border-primary-500 rounded-sm",
|
|
13200
13216
|
onInput: o[0] ||= (e) => r("update:modelValue", e.target.value)
|
|
13201
|
-
}), null, 16,
|
|
13217
|
+
}), null, 16, Yh)]),
|
|
13202
13218
|
_: 3
|
|
13203
13219
|
}, 8, [
|
|
13204
13220
|
"title",
|
|
@@ -13206,7 +13222,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13206
13222
|
"style"
|
|
13207
13223
|
]));
|
|
13208
13224
|
}
|
|
13209
|
-
}),
|
|
13225
|
+
}), Zh = ["value"], Qh = /* @__PURE__ */ c({
|
|
13210
13226
|
name: "HstTextarea",
|
|
13211
13227
|
inheritAttrs: !1,
|
|
13212
13228
|
props: {
|
|
@@ -13234,7 +13250,7 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13234
13250
|
value: e.modelValue,
|
|
13235
13251
|
class: "text-inherit bg-transparent w-full outline-none px-2 py-1 -my-1 border border-solid border-black/25 dark:border-white/25 focus:border-primary-500 dark:focus:border-primary-500 rounded-sm box-border resize-y min-h-[26px]",
|
|
13236
13252
|
onInput: o[0] ||= (e) => r("update:modelValue", e.target.value)
|
|
13237
|
-
}), null, 16,
|
|
13253
|
+
}), null, 16, Zh)]),
|
|
13238
13254
|
_: 3
|
|
13239
13255
|
}, 8, [
|
|
13240
13256
|
"title",
|
|
@@ -13242,23 +13258,23 @@ var Ph = /*#__PURE__*/ ((e, t) => {
|
|
|
13242
13258
|
"style"
|
|
13243
13259
|
]));
|
|
13244
13260
|
}
|
|
13245
|
-
}),
|
|
13246
|
-
HstButton:
|
|
13247
|
-
HstButtonGroup:
|
|
13248
|
-
HstCheckbox:
|
|
13249
|
-
HstCheckboxList:
|
|
13250
|
-
HstText:
|
|
13251
|
-
HstNumber:
|
|
13252
|
-
HstSlider:
|
|
13253
|
-
HstTextarea:
|
|
13254
|
-
HstSelect:
|
|
13255
|
-
HstRadio:
|
|
13256
|
-
HstJson:
|
|
13257
|
-
HstColorShades:
|
|
13258
|
-
HstTokenList:
|
|
13259
|
-
HstTokenGrid:
|
|
13260
|
-
HstCopyIcon:
|
|
13261
|
-
HstColorSelect:
|
|
13261
|
+
}), $h = oe, eg = fe, tg = ge, ng = ye, rg = Xh, ig = Ih, ag = Jh, og = Qh, sg = Kh, cg = Ae, lg = Ue, ug = Ie, dg = Ce, fg = Bh, pg = Fh, mg = Se, hg = {
|
|
13262
|
+
HstButton: $h,
|
|
13263
|
+
HstButtonGroup: eg,
|
|
13264
|
+
HstCheckbox: tg,
|
|
13265
|
+
HstCheckboxList: ng,
|
|
13266
|
+
HstText: rg,
|
|
13267
|
+
HstNumber: ig,
|
|
13268
|
+
HstSlider: ag,
|
|
13269
|
+
HstTextarea: og,
|
|
13270
|
+
HstSelect: sg,
|
|
13271
|
+
HstRadio: fg,
|
|
13272
|
+
HstJson: pg,
|
|
13273
|
+
HstColorShades: cg,
|
|
13274
|
+
HstTokenList: lg,
|
|
13275
|
+
HstTokenGrid: ug,
|
|
13276
|
+
HstCopyIcon: dg,
|
|
13277
|
+
HstColorSelect: mg
|
|
13262
13278
|
};
|
|
13263
13279
|
//#endregion
|
|
13264
|
-
export {
|
|
13280
|
+
export { $h as HstButton, eg as HstButtonGroup, tg as HstCheckbox, ng as HstCheckboxList, mg as HstColorSelect, cg as HstColorShades, dg as HstCopyIcon, pg as HstJson, ig as HstNumber, fg as HstRadio, sg as HstSelect, ag as HstSlider, rg as HstText, og as HstTextarea, ug as HstTokenGrid, lg as HstTokenList, hg as components };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poveste/controls",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"description": "Prebuilt controls components",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Sorin Gitlan"
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"@codemirror/state": "^6.4.1",
|
|
39
39
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
40
40
|
"@codemirror/view": "^6.35.0",
|
|
41
|
-
"@poveste/vendors": "^0.
|
|
42
|
-
"@poveste/shared": "^0.
|
|
41
|
+
"@poveste/vendors": "^0.6.0",
|
|
42
|
+
"@poveste/shared": "^0.6.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@tailwindcss/cli": "^4.3.3",
|
|
@@ -34,6 +34,7 @@ import { VTooltip as vTooltip } from 'floating-vue'
|
|
|
34
34
|
import { onMounted, ref, watch, watchEffect } from 'vue'
|
|
35
35
|
import { isDark } from '../../utils'
|
|
36
36
|
import HstWrapper from '../HstWrapper.vue'
|
|
37
|
+
import { stringifyState } from './serialize.js'
|
|
37
38
|
|
|
38
39
|
const props = defineProps<{
|
|
39
40
|
title?: string
|
|
@@ -45,6 +46,12 @@ const emit = defineEmits({
|
|
|
45
46
|
})
|
|
46
47
|
|
|
47
48
|
let editorView: EditorView
|
|
49
|
+
// The document this control last rendered from `modelValue`. A change producing
|
|
50
|
+
// exactly this is our own render, not an edit, and must not be written back:
|
|
51
|
+
// `stringifyState` substitutes markers for what JSON cannot carry, so echoing
|
|
52
|
+
// our own document replaces a cycle in the story's state with the string
|
|
53
|
+
// `[Circular]`, and a function with `[Function]`.
|
|
54
|
+
let renderedDoc: string | undefined
|
|
48
55
|
const internalValue = ref('')
|
|
49
56
|
const invalidValue = ref(false)
|
|
50
57
|
const editorElement = ref<HTMLInputElement>()
|
|
@@ -76,8 +83,10 @@ const extensions = [
|
|
|
76
83
|
]
|
|
77
84
|
|
|
78
85
|
onMounted(() => {
|
|
86
|
+
renderedDoc = stringifyState(props.modelValue, 2)
|
|
87
|
+
|
|
79
88
|
editorView = new EditorView({
|
|
80
|
-
doc:
|
|
89
|
+
doc: renderedDoc,
|
|
81
90
|
extensions,
|
|
82
91
|
parent: editorElement.value,
|
|
83
92
|
})
|
|
@@ -95,18 +104,25 @@ watch(() => props.modelValue, () => {
|
|
|
95
104
|
let sameDocument
|
|
96
105
|
|
|
97
106
|
try {
|
|
98
|
-
sameDocument = (
|
|
107
|
+
sameDocument = (stringifyState(JSON.parse(internalValue.value)) === stringifyState(props.modelValue))
|
|
99
108
|
}
|
|
100
109
|
catch (e) {
|
|
101
110
|
sameDocument = false
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
if (!sameDocument) {
|
|
105
|
-
|
|
114
|
+
renderedDoc = stringifyState(props.modelValue, 2)
|
|
115
|
+
editorView.dispatch({ changes: [{ from: 0, to: editorView.state.doc.length, insert: renderedDoc }] })
|
|
106
116
|
}
|
|
107
117
|
}, { deep: true })
|
|
108
118
|
|
|
109
119
|
watch(() => internalValue.value, () => {
|
|
120
|
+
// Our own render coming back round. The model already holds this, and parsing
|
|
121
|
+
// it would hand back the markers instead of the values they stand for.
|
|
122
|
+
if (internalValue.value === renderedDoc) {
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
110
126
|
invalidValue.value = false
|
|
111
127
|
try {
|
|
112
128
|
emit('update:modelValue', JSON.parse(internalValue.value))
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { EditorView } from '@codemirror/view'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
|
+
import { nextTick } from 'vue'
|
|
5
|
+
import HstJson from './HstJson.vue'
|
|
6
|
+
import { stringifyState } from './serialize.js'
|
|
7
|
+
|
|
8
|
+
describe('stringifyState', () => {
|
|
9
|
+
it('matches JSON.stringify on values JSON can express', () => {
|
|
10
|
+
const value = { a: 1, b: 'two', c: [1, 2], d: { e: null }, f: true }
|
|
11
|
+
expect(stringifyState(value)).toBe(JSON.stringify(value))
|
|
12
|
+
expect(stringifyState(value, 2)).toBe(JSON.stringify(value, null, 2))
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('names a cycle instead of throwing', () => {
|
|
16
|
+
// The ComplexParameter story in the vue3 example holds exactly this, and
|
|
17
|
+
// once the state sync started delivering the story's own scope it reached
|
|
18
|
+
// the editor. `JSON.stringify` throws here, from a lifecycle hook, taking
|
|
19
|
+
// the rest of the flush — the panel and the toolbar — down with it.
|
|
20
|
+
const parent: any = { name: 'hello' }
|
|
21
|
+
const child: any = { parent }
|
|
22
|
+
parent.child = child
|
|
23
|
+
|
|
24
|
+
expect(JSON.parse(stringifyState(parent))).toEqual({
|
|
25
|
+
name: 'hello',
|
|
26
|
+
child: { parent: '[Circular]' },
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('names a function, which JSON.stringify drops silently', () => {
|
|
31
|
+
expect(JSON.parse(stringifyState({ action: () => {} }))).toEqual({ action: '[Function]' })
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('does not mistake a shared reference for a cycle', () => {
|
|
35
|
+
// The guard tracks ancestors, not everything seen. The same object twice as
|
|
36
|
+
// a sibling serialises fine and must not be blanked out.
|
|
37
|
+
const shared = { a: 1 }
|
|
38
|
+
expect(JSON.parse(stringifyState({ x: shared, y: shared }))).toEqual({ x: { a: 1 }, y: { a: 1 } })
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('names a node rather than walking it', () => {
|
|
42
|
+
const el = document.createElement('div')
|
|
43
|
+
expect(JSON.parse(stringifyState({ el }))).toEqual({ el: '[Node]' })
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('carries a bigint across as its digits', () => {
|
|
47
|
+
expect(JSON.parse(stringifyState({ n: 10n }))).toEqual({ n: '10' })
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('hstJson round-trip', () => {
|
|
52
|
+
it('does not write its own markers back into the model', async () => {
|
|
53
|
+
// The markers stand for what JSON cannot carry. Parsing the rendered
|
|
54
|
+
// document and emitting it replaces the thing itself with its label — a
|
|
55
|
+
// cycle in the story's state becomes the string `[Circular]` — and it used
|
|
56
|
+
// to happen on mount, with nobody touching the editor.
|
|
57
|
+
const parent: any = { name: 'hello' }
|
|
58
|
+
parent.child = { parent }
|
|
59
|
+
|
|
60
|
+
const wrapper = mount(HstJson, { props: { modelValue: parent, title: 'Recursive' } })
|
|
61
|
+
await nextTick()
|
|
62
|
+
|
|
63
|
+
await wrapper.setProps({ modelValue: { ...parent, name: 'changed' } })
|
|
64
|
+
await nextTick()
|
|
65
|
+
await new Promise(resolve => setTimeout(resolve, 50))
|
|
66
|
+
|
|
67
|
+
expect(wrapper.emitted('update:modelValue')).toBeUndefined()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('still reports a document the reader changed', async () => {
|
|
71
|
+
// The guard is keyed on the exact text this control last rendered, so it
|
|
72
|
+
// must not swallow anything else. Without this, suppressing the echo would
|
|
73
|
+
// quietly turn the editor read-only and the test above would still pass.
|
|
74
|
+
const wrapper = mount(HstJson, { props: { modelValue: { a: 1 }, title: 'Plain' } })
|
|
75
|
+
await nextTick()
|
|
76
|
+
|
|
77
|
+
const view = EditorView.findFromDOM(wrapper.find('.cm-content').element as HTMLElement)!
|
|
78
|
+
expect(view).toBeTruthy()
|
|
79
|
+
|
|
80
|
+
view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: '{ "a": 2 }' } })
|
|
81
|
+
await nextTick()
|
|
82
|
+
|
|
83
|
+
expect(wrapper.emitted('update:modelValue')?.at(-1)).toEqual([{ a: 2 }])
|
|
84
|
+
})
|
|
85
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `JSON.stringify` for values that were never promised to be JSON.
|
|
3
|
+
*
|
|
4
|
+
* State reaches this control straight out of a story's own scope, so it holds
|
|
5
|
+
* whatever a `<script setup>` holds: a function, a DOM node, an object that
|
|
6
|
+
* refers back to itself. `JSON.stringify` throws on the cycle, and it throws
|
|
7
|
+
* from inside a lifecycle hook — which takes the rest of that flush with it, so
|
|
8
|
+
* the panel and the toolbar beside it never mount. A marker in the editor is
|
|
9
|
+
* the smaller loss, and it is what the story events pane already does.
|
|
10
|
+
*
|
|
11
|
+
* Nodes and windows are named rather than walked. Both are cyclic, so the guard
|
|
12
|
+
* below would terminate on them, but only after walking a few thousand
|
|
13
|
+
* properties of `window` on every keystroke.
|
|
14
|
+
*/
|
|
15
|
+
export function stringifyState(value: unknown, space?: number) {
|
|
16
|
+
// Ancestors, not everything seen: the same object appearing twice as a
|
|
17
|
+
// sibling is a shared reference, not a cycle, and is perfectly serialisable.
|
|
18
|
+
// The replacer's `this` is the holder, which is what lets the stack unwind.
|
|
19
|
+
const ancestors: any[] = []
|
|
20
|
+
|
|
21
|
+
return JSON.stringify(value, function (this: any, _key, current) {
|
|
22
|
+
if (typeof current === 'function') {
|
|
23
|
+
return '[Function]'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof current === 'bigint') {
|
|
27
|
+
return current.toString()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (current === null || typeof current !== 'object') {
|
|
31
|
+
return current
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof Node !== 'undefined' && current instanceof Node) {
|
|
35
|
+
return '[Node]'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof Window !== 'undefined' && current instanceof Window) {
|
|
39
|
+
return '[Window]'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) {
|
|
43
|
+
ancestors.pop()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (ancestors.includes(current)) {
|
|
47
|
+
return '[Circular]'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
ancestors.push(current)
|
|
51
|
+
|
|
52
|
+
return current
|
|
53
|
+
}, space)
|
|
54
|
+
}
|