@oscarpalmer/abydon 0.22.0 → 0.23.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.
@@ -1,12 +1,10 @@
1
1
  import { EXPRESSION_ABYDON_CONTENT, EXPRESSION_TEXTAREA_VALUE } from "../constants.mjs";
2
- import { isFragment, isFragments } from "../helpers/index.mjs";
2
+ import { isFragment, isFragments, setComputedValue } from "../helpers/index.mjs";
3
3
  import { fragmentsStates, handleFragments } from "../fragments.mjs";
4
4
  import { createNodes } from "../helpers/dom.mjs";
5
- import { mapEvent } from "./event.mjs";
6
- import { mapAttributes } from "../attribute/index.mjs";
5
+ import { mapAttributeValue, mapAttributes } from "../attribute/index.mjs";
7
6
  import { setReactiveValue } from "./value.mjs";
8
- import { getString } from "@oscarpalmer/atoms/string";
9
- import { computed, isComputed, isReactive, isSignal } from "@oscarpalmer/mora";
7
+ import { isReactive } from "@oscarpalmer/mora";
10
8
  import { isHTMLOrSVGElement } from "@oscarpalmer/toretto/is";
11
9
  //#region src/node/index.ts
12
10
  function mapNode(data, comment) {
@@ -22,42 +20,29 @@ function mapNodes(data, nodes) {
22
20
  mapNode(data, node);
23
21
  continue;
24
22
  }
25
- if (node instanceof HTMLTextAreaElement) mapTextarea(data, node);
26
- if (isHTMLOrSVGElement(node)) mapAttributes(data, node);
23
+ if (isHTMLOrSVGElement(node)) {
24
+ let ignoreValueAttribute = false;
25
+ if (node instanceof HTMLTextAreaElement) ignoreValueAttribute = mapTextarea(data, node);
26
+ mapAttributes(data, node, ignoreValueAttribute);
27
+ }
27
28
  if (node.hasChildNodes()) mapNodes(data, [...node.childNodes]);
28
29
  }
29
30
  }
30
31
  function mapTextarea(data, element) {
31
32
  const [, index] = EXPRESSION_TEXTAREA_VALUE.exec(element.textContent) ?? EXPRESSION_TEXTAREA_VALUE.exec(element.value) ?? [];
32
- if (index == null) return;
33
+ if (index == null) return false;
33
34
  element.textContent = "";
34
35
  element.value = "";
35
- const value = data.values[Number.parseInt(index, 10)];
36
- if (isSignal(value)) {
37
- element.value = getString(value.peek());
38
- mapEvent(element, "on", () => {
39
- value.set(element.value);
40
- });
41
- data.mora.subscribers.add(value.subscribe((value) => {
42
- element.value = getString(value);
43
- }));
44
- return;
45
- }
46
- let reactive;
47
- if (typeof value === "function") reactive = computed(value);
48
- else if (isComputed(value)) reactive = value;
49
- if (reactive == null) element.value = "";
50
- else data.mora.subscribers.add(reactive.subscribe((value) => {
51
- element.value = getString(value);
52
- }));
36
+ mapAttributeValue(data, element, "value", data.values[Number.parseInt(index, 10)]);
37
+ return true;
53
38
  }
54
39
  function mapValue(data, comment, value) {
55
40
  switch (true) {
56
41
  case typeof value === "function":
57
- setComputedValue(data, comment, value);
42
+ setComputedNode(data, comment, value);
58
43
  break;
59
44
  case isFragments(value):
60
- setFragmentsValue(data, comment, value);
45
+ setFragmentsNode(data, comment, value);
61
46
  break;
62
47
  case isReactive(value):
63
48
  setReactiveValue(data, comment, value);
@@ -76,12 +61,12 @@ function replaceComment(data, comment, value) {
76
61
  }
77
62
  comment.replaceWith(...nodes);
78
63
  }
79
- function setComputedValue(data, comment, callback) {
80
- const value = computed(callback);
81
- data.mora.values.add(value);
82
- setReactiveValue(data, comment, value);
64
+ function setComputedNode(data, comment, callback) {
65
+ setComputedValue(data, callback, (computation) => {
66
+ setReactiveValue(data, comment, computation);
67
+ });
83
68
  }
84
- function setFragmentsValue(data, comment, fragments) {
69
+ function setFragmentsNode(data, comment, fragments) {
85
70
  const state = fragmentsStates.get(fragments);
86
71
  handleFragments(state, false);
87
72
  setReactiveValue(data, comment, state.mapped);
@@ -7,7 +7,7 @@ import { isChildNode } from "@oscarpalmer/toretto/is";
7
7
  //#region src/node/value.ts
8
8
  function addToArray(identifiers, items, nodes, added) {
9
9
  let position = nodes[0];
10
- const before = added && !identifiers.previous.has(items.templates[0].identifier);
10
+ const before = added && !identifiers.previous.set.has(items.templates[0].identifier);
11
11
  const next = items.next.flatMap((fragment) => fragment.get().flatMap((node) => ({
12
12
  identifier: fragment.identifier,
13
13
  value: node
@@ -15,19 +15,48 @@ function addToArray(identifiers, items, nodes, added) {
15
15
  const { length } = next;
16
16
  for (let index = 0; index < length; index += 1) {
17
17
  const node = next[index];
18
- if (!(added && identifiers.previous.has(node.identifier))) if (index === 0 && before) position.before(node.value);
18
+ if (!(added && identifiers.previous.set.has(node.identifier))) if (index === 0 && before) position.before(node.value);
19
19
  else position.after(node.value);
20
20
  position = node.value;
21
21
  }
22
22
  }
23
+ function getArrayData(item, values) {
24
+ const { length } = values;
25
+ const next = [];
26
+ let templates = [];
27
+ for (let index = 0; index < length; index += 1) {
28
+ const value = values[index];
29
+ if (isFragment(value) && value.identifier != null) {
30
+ next.push(value.identifier);
31
+ templates.push(value);
32
+ }
33
+ }
34
+ const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
35
+ const nextSet = new Set(next);
36
+ if (nextSet.size !== templates.length) templates = [];
37
+ return {
38
+ next: {
39
+ array: next,
40
+ set: nextSet
41
+ },
42
+ previous: {
43
+ array: previous,
44
+ set: new Set(previous)
45
+ },
46
+ template: {
47
+ empty: templates.length === 0,
48
+ items: templates
49
+ }
50
+ };
51
+ }
23
52
  function handleArray(identifiers, items, nodes) {
24
- const next = items.templates.map((template) => items.fragments?.find((fragment) => fragment.identifier === template.identifier) ?? template);
25
- const comparison = compareArrays(items.fragments ?? [], items.templates);
53
+ const next = items.templates.map((template) => items.fragments.find((fragment) => fragment.identifier === template.identifier) ?? template);
54
+ const comparison = compareArrays(identifiers.previous.array, identifiers.next.array);
26
55
  if (comparison !== "removed") addToArray(identifiers, {
27
56
  ...items,
28
57
  next
29
58
  }, nodes, comparison === ARRAY_COMPARISON_ADDED);
30
- const toRemove = items.fragments?.filter((fragment) => !identifiers.next.has(fragment.identifier)) ?? [];
59
+ const toRemove = items.fragments.filter((fragment) => !identifiers.next.set.has(fragment.identifier));
31
60
  const { length } = toRemove;
32
61
  for (let index = 0; index < length; index += 1) toRemove[index].remove();
33
62
  return {
@@ -35,52 +64,45 @@ function handleArray(identifiers, items, nodes) {
35
64
  nodes: next.flatMap((fragment) => fragment.get())
36
65
  };
37
66
  }
38
- function removeFragments(fragments) {
39
- if (fragments != null) {
40
- const { length } = fragments;
41
- for (let index = 0; index < length; index += 1) fragments[index].remove();
42
- }
67
+ function removeArray(item, comment) {
68
+ const fragments = (item.fragments ?? []).slice();
69
+ const result = { nodes: setText(item, comment) };
70
+ removeFragmentsItems(fragments);
71
+ return result;
43
72
  }
44
- function replaceText(item, comment, isNullable) {
45
- let to;
46
- if (isNullable) to = [comment];
47
- else to = item.text == null ? [] : [item.text];
48
- replaceNodes(item.nodes ?? [], to);
73
+ function removeFragmentsItems(fragments) {
74
+ const { length } = fragments;
75
+ for (let index = 0; index < length; index += 1) fragments[index].remove();
49
76
  }
50
77
  function setArray(item, comment, value) {
51
- if (value.length === 0) return { nodes: setText(item, comment, value) };
52
- let templates = value.filter((item) => isFragment(item) && item.identifier != null);
53
- const next = templates.map((fragment) => fragment.identifier);
54
- const previous = item.fragments?.map((fragment) => fragment.identifier) ?? [];
55
- const nextSet = new Set(next);
56
- if (nextSet.size !== templates.length) templates = [];
57
- const noTemplates = templates.length === 0;
58
- if (noTemplates || item.nodes == null || previous.some((identifier) => identifier == null)) return {
59
- fragments: noTemplates ? void 0 : templates,
60
- nodes: setNodes(item, comment, noTemplates ? value.flatMap((item) => createNodes(item)) : templates.flatMap((template) => template.get()))
61
- };
78
+ if (value.length === 0) return removeArray(item, comment);
79
+ const { next, previous, template } = getArrayData(item, value);
80
+ if (template.empty || item.nodes == null || previous.array.some((identifier) => identifier == null)) {
81
+ const fragments = (item.fragments ?? []).slice();
82
+ const result = {
83
+ fragments: template.empty ? void 0 : template.items,
84
+ nodes: replaceNodes(item.nodes ?? [comment], template.empty ? value.flatMap((item) => createNodes(item)) : template.items.flatMap((template) => template.get()))
85
+ };
86
+ removeFragmentsItems(fragments);
87
+ return result;
88
+ }
62
89
  return handleArray({
63
- next: nextSet,
64
- previous: new Set(previous)
90
+ next,
91
+ previous
65
92
  }, {
66
- templates,
67
- fragments: item.fragments ?? []
93
+ fragments: item.fragments ?? [],
94
+ templates: template.items
68
95
  }, item.nodes);
69
96
  }
70
97
  function setNodes(item, comment, next) {
71
- if (item.nodes == null) replaceNodes([comment], next);
72
- else replaceNodes(item.nodes, next);
73
- removeFragments(item.fragments);
74
- return next;
98
+ return replaceNodes(item.nodes ?? [comment], next);
75
99
  }
76
100
  function setReactiveValue(data, comment, reactive) {
77
101
  let item = data.items.find((item) => item.nodes?.includes(comment));
78
102
  item ??= {};
79
- item.text = new Text();
80
103
  data.mora.subscribers.add(reactive.subscribe((value) => {
81
104
  if (Array.isArray(value)) setReactiveValueForArray(item, comment, value);
82
105
  else setReactiveValueForSingle(item, comment, value);
83
- item.nodes ??= [comment];
84
106
  }));
85
107
  }
86
108
  function setReactiveValueForArray(item, comment, value) {
@@ -89,25 +111,20 @@ function setReactiveValueForArray(item, comment, value) {
89
111
  item.nodes = nodes;
90
112
  }
91
113
  function setReactiveValueForSingle(item, comment, value) {
114
+ const fragments = (item.fragments ?? []).slice();
92
115
  const valueIsFragment = isFragment(value);
93
- item.fragments = valueIsFragment ? [value] : void 0;
94
116
  if (valueIsFragment || isChildNode(value)) item.nodes = setNodes(item, comment, createNodes(value));
95
117
  else item.nodes = setText(item, comment, value);
118
+ item.fragments = valueIsFragment ? [value] : void 0;
119
+ removeFragmentsItems(fragments);
96
120
  }
97
121
  function setText(item, comment, value) {
98
- const isNullable = isNullableOrWhitespace(value);
99
- if (item.text != null) item.text.textContent = isNullable ? "" : getString(value);
100
- let result = false;
101
- if (item.nodes != null) {
102
- replaceText(item, comment, isNullable);
103
- result = !isNullable;
104
- } else if (isNullable && comment.parentNode == null) item.text?.replaceWith(comment);
105
- else if (!isNullable && item?.text?.parentNode == null) {
106
- if (item.text != null) comment.replaceWith(item.text);
107
- result = true;
108
- }
109
- removeFragments(item.fragments);
110
- if (result) return item.text == null ? [] : [item.text];
122
+ const valueIsNullable = isNullableOrWhitespace(value);
123
+ item.text ??= new Text();
124
+ item.text.textContent = valueIsNullable ? "" : getString(value);
125
+ const result = valueIsNullable ? [comment] : [item.text];
126
+ replaceNodes(item.nodes ?? [comment], result);
127
+ return result;
111
128
  }
112
129
  //#endregion
113
130
  export { setReactiveValue };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/abydon",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "keywords": [
5
5
  "components",
6
6
  "dom",
@@ -44,7 +44,7 @@
44
44
  "dependencies": {
45
45
  "@oscarpalmer/atoms": "^0.185",
46
46
  "@oscarpalmer/mora": "^0.29",
47
- "@oscarpalmer/toretto": "^0.43"
47
+ "@oscarpalmer/toretto": "^0.45"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@oscarpalmer/oui": "^0.20",
@@ -1,30 +1,53 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {computed, isSignal} from '@oscarpalmer/mora';
2
+ import {isSignal} from '@oscarpalmer/mora';
3
+ import {isInputElement} from '@oscarpalmer/toretto/is';
3
4
  import {
4
5
  EVENT_ON_VALUE,
5
6
  EXPRESSION_ABYDON_ATTRIBUTE_PREFIX,
6
7
  EXPRESSION_ABYDON_CONTENT,
7
8
  EXPRESSION_EVENT_PREFIX,
8
- INPUT_TYPE_CHECKBOX,
9
- INPUT_TYPE_RADIO,
10
- PROPERTY_CHECKED,
11
9
  PROPERTY_VALUE,
12
10
  } from '../constants';
13
- import {isInputElement} from '../helpers/dom';
11
+ import {setComputedValue} from '../helpers';
14
12
  import type {FragmentData} from '../models';
15
13
  import {mapEvent} from '../node/event';
16
14
  import {setAttribute} from './value';
17
15
 
16
+ function compareAttributes(first: Attr, second: Attr): number {
17
+ return first.name.localeCompare(second.name);
18
+ }
19
+
18
20
  function getValue(data: FragmentData, original: string): unknown {
19
21
  const matches = EXPRESSION_ABYDON_CONTENT.exec(original);
20
22
 
21
23
  return matches == null ? original : data.values[+matches[1]];
22
24
  }
23
25
 
24
- export function mapAttributes(data: FragmentData, element: HTMLElement | SVGElement): void {
25
- const attributes = [...element.attributes].sort((first, second) =>
26
- first.name.localeCompare(second.name),
27
- );
26
+ export function mapAttributeValue(
27
+ data: FragmentData,
28
+ element: HTMLElement | SVGElement,
29
+ name: string,
30
+ value: unknown,
31
+ ): void {
32
+ if (typeof value === 'function') {
33
+ setComputedAttribute(data, element, name, value as GenericCallback);
34
+ } else {
35
+ setAttribute(data, element, name, value);
36
+ }
37
+
38
+ if (isInputElement(element) && isSignal(value) && name in element) {
39
+ mapEvent(element, EVENT_ON_VALUE, () => {
40
+ value.set(element[name as keyof typeof element]);
41
+ });
42
+ }
43
+ }
44
+
45
+ export function mapAttributes(
46
+ data: FragmentData,
47
+ element: HTMLElement | SVGElement,
48
+ ignoreValue: boolean,
49
+ ): void {
50
+ const attributes = [...element.attributes].sort(compareAttributes);
28
51
 
29
52
  const {length} = attributes;
30
53
 
@@ -32,52 +55,35 @@ export function mapAttributes(data: FragmentData, element: HTMLElement | SVGElem
32
55
  const {name, value} = attributes[index];
33
56
 
34
57
  const actualName = name.replace(EXPRESSION_ABYDON_ATTRIBUTE_PREFIX, '');
35
- const actualValue = getValue(data, value) as never;
58
+ const actualValue = getValue(data, value);
36
59
 
37
60
  if (actualName !== name) {
38
61
  element.removeAttribute(name);
39
62
  }
40
63
 
64
+ if (actualName === PROPERTY_VALUE && ignoreValue) {
65
+ continue;
66
+ }
67
+
41
68
  switch (true) {
42
69
  case EXPRESSION_EVENT_PREFIX.test(actualName):
43
70
  mapEvent(element, actualName, actualValue);
44
71
  break;
45
72
 
46
73
  default:
47
- mapValue(data, element, actualName, actualValue);
74
+ mapAttributeValue(data, element, actualName, actualValue);
48
75
  break;
49
76
  }
50
77
  }
51
78
  }
52
79
 
53
- function mapValue(
54
- data: FragmentData,
55
- element: HTMLElement | SVGElement,
56
- name: string,
57
- value: unknown,
58
- ): void {
59
- if (typeof value === 'function') {
60
- setComputedAttribute(data, element, name, value as GenericCallback);
61
- } else {
62
- setAttribute(data, element, name, value);
63
- }
64
-
65
- if (isInputElement(element) && isSignal(value) && name in element) {
66
- mapEvent(element, EVENT_ON_VALUE, () => {
67
- value.set(element[name as keyof typeof element]);
68
- });
69
- }
70
- }
71
-
72
80
  function setComputedAttribute(
73
81
  data: FragmentData,
74
82
  element: HTMLElement | SVGElement,
75
83
  name: string,
76
84
  callback: GenericCallback,
77
85
  ): void {
78
- const value = computed(callback);
79
-
80
- data.mora.values.add(value);
81
-
82
- setAttribute(data, element, name, value);
86
+ setComputedValue(data, callback, computation => {
87
+ setAttribute(data, element, name, computation);
88
+ });
83
89
  }
@@ -1,6 +1,5 @@
1
1
  import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
2
2
  import {getString} from '@oscarpalmer/atoms/string';
3
- import {camelCase} from '@oscarpalmer/atoms/string/case';
4
3
  import {isReactive} from '@oscarpalmer/mora';
5
4
  import {setAttribute as setTorettoAttribute} from '@oscarpalmer/toretto/attribute';
6
5
  import {setStyle} from '@oscarpalmer/toretto/style';
@@ -10,23 +9,21 @@ import {
10
9
  EXPRESSION_ATTRIBUTE_CLASS,
11
10
  EXPRESSION_ATTRIBUTE_STYLE_FULL,
12
11
  EXPRESSION_ATTRIBUTE_STYLE_PREFIX,
13
- EXPRESSION_ATTRIBUTE_STYLE_PROPERTY,
14
- PROPERTY_CHECKED,
15
- PROPERTY_VALUE,
12
+ EXPRESSION_ATTRIBUTE_STYLE_VARIABLE,
16
13
  VALUE_TRUE,
17
14
  } from '../constants';
18
15
  import type {FragmentData} from '../models';
19
16
 
20
- function getStyleValue(value: unknown, unit: string, isProperty: boolean): string | undefined {
21
- if (removeStyleValue(value, unit, isProperty)) {
17
+ function getStyleValue(value: unknown, unit: string, isVariable: boolean): string | undefined {
18
+ if (removeStyleValue(value, unit, isVariable)) {
22
19
  return;
23
20
  }
24
21
 
25
22
  return value === true || value === VALUE_TRUE ? unit : `${getString(value)}${unit ?? ''}`;
26
23
  }
27
24
 
28
- function removeStyleValue(value: unknown, unit: unknown, isProperty: boolean): boolean {
29
- if (value == null || value === false || (isProperty && isNullableOrWhitespace(value))) {
25
+ function removeStyleValue(value: unknown, unit: unknown, isVariable: boolean): boolean {
26
+ if (value == null || value === false || (isVariable && isNullableOrWhitespace(value))) {
30
27
  return true;
31
28
  }
32
29
 
@@ -60,21 +57,15 @@ function setClassValues(
60
57
  name: string,
61
58
  value: unknown,
62
59
  ): void {
63
- function update(value: unknown): void {
60
+ const classes = name.slice(ATTRIBUTE_CLASS_PREFIX_LENGTH).split(ATTRIBUTE_NAME_DELIMITER);
61
+
62
+ updateValue(data, value, value => {
64
63
  if (value === true || value === VALUE_TRUE) {
65
64
  element.classList.add(...classes);
66
65
  } else {
67
66
  element.classList.remove(...classes);
68
67
  }
69
- }
70
-
71
- const classes = name.slice(ATTRIBUTE_CLASS_PREFIX_LENGTH).split(ATTRIBUTE_NAME_DELIMITER);
72
-
73
- if (isReactive(value)) {
74
- data.mora.subscribers.add(value.subscribe(update));
75
- } else {
76
- update(value);
77
- }
68
+ });
78
69
  }
79
70
 
80
71
  function setStyleValues(
@@ -85,27 +76,15 @@ function setStyleValues(
85
76
  ): void {
86
77
  let [, property, unit] = EXPRESSION_ATTRIBUTE_STYLE_FULL.exec(name)!;
87
78
 
88
- const isProperty = EXPRESSION_ATTRIBUTE_STYLE_PROPERTY.test(name);
89
-
90
- property = camelCase(property);
79
+ const isVariable = EXPRESSION_ATTRIBUTE_STYLE_VARIABLE.test(name);
91
80
 
92
- if (isProperty) {
93
- property = `--${property}`;
94
- }
95
-
96
- function update(value: unknown): void {
81
+ updateValue(data, value, value => {
97
82
  setStyle(
98
83
  element,
99
84
  property as keyof CSSStyleDeclaration,
100
- getStyleValue(value, unit, isProperty),
85
+ getStyleValue(value, unit, isVariable),
101
86
  );
102
- }
103
-
104
- if (isReactive(value)) {
105
- data.mora.subscribers.add(value.subscribe(update));
106
- } else {
107
- update(value);
108
- }
87
+ });
109
88
  }
110
89
 
111
90
  function setValue(
@@ -114,27 +93,15 @@ function setValue(
114
93
  name: string,
115
94
  value: unknown,
116
95
  ): void {
117
- const isReactiveValue = isReactive(value);
118
-
119
- unsetValue(element, name, isReactiveValue ? value.peek() : value);
120
-
121
- if (isReactiveValue) {
122
- data.mora.subscribers.add(
123
- value.subscribe(next => {
124
- setTorettoAttribute(element, name, next);
125
- }),
126
- );
127
- } else {
96
+ updateValue(data, value, value => {
128
97
  setTorettoAttribute(element, name, value);
129
- }
98
+ });
130
99
  }
131
100
 
132
- function unsetValue(element: HTMLElement | SVGElement, name: string, value: unknown): void {
133
- if (name === PROPERTY_CHECKED) {
134
- (element as HTMLInputElement).checked = !(value === true || value === VALUE_TRUE);
135
- }
136
-
137
- if (name === PROPERTY_VALUE) {
138
- (element as HTMLInputElement).value = (element as HTMLInputElement).value === '' ? '_' : '';
101
+ function updateValue(data: FragmentData, value: unknown, updater: (value: unknown) => void): void {
102
+ if (isReactive(value)) {
103
+ data.mora.subscribers.add(value.subscribe(updater));
104
+ } else {
105
+ updater(value);
139
106
  }
140
107
  }
package/src/constants.ts CHANGED
@@ -4,7 +4,7 @@ export const ARRAY_COMPARISON_DISSIMILAR = 'dissimilar';
4
4
 
5
5
  export const ARRAY_COMPARISON_REMOVED = 'removed';
6
6
 
7
- export const ATTRIBUTE_CLASS_PREFIX_LENGTH = 6;
7
+ export const ATTRIBUTE_CLASS_PREFIX_LENGTH = 'class.'.length;
8
8
 
9
9
  export const ATTRIBUTE_NAME_DELIMITER = '.';
10
10
 
@@ -12,7 +12,7 @@ export const CHANGE_INPUTS = new Set(['checkbox', 'radio']);
12
12
 
13
13
  export const ERROR_FRAGMENT = 'Fragment function must return a Fragment instance';
14
14
 
15
- export const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<id>'";
15
+ export const ERROR_IDENTIFIER_DUPLICATE = "Duplicate identifier found: '<>'";
16
16
 
17
17
  export const ERROR_IDENTIFIER_TYPE = 'Identifier cannot be null or undefined';
18
18
 
@@ -47,34 +47,26 @@ export const EXPRESSION_ATTRIBUTE_STYLE_FULL = /^style\.([\w-]+)(?:\.([\w-]+))?$
47
47
 
48
48
  export const EXPRESSION_ATTRIBUTE_STYLE_PREFIX = /^style\./;
49
49
 
50
- export const EXPRESSION_ATTRIBUTE_STYLE_PROPERTY = /^style\.--/;
50
+ export const EXPRESSION_ATTRIBUTE_STYLE_VARIABLE = /^style\.--/;
51
51
 
52
52
  export const EXPRESSION_EVENT_ATTRIBUTE = /^@([\w-]+)(?::([a-z:]+))?="$/i;
53
53
 
54
54
  export const EXPRESSION_EVENT_NAME = /^@?([\w-]+)(?::([a-z:]+))?$/i;
55
55
 
56
- export const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(ctive)$/i;
56
+ export const EXPRESSION_EVENT_OPTIONS_ACTIVE = /^a(?:ctive)$/i;
57
57
 
58
- export const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(apture)$/i;
58
+ export const EXPRESSION_EVENT_OPTIONS_CAPTURE = /^c(?:apture)$/i;
59
59
 
60
- export const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(nce)$/i;
60
+ export const EXPRESSION_EVENT_OPTIONS_ONCE = /^o(?:nce)$/i;
61
61
 
62
62
  export const EXPRESSION_EVENT_PREFIX = /^@/;
63
63
 
64
- export const EXPRESSION_PERIOD = /\./;
65
-
66
64
  export const EXPRESSION_TEXTAREA_VALUE = /(?:<|&lt;)!--abydon\.(\d+)--(?:>|&gt;)/;
67
65
 
68
- export const INPUT_TYPE_CHECKBOX = 'checkbox';
69
-
70
- export const INPUT_TYPE_RADIO = 'radio';
71
-
72
66
  export const NAME_FRAGMENT = '$fragment';
73
67
 
74
68
  export const NAME_FRAGMENTS = '$fragments';
75
69
 
76
- export const PROPERTY_CHECKED = 'checked';
77
-
78
70
  export const PROPERTY_IDENTIFIER = 'identifier';
79
71
 
80
72
  export const PROPERTY_VALUE = 'value';
package/src/fragment.ts CHANGED
@@ -19,12 +19,14 @@ export class Fragment {
19
19
  /**
20
20
  * Is template caching enabled?
21
21
  */
22
- get caching(): boolean {
22
+ get cache(): boolean {
23
23
  return this.#configuration.cache;
24
24
  }
25
25
 
26
26
  /**
27
- * Fragment identifier
27
+ * Identifier for the _Fragment_
28
+ *
29
+ * _An identifier can be used to uniquely identify a Fragment, which helps prevent re-rendering in reactive arrays and Fragments_
28
30
  */
29
31
  get identifier(): unknown {
30
32
  return this.#configuration.identifier;
@@ -48,7 +50,15 @@ export class Fragment {
48
50
  }
49
51
 
50
52
  /**
51
- * Append the fragment to the given element
53
+ * Insert the _Fragment_ after the given element
54
+ * @param element Element to insert after
55
+ */
56
+ after(element: Element): void {
57
+ element.after(...this.get());
58
+ }
59
+
60
+ /**
61
+ * Append the _Fragment_ to the given element
52
62
  * @param element Element to append to
53
63
  */
54
64
  appendTo(element: Element): void {
@@ -56,9 +66,19 @@ export class Fragment {
56
66
  }
57
67
 
58
68
  /**
59
- * Configure the fragment
69
+ * Insert the _Fragment_ before the given element
70
+ * @param element Element to insert before
71
+ */
72
+ before(element: Element): void {
73
+ element.before(...this.get());
74
+ }
75
+
76
+ /**
77
+ * Configure the _Fragment_
78
+ *
79
+ * _Returns the Fragment instance for chaining_
60
80
  * @param configuration Configuration options
61
- * @returns Fragment
81
+ * @returns _Fragment_
62
82
  */
63
83
  configure(configuration: FragmentConfiguration): Fragment {
64
84
  const actual = isPlainObject(configuration) ? configuration : {};
@@ -75,7 +95,7 @@ export class Fragment {
75
95
  }
76
96
 
77
97
  /**
78
- * Get a list of the fragment's nodes
98
+ * Get a list of the _Fragment_'s nodes
79
99
  * @returns List of nodes
80
100
  */
81
101
  get(): ChildNode[] {
@@ -108,21 +128,18 @@ export class Fragment {
108
128
  }
109
129
 
110
130
  /**
111
- * Set an identifier for the fragment
112
- *
113
- * _An identifier can be used to uniquely identify a fragment,
114
- * which helps prevent re-rendering in certain scenarios._
115
- * @param identifier Identifier
116
- * @returns Fragment
131
+ * Prepend the _Fragment_ to the given element
132
+ * @param element Element to prepend to
117
133
  */
118
- identify(identifier: unknown): Fragment {
119
- this.#configuration.identifier = identifier;
120
-
121
- return this;
134
+ prependTo(element: Element): void {
135
+ element.prepend(...this.get());
122
136
  }
123
137
 
124
138
  /**
125
- * Remove the fragment from the DOM
139
+ * Remove the _Fragment_ _(and all its descendants)_ from the _DOM_
140
+ *
141
+ * - _Any events, reactive values, and Fragments will also be cleaned up and removed_
142
+ * - _After being removed, the Fragment can be re-inserted into the DOM_
126
143
  */
127
144
  remove(): void {
128
145
  removeFragment(this.#data);