@zeus-js/runtime-dom 0.1.0-beta.2 → 0.1.0-beta.4

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * runtime-dom v0.1.0-beta.2
2
+ * runtime-dom v0.1.0-beta.4
3
3
  * (c) 2026 baicie
4
4
  * Released under the MIT License.
5
5
  **/
@@ -172,7 +172,7 @@ function useContext(context, fallback) {
172
172
  const ZEUS_CONTEXT_REQUEST = "zeus:context-request";
173
173
  /**
174
174
  * Creates a transparent DOM element that acts as a context boundary.
175
- * Native custom elements inside it can use `requestDOMContext` to receive
175
+ * Native custom elements inside it can use `resolveDOMContext` to receive
176
176
  * context values via the DOM event protocol.
177
177
  */
178
178
  function createDOMContextBoundary(context, value, children) {
@@ -184,7 +184,7 @@ function createDOMContextBoundary(context, value, children) {
184
184
  }
185
185
  /**
186
186
  * Registers a context value on a DOM target so that any descendant custom
187
- * element can pick it up via `requestDOMContext`.
187
+ * element can pick it up via `resolveDOMContext`.
188
188
  */
189
189
  function provideDOMContext(target, context, value) {
190
190
  const handler = (event) => {
@@ -202,7 +202,7 @@ function provideDOMContext(target, context, value) {
202
202
  /**
203
203
  * Internal precise DOM context resolver.
204
204
  *
205
- * Unlike requestDOMContext(), this can distinguish:
205
+ * The result distinguishes:
206
206
  * - found: false, value: undefined
207
207
  * - found: true, value: undefined
208
208
  */
@@ -231,16 +231,6 @@ function resolveDOMContext(host, context) {
231
231
  value
232
232
  };
233
233
  }
234
- /**
235
- * Public compatibility API.
236
- *
237
- * Returns the resolved value if found, otherwise undefined.
238
- * If you need to distinguish "not found" from "found undefined",
239
- * use resolveDOMContext().
240
- */
241
- function requestDOMContext(host, context) {
242
- return resolveDOMContext(host, context).value;
243
- }
244
234
  function resolveValue$3(value) {
245
235
  return typeof value === "function" ? value() : value !== null && value !== void 0 ? value : null;
246
236
  }
@@ -632,6 +622,41 @@ function mountFor(parent, marker, each, key, render) {
632
622
  }
633
623
  //#endregion
634
624
  //#region packages/core/runtime-dom/src/defineElement.ts
625
+ const ZEUS_ELEMENT_DEFINITION = Symbol.for("zeus.element.definition");
626
+ function prop(input, options = {}) {
627
+ var _options$reflect, _options$default;
628
+ if (Array.isArray(input)) return {
629
+ type: String,
630
+ values: input,
631
+ attr: options.attr,
632
+ reflect: options.reflect,
633
+ default: options.default,
634
+ serialize: options.serialize,
635
+ deserialize: options.deserialize
636
+ };
637
+ const type = input;
638
+ return {
639
+ type,
640
+ attr: options.attr,
641
+ reflect: type === Boolean ? (_options$reflect = options.reflect) !== null && _options$reflect !== void 0 ? _options$reflect : true : options.reflect,
642
+ default: type === Boolean ? (_options$default = options.default) !== null && _options$default !== void 0 ? _options$default : false : options.default,
643
+ serialize: options.serialize,
644
+ deserialize: options.deserialize
645
+ };
646
+ }
647
+ function event(input) {
648
+ if (typeof input === "string") return {
649
+ __zeusEvent: true,
650
+ name: input
651
+ };
652
+ return {
653
+ __zeusEvent: true,
654
+ name: input === null || input === void 0 ? void 0 : input.name,
655
+ bubbles: input === null || input === void 0 ? void 0 : input.bubbles,
656
+ composed: input === null || input === void 0 ? void 0 : input.composed,
657
+ cancelable: input === null || input === void 0 ? void 0 : input.cancelable
658
+ };
659
+ }
635
660
  function createPropStore(defs) {
636
661
  const slots = /* @__PURE__ */ new Map();
637
662
  const props = {};
@@ -671,6 +696,12 @@ function createPropStore(defs) {
671
696
  function defineElement(tagName, options, setup) {
672
697
  var _options$props;
673
698
  const propDefs = normalizePropDefinitions((_options$props = options.props) !== null && _options$props !== void 0 ? _options$props : {});
699
+ const definition = {
700
+ tagName,
701
+ options,
702
+ setup,
703
+ propDefs
704
+ };
674
705
  const observedAttributes = propDefs.filter((def) => def.attr !== false).map((def) => def.attr);
675
706
  class ZeusElement extends HTMLElement {
676
707
  static get observedAttributes() {
@@ -685,6 +716,13 @@ function defineElement(tagName, options, setup) {
685
716
  this.props = this.propStore.props;
686
717
  applyPropDefaults(this.propStore, propDefs);
687
718
  definePropAccessors(this, this.propStore, propDefs);
719
+ if (options.formAssociated) this.internals = attachElementInternals(this);
720
+ this.setupContext = {
721
+ host: this,
722
+ internals: this.internals,
723
+ emit: createEmitApi(this, options.emits),
724
+ expose: createExpose(this)
725
+ };
688
726
  }
689
727
  connectedCallback() {
690
728
  var _options$shadow, _options$consumes;
@@ -708,19 +746,10 @@ function defineElement(tagName, options, setup) {
708
746
  mode,
709
747
  lightChildren: this.lightChildren
710
748
  };
711
- const setupContext = {
712
- host: this,
713
- emit: (name, detail, eventOptions) => {
714
- return this.dispatchEvent(new CustomEvent(name, {
715
- bubbles: true,
716
- composed: true,
717
- cancelable: true,
718
- ...eventOptions,
719
- detail
720
- }));
721
- }
722
- };
723
- this.dispose = render(() => runWithOwner(owner, () => withHostContext(hostContext, () => setup(this.props, setupContext))), target, { owner });
749
+ this.dispose = render(() => runWithOwner(owner, () => withHostContext(hostContext, () => {
750
+ syncFormValue(this.props, this.setupContext, options.form);
751
+ return setup(this.props, this.setupContext);
752
+ })), target, { owner });
724
753
  mountStyles(target, options.styles);
725
754
  }
726
755
  disconnectedCallback() {
@@ -728,6 +757,22 @@ function defineElement(tagName, options, setup) {
728
757
  (_this$dispose = this.dispose) === null || _this$dispose === void 0 || _this$dispose.call(this);
729
758
  this.dispose = void 0;
730
759
  }
760
+ formAssociatedCallback(form) {
761
+ var _options$form, _options$form$associa;
762
+ (_options$form = options.form) === null || _options$form === void 0 || (_options$form$associa = _options$form.associated) === null || _options$form$associa === void 0 || _options$form$associa.call(_options$form, form, this.props, this.setupContext);
763
+ }
764
+ formDisabledCallback(disabled) {
765
+ var _options$form2, _options$form2$disabl;
766
+ (_options$form2 = options.form) === null || _options$form2 === void 0 || (_options$form2$disabl = _options$form2.disabled) === null || _options$form2$disabl === void 0 || _options$form2$disabl.call(_options$form2, disabled, this.props, this.setupContext);
767
+ }
768
+ formResetCallback() {
769
+ var _options$form3, _options$form3$reset;
770
+ (_options$form3 = options.form) === null || _options$form3 === void 0 || (_options$form3$reset = _options$form3.reset) === null || _options$form3$reset === void 0 || _options$form3$reset.call(_options$form3, this.props, this.setupContext);
771
+ }
772
+ formStateRestoreCallback(state, mode) {
773
+ var _options$form4, _options$form4$stateR;
774
+ (_options$form4 = options.form) === null || _options$form4 === void 0 || (_options$form4$stateR = _options$form4.stateRestore) === null || _options$form4$stateR === void 0 || _options$form4$stateR.call(_options$form4, state, mode, this.props, this.setupContext);
775
+ }
731
776
  attributeChangedCallback(name, oldValue, newValue) {
732
777
  if (oldValue === newValue || this.reflecting) return;
733
778
  const def = propDefs.find((item) => item.attr === name);
@@ -763,24 +808,131 @@ function defineElement(tagName, options, setup) {
763
808
  }
764
809
  }
765
810
  }
811
+ ZeusElement.formAssociated = Boolean(options.formAssociated);
766
812
  if (!customElements.get(tagName)) customElements.define(tagName, ZeusElement);
813
+ Object.defineProperty(ZeusElement, ZEUS_ELEMENT_DEFINITION, {
814
+ value: definition,
815
+ configurable: false
816
+ });
767
817
  return ZeusElement;
768
818
  }
819
+ function getElementDefinition(ctor) {
820
+ const definition = ctor[ZEUS_ELEMENT_DEFINITION];
821
+ if (!definition) throw new Error("[zeus] Element definition metadata not found.");
822
+ return definition;
823
+ }
824
+ function mountElementDefinition(ctor, host, initialValues = /* @__PURE__ */ new Map(), mountState = {}) {
825
+ var _options$shadow2, _mountState$lightChil, _options$consumes2, _mountState$target, _mountState$internals;
826
+ const { options, setup, propDefs } = getElementDefinition(ctor);
827
+ const propStore = createPropStore(propDefs);
828
+ applyPropDefaults(propStore, propDefs);
829
+ for (const def of propDefs) {
830
+ var _mountState$attribute;
831
+ if (def.attr !== false && ((_mountState$attribute = mountState.attributeProps) === null || _mountState$attribute === void 0 ? void 0 : _mountState$attribute.has(def.key))) {
832
+ propStore.set(def.key, castAttributeValue(host.getAttribute(def.attr), def));
833
+ initialValues.set(def.key, propStore.get(def.key));
834
+ continue;
835
+ }
836
+ if (initialValues.has(def.key)) {
837
+ propStore.set(def.key, initialValues.get(def.key));
838
+ continue;
839
+ }
840
+ /**
841
+ * Sync real definition defaults (e.g. factory defaults like `default: () => []`)
842
+ * back to the lazy host value map so that `element.propName` returns the
843
+ * correct default value rather than undefined.
844
+ */
845
+ initialValues.set(def.key, propStore.get(def.key));
846
+ }
847
+ for (const def of propDefs) {
848
+ var _mountState$attribute2;
849
+ if (def.reflect && def.serialize && !((_mountState$attribute2 = mountState.attributeProps) === null || _mountState$attribute2 === void 0 ? void 0 : _mountState$attribute2.has(def.key))) reflectExternalProp(host, def, propStore.get(def.key), mountState.reflectingAttrs);
850
+ }
851
+ const shadow = (_options$shadow2 = options.shadow) !== null && _options$shadow2 !== void 0 ? _options$shadow2 : false;
852
+ const mode = shadow ? "shadow" : "light";
853
+ if (mode === "light" && !mountState.capturedLightChildren) {
854
+ mountState.lightChildren = Array.from(host.childNodes);
855
+ mountState.capturedLightChildren = true;
856
+ }
857
+ const lightChildren = (_mountState$lightChil = mountState.lightChildren) !== null && _mountState$lightChil !== void 0 ? _mountState$lightChil : [];
858
+ const owner = createOwner();
859
+ for (const context of (_options$consumes2 = options.consumes) !== null && _options$consumes2 !== void 0 ? _options$consumes2 : []) {
860
+ const resolved = resolveDOMContext(host, context);
861
+ if (resolved.found) owner.provides.set(context.id, resolved.value);
862
+ else if (context.hasDefaultValue) owner.provides.set(context.id, context.defaultValue);
863
+ }
864
+ const target = (_mountState$target = mountState.target) !== null && _mountState$target !== void 0 ? _mountState$target : mountState.target = resolveExternalRenderTarget(host, shadow);
865
+ const hostContext = {
866
+ host,
867
+ mode,
868
+ lightChildren
869
+ };
870
+ const setupContext = {
871
+ host,
872
+ internals: (_mountState$internals = mountState.internals) !== null && _mountState$internals !== void 0 ? _mountState$internals : options.formAssociated ? attachElementInternals(host) : void 0,
873
+ emit: createEmitApi(host, options.emits),
874
+ expose: createExpose(host)
875
+ };
876
+ mountState.internals = setupContext.internals;
877
+ const dispose = render(() => runWithOwner(owner, () => withHostContext(hostContext, () => {
878
+ syncFormValue(propStore.props, setupContext, options.form);
879
+ return setup(propStore.props, setupContext);
880
+ })), target, { owner });
881
+ mountStyles(target, options.styles);
882
+ return {
883
+ propertyChanged(name, _oldValue, newValue) {
884
+ var _mountState$attribute3;
885
+ const def = propDefs.find((item) => item.key === name);
886
+ const fromAttribute = Boolean((def === null || def === void 0 ? void 0 : def.attr) !== false && ((_mountState$attribute3 = mountState.attributeProps) === null || _mountState$attribute3 === void 0 ? void 0 : _mountState$attribute3.has(name)));
887
+ const value = fromAttribute && def ? castAttributeValue(typeof newValue === "string" ? newValue : null, def) : newValue;
888
+ propStore.set(name, value);
889
+ initialValues.set(name, value);
890
+ if ((def === null || def === void 0 ? void 0 : def.reflect) && !fromAttribute) reflectExternalProp(host, def, value, mountState.reflectingAttrs);
891
+ },
892
+ formAssociated(form) {
893
+ var _options$form5, _options$form5$associ;
894
+ (_options$form5 = options.form) === null || _options$form5 === void 0 || (_options$form5$associ = _options$form5.associated) === null || _options$form5$associ === void 0 || _options$form5$associ.call(_options$form5, form, propStore.props, setupContext);
895
+ },
896
+ formDisabled(disabled) {
897
+ var _options$form6, _options$form6$disabl;
898
+ (_options$form6 = options.form) === null || _options$form6 === void 0 || (_options$form6$disabl = _options$form6.disabled) === null || _options$form6$disabl === void 0 || _options$form6$disabl.call(_options$form6, disabled, propStore.props, setupContext);
899
+ },
900
+ formReset() {
901
+ var _options$form7, _options$form7$reset;
902
+ (_options$form7 = options.form) === null || _options$form7 === void 0 || (_options$form7$reset = _options$form7.reset) === null || _options$form7$reset === void 0 || _options$form7$reset.call(_options$form7, propStore.props, setupContext);
903
+ },
904
+ formStateRestore(state, mode) {
905
+ var _options$form8, _options$form8$stateR;
906
+ (_options$form8 = options.form) === null || _options$form8 === void 0 || (_options$form8$stateR = _options$form8.stateRestore) === null || _options$form8$stateR === void 0 || _options$form8$stateR.call(_options$form8, state, mode, propStore.props, setupContext);
907
+ },
908
+ dispose() {
909
+ dispose();
910
+ }
911
+ };
912
+ }
769
913
  function normalizePropDefinitions(props) {
770
914
  return Object.keys(props).map((key) => {
915
+ const propKey = String(key);
771
916
  const input = props[key];
772
- if (typeof input === "function") return {
773
- key,
774
- attr: toKebabCase(key),
775
- type: input,
776
- reflect: false
777
- };
917
+ if (typeof input === "function") {
918
+ const type = input;
919
+ return {
920
+ key: propKey,
921
+ attr: isAttributeBackedConstructor(type) ? toKebabCase(propKey) : false,
922
+ type,
923
+ reflect: false
924
+ };
925
+ }
926
+ const type = input === null || input === void 0 ? void 0 : input.type;
927
+ const defaultAttr = isAttributeBackedConstructor(type) ? toKebabCase(propKey) : false;
778
928
  return {
779
- key,
780
- attr: (input === null || input === void 0 ? void 0 : input.attr) === void 0 ? toKebabCase(key) : input.attr,
781
- type: input === null || input === void 0 ? void 0 : input.type,
929
+ key: propKey,
930
+ attr: (input === null || input === void 0 ? void 0 : input.attr) === void 0 ? defaultAttr : input.attr,
931
+ type,
782
932
  reflect: Boolean(input === null || input === void 0 ? void 0 : input.reflect),
783
- default: input === null || input === void 0 ? void 0 : input.default
933
+ default: input === null || input === void 0 ? void 0 : input.default,
934
+ serialize: input === null || input === void 0 ? void 0 : input.serialize,
935
+ deserialize: input === null || input === void 0 ? void 0 : input.deserialize
784
936
  };
785
937
  });
786
938
  }
@@ -816,6 +968,7 @@ function definePropAccessors(element, store, defs) {
816
968
  }
817
969
  }
818
970
  function castAttributeValue(value, def) {
971
+ if (def.deserialize) return def.deserialize(value);
819
972
  if (def.type === Boolean) return value !== null;
820
973
  if (value === null) return;
821
974
  if (def.type === Number) return Number(value);
@@ -825,10 +978,17 @@ function castAttributeValue(value, def) {
825
978
  console.warn(`[Zeus custom-element] Failed to parse JSON attribute "${def.attr}".`);
826
979
  return def.type === Array ? [] : {};
827
980
  }
981
+ if (def.type === Function) return;
828
982
  return value;
829
983
  }
830
984
  function reflectPropToAttribute(element, def, value) {
831
985
  if (def.attr === false) return;
986
+ if (def.serialize) {
987
+ const serialized = def.serialize(value);
988
+ if (serialized == null) element.removeAttribute(def.attr);
989
+ else element.setAttribute(def.attr, serialized);
990
+ return;
991
+ }
832
992
  if (def.type === Boolean) {
833
993
  if (value) element.setAttribute(def.attr, "");
834
994
  else element.removeAttribute(def.attr);
@@ -842,8 +1002,85 @@ function reflectPropToAttribute(element, def, value) {
842
1002
  element.setAttribute(def.attr, JSON.stringify(value));
843
1003
  return;
844
1004
  }
1005
+ if (def.type === Function) return;
845
1006
  element.setAttribute(def.attr, String(value));
846
1007
  }
1008
+ function reflectExternalProp(element, def, value, reflectingAttrs) {
1009
+ if (def.attr === false) return;
1010
+ const attrName = def.attr.toLowerCase();
1011
+ reflectingAttrs === null || reflectingAttrs === void 0 || reflectingAttrs.add(attrName);
1012
+ try {
1013
+ reflectPropToAttribute(element, def, value);
1014
+ } finally {
1015
+ reflectingAttrs === null || reflectingAttrs === void 0 || reflectingAttrs.delete(attrName);
1016
+ }
1017
+ }
1018
+ function syncFormValue(props, context, form) {
1019
+ const valueResolver = form === null || form === void 0 ? void 0 : form.value;
1020
+ const stateResolver = form === null || form === void 0 ? void 0 : form.state;
1021
+ if (!context.internals || valueResolver === void 0) return;
1022
+ (0, _zeus_js_signal.effect)(() => {
1023
+ const value = resolveFormValue(props, valueResolver);
1024
+ const state = stateResolver === void 0 ? void 0 : resolveFormValue(props, stateResolver);
1025
+ context.internals.setFormValue(value, state);
1026
+ });
1027
+ }
1028
+ function resolveFormValue(props, resolver) {
1029
+ if (typeof resolver === "function") {
1030
+ var _resolver;
1031
+ return (_resolver = resolver(props)) !== null && _resolver !== void 0 ? _resolver : null;
1032
+ }
1033
+ const value = props[resolver];
1034
+ return value == null ? null : value;
1035
+ }
1036
+ function attachElementInternals(host) {
1037
+ const attachInternals = host.attachInternals;
1038
+ if (typeof attachInternals !== "function") return;
1039
+ try {
1040
+ return attachInternals.call(host);
1041
+ } catch (error) {
1042
+ console.warn("[Zeus custom-element] Failed to attach ElementInternals.", error);
1043
+ return;
1044
+ }
1045
+ }
1046
+ function createEmitApi(host, emits) {
1047
+ const emit = {};
1048
+ const dispatch = (name, detail, options) => {
1049
+ var _definition$name, _ref, _options$bubbles, _ref2, _options$composed, _ref3, _options$cancelable;
1050
+ const definition = emits === null || emits === void 0 ? void 0 : emits[name];
1051
+ return host.dispatchEvent(new CustomEvent((_definition$name = definition === null || definition === void 0 ? void 0 : definition.name) !== null && _definition$name !== void 0 ? _definition$name : toKebabCase(name), {
1052
+ bubbles: (_ref = (_options$bubbles = options === null || options === void 0 ? void 0 : options.bubbles) !== null && _options$bubbles !== void 0 ? _options$bubbles : definition === null || definition === void 0 ? void 0 : definition.bubbles) !== null && _ref !== void 0 ? _ref : true,
1053
+ composed: (_ref2 = (_options$composed = options === null || options === void 0 ? void 0 : options.composed) !== null && _options$composed !== void 0 ? _options$composed : definition === null || definition === void 0 ? void 0 : definition.composed) !== null && _ref2 !== void 0 ? _ref2 : true,
1054
+ cancelable: (_ref3 = (_options$cancelable = options === null || options === void 0 ? void 0 : options.cancelable) !== null && _options$cancelable !== void 0 ? _options$cancelable : definition === null || definition === void 0 ? void 0 : definition.cancelable) !== null && _ref3 !== void 0 ? _ref3 : false,
1055
+ detail
1056
+ }));
1057
+ };
1058
+ if (emits) for (const key of Object.keys(emits)) Object.defineProperty(emit, key, {
1059
+ configurable: true,
1060
+ enumerable: true,
1061
+ value(detail, options) {
1062
+ return dispatch(key, detail, options);
1063
+ }
1064
+ });
1065
+ return emit;
1066
+ }
1067
+ function createExpose(host) {
1068
+ return (methods) => {
1069
+ for (const key of Object.keys(methods)) Object.defineProperty(host, key, {
1070
+ configurable: true,
1071
+ enumerable: false,
1072
+ value: methods[key]
1073
+ });
1074
+ };
1075
+ }
1076
+ function isAttributeBackedConstructor(type) {
1077
+ return type === String || type === Number || type === Boolean;
1078
+ }
1079
+ function resolveExternalRenderTarget(host, shadow) {
1080
+ var _host$shadowRoot;
1081
+ if (!shadow) return host;
1082
+ return (_host$shadowRoot = host.shadowRoot) !== null && _host$shadowRoot !== void 0 ? _host$shadowRoot : host.attachShadow(typeof shadow === "object" ? shadow : { mode: "open" });
1083
+ }
847
1084
  function mountStyles(target, styles) {
848
1085
  if (!styles) return;
849
1086
  const list = Array.isArray(styles) ? styles : [styles];
@@ -985,6 +1222,7 @@ exports.Host = Host;
985
1222
  exports.Show = Show;
986
1223
  exports.Slot = Slot;
987
1224
  exports.ZEUS_CONTEXT_REQUEST = ZEUS_CONTEXT_REQUEST;
1225
+ exports.ZEUS_ELEMENT_DEFINITION = ZEUS_ELEMENT_DEFINITION;
988
1226
  exports.bindAttr = bindAttr;
989
1227
  exports.bindClass = bindClass;
990
1228
  exports.bindEvent = bindEvent;
@@ -1002,21 +1240,24 @@ exports.createOwner = createOwner;
1002
1240
  exports.createSlot = createSlot;
1003
1241
  exports.defineElement = defineElement;
1004
1242
  exports.delegateEvents = delegateEvents;
1243
+ exports.event = event;
1005
1244
  exports.getCurrentHostContext = getCurrentHostContext;
1006
1245
  exports.getCurrentOwner = getCurrentOwner;
1246
+ exports.getElementDefinition = getElementDefinition;
1007
1247
  exports.inject = inject;
1008
1248
  exports.insert = insert;
1009
1249
  exports.insertTracked = insertTracked;
1010
1250
  exports.marker = marker;
1011
1251
  exports.mountDynamic = mountDynamic;
1252
+ exports.mountElementDefinition = mountElementDefinition;
1012
1253
  exports.mountFor = mountFor;
1013
1254
  exports.mountShow = mountShow;
1014
1255
  exports.normalizeClass = normalizeClass;
1256
+ exports.prop = prop;
1015
1257
  exports.provide = provide;
1016
1258
  exports.provideDOMContext = provideDOMContext;
1017
1259
  exports.removeNodes = removeNodes;
1018
1260
  exports.render = render;
1019
- exports.requestDOMContext = requestDOMContext;
1020
1261
  exports.resolveDOMContext = resolveDOMContext;
1021
1262
  exports.resolveValue = resolveValue;
1022
1263
  exports.runWithOwner = runWithOwner;