native-document 1.0.261 → 1.0.262

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/elements.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // Main NativeDocument type definitions index
2
2
  export * from './types/observable';
3
3
  export * from './types/elements';
4
+ export * from './types/slot';
4
5
  export * from './types/nd-element';
5
6
  export * from './types/forms';
6
7
  export * from './types/images';
package/elements.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export {default as Anchor, default as NativeDocumentFragment } from "./src/core/elements/anchor/anchor";
2
+ export { default as Slot } from "./src/core/elements/anchor/Slot";
2
3
  export { createPortal } from "./src/core/elements/anchor/anchor";
3
4
  export * from './src/core/elements/index';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "native-document",
3
- "version": "1.0.261",
3
+ "version": "1.0.262",
4
4
  "description": "A reactive JavaScript framework that preserves native DOM simplicity without sacrificing modern features",
5
5
  "author": "AfroCodeur <https://github.com/afrocodeur>",
6
6
  "license": "MIT",
@@ -53,6 +53,7 @@ export default function SelectField(name, props) {
53
53
  selectedLabelRender: false,
54
54
  renderItem: null,
55
55
  truncateMax: null,
56
+ optionsMapper: null,
56
57
  });
57
58
  }
58
59
 
@@ -83,7 +84,8 @@ SelectField.defaultTemplate = null;
83
84
  * hasErrors: Observable<boolean>,
84
85
  * errors: Observable<string[]>,
85
86
  * showErrors: Observable<boolean>,
86
- * props: GlobalAttributes
87
+ * props: GlobalAttributes,
88
+ * optionsMapper: (item) => {value: string|number, label: NdChild},
87
89
  * }, instance: SelectField) => NdChild} template
88
90
  */
89
91
  SelectField.use = function(template) {
@@ -95,10 +97,12 @@ SelectField.prototype.constructor = SelectField;
95
97
 
96
98
  /**
97
99
  * @param {Array<{ value: *, label: NdChild, props?: GlobalAttributes }>} opts
100
+ * @param {(item) => {value: string, label: string}} mapper
98
101
  * @returns {this}
99
102
  */
100
- SelectField.prototype.options = function(opts) {
103
+ SelectField.prototype.options = function(opts, mapper) {
101
104
  this.$description.options = opts;
105
+ this.$description.optionsMapper = mapper;
102
106
  return this;
103
107
  };
104
108
 
@@ -0,0 +1,30 @@
1
+ import Anchor from './anchor';
2
+ import {NDElement} from '../../wrappers/NDElement';
3
+
4
+
5
+ export default function Slot(content) {
6
+ if(!(this instanceof Slot)) {
7
+ return new Slot(content);
8
+ }
9
+ this.$anchor = Anchor();
10
+
11
+ content && this.$anchor.setContent(content);
12
+ }
13
+
14
+
15
+ Slot.prototype.toNdElement = function() {
16
+ return this.$anchor;
17
+ };
18
+
19
+ Slot.prototype.set = function(value) {
20
+ this.$anchor.replaceContent(value);
21
+ return this;
22
+ };
23
+
24
+ Slot.prototype.clear = function() {
25
+ this.$anchor.clear();
26
+ return this;
27
+ };
28
+
29
+ Slot.prototype.refSelf = NDElement.prototype.refSelf;
30
+ Slot.prototype.ref = NDElement.prototype.ref;
@@ -33,6 +33,26 @@ export default function SelectFieldRender($desc, instance) {
33
33
  return Div(instance.resolveProps(), content).nd.with({input: nativeSelect});
34
34
  }
35
35
 
36
+ const getUsableOption = ($desc, option) => {
37
+ if(option == null) {
38
+ option = { value: null, label: null};
39
+ }
40
+ else if($desc.optionsMapper) {
41
+ option = $desc.optionsMapper(option);
42
+ }
43
+ if(typeof option === 'string' || typeof option === 'number') {
44
+ option = { value: option, label: option };
45
+ }
46
+ return option;
47
+ };
48
+
49
+ const findUsableOption = ($desc, options, val) => {
50
+ return options.filter((option) => {
51
+ const optionsSource = getUsableOption($desc, option);
52
+ return optionsSource.value.valueOf() === val;
53
+ }).map((option) => getUsableOption($desc, option)).at(0);
54
+ };
55
+
36
56
  const buildLabel = ($desc) => {
37
57
  return Label({
38
58
  class: 'field-label',
@@ -91,7 +111,7 @@ const buildTagsDisplay = ($desc) => {
91
111
 
92
112
  return Div({class: 'select-field-tags'},
93
113
  ForEachArray($desc.value, (val) => {
94
- const opt = options.find(o => (o.value ?? o) === val);
114
+ const opt = findUsableOption($desc, options, val);
95
115
  const label = opt?.label ?? opt ?? val;
96
116
 
97
117
  const tag = Span({class: 'select-field-tag'}, [
@@ -133,7 +153,7 @@ const buildSelectedLabel = ($desc) => {
133
153
  const options = $desc.options;
134
154
 
135
155
  if(!$desc.multiple || !Array.isArray(val)) {
136
- const opt = options.find(o => (o.value ?? o) == val);
156
+ const opt = findUsableOption($desc, options, val);
137
157
  return opt?.label ?? opt ?? val;
138
158
  }
139
159
 
@@ -144,7 +164,7 @@ const buildSelectedLabel = ($desc) => {
144
164
  }
145
165
 
146
166
  const labels = val.map(v => {
147
- const opt = options.find(o => (o.value ?? o) === v);
167
+ const opt = findUsableOption($desc, options, v);
148
168
  return opt?.label ?? opt ?? v;
149
169
  });
150
170
 
@@ -175,8 +195,9 @@ const buildNativeSelect = ($desc) => {
175
195
  multiple: $desc.multiple,
176
196
  style: {display: 'none'},
177
197
  }, ForEachArray($options, (option) => {
178
- const optValue = option.value ?? option;
179
- const optLabel = option.label ?? option;
198
+ const optionSource = getUsableOption($desc, option);
199
+ const optValue = optionSource.value ?? option;
200
+ const optLabel = optionSource.label ?? option;
180
201
  const optProps = option.props ?? {};
181
202
  return Option({value: optValue, ...optProps}, optLabel);
182
203
  }));
@@ -224,8 +245,9 @@ const buildDropdown = ($desc, instance, trigger, nativeSelect) => {
224
245
  }
225
246
 
226
247
  return dropdown.bind($sourceItem, (option) => {
227
- const optValue = option.value ?? option;
228
- const optLabel = option.label ?? option;
248
+ const optionSource = getUsableOption($desc, option);
249
+ const optValue = optionSource.value ?? option;
250
+ const optLabel = optionSource.label ?? option;
229
251
 
230
252
  const $isSelected = $desc.value?.__$isObservable
231
253
  ? $desc.value.is(v => $desc.multiple
package/types/slot.ts ADDED
@@ -0,0 +1,75 @@
1
+ import {AnchorDocumentFragment, ValidChild} from "./elements";
2
+
3
+
4
+ /**
5
+ * A dynamic content slot backed by an Anchor.
6
+ * Allows replacing, clearing and referencing a zone of the DOM
7
+ * without re-rendering the surrounding structure.
8
+ *
9
+ * @example
10
+ * const slot = Slot(Div('Initial content'));
11
+ *
12
+ * // Replace content
13
+ * slot.set(Div('New content'));
14
+ *
15
+ * // Clear content
16
+ * slot.clear();
17
+ *
18
+ * // Store reference
19
+ * const refs = { content: null };
20
+ * slot.refSelf(refs, 'content');
21
+ * refs.content.set(Div('Updated'));
22
+ */
23
+ export interface SlotInterface {
24
+ /** Internal anchor managing the DOM zone. */
25
+ readonly $anchor: AnchorDocumentFragment;
26
+
27
+ /**
28
+ * Replaces the current content of the slot.
29
+ *
30
+ * @param value - New content to display
31
+ */
32
+ set(value: ValidChild): this;
33
+
34
+ /**
35
+ * Clears the slot content.
36
+ */
37
+ clear(): this;
38
+
39
+ /**
40
+ * Stores the native DOM node reference in an object under the given key.
41
+ *
42
+ * @param target - Object to store the reference in
43
+ * @param key - Key to assign the reference to
44
+ */
45
+ ref<T extends object>(target: T, key: keyof T): this;
46
+
47
+ /**
48
+ * Stores the SlotInterface instance itself in an object under the given key.
49
+ *
50
+ * @param target - Object to store the reference in
51
+ * @param key - Key to assign the reference to
52
+ */
53
+ refSelf<T extends object>(target: T, key: keyof T): this;
54
+
55
+ /**
56
+ * Returns the underlying Anchor element for DOM insertion.
57
+ * Called automatically by NativeDocument when the Slot is used as a child.
58
+ */
59
+ toNdElement(): AnchorDocumentFragment;
60
+ }
61
+
62
+ /**
63
+ * Creates a dynamic content slot — a named zone of the DOM that can be
64
+ * replaced or cleared independently of its surroundings.
65
+ *
66
+ * @param content - Optional initial content
67
+ * @returns SlotInterface instance
68
+ *
69
+ * @example
70
+ * const $header = Slot(H1('Welcome'));
71
+ *
72
+ * // Later
73
+ * $header.set(H1('Dashboard'));
74
+ */
75
+ export declare function Slot(content?: ValidChild): SlotInterface;