bruh 2.0.0-beta.1 → 2.0.0-beta.3

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/browser.mjs CHANGED
@@ -8,21 +8,32 @@ const terminalBruhChildToNode = (child) => {
8
8
  else
9
9
  return document.createTextNode(child + "");
10
10
  };
11
+ const isBruhIterable = (x) => x != null && typeof x === "object" && !(x instanceof Node) && Symbol.iterator in x;
12
+ const ownedReactivesSymbol = /* @__PURE__ */ Symbol.for("bruh owned reactives");
11
13
  const reactiveTerminalBruhChildToNode = (child) => {
12
- let node = terminalBruhChildToNode(child.value);
14
+ const node = terminalBruhChildToNode(child.value);
15
+ let ownedReactives = node[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
16
+ ownedReactives.add(child);
17
+ let nodeWeakRef = new WeakRef(node);
13
18
  const stopReacting = child.addReaction(() => {
14
- if (!node.parentNode) {
19
+ const node2 = nodeWeakRef.deref();
20
+ if (!node2?.parentNode) {
15
21
  stopReacting();
16
22
  return;
17
23
  }
18
- const oldNode = node;
24
+ const oldNode = node2;
19
25
  const child_ = child;
20
- if (child.value != null && typeof child.value === "object" && Symbol.iterator in child.value) {
26
+ ownedReactives.delete(child);
27
+ if (isBruhIterable(child.value)) {
21
28
  stopReacting();
22
- oldNode.replaceWith(...reactiveIterableBruhChildToNodes(child_));
29
+ const nodes = reactiveIterableBruhChildToNodes(child_);
30
+ oldNode.replaceWith(...nodes);
23
31
  } else {
24
- node = terminalBruhChildToNode(child_.value);
25
- oldNode.replaceWith(node);
32
+ const node3 = terminalBruhChildToNode(child_.value);
33
+ ownedReactives = node3[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
34
+ ownedReactives.add(child);
35
+ nodeWeakRef = new WeakRef(node3);
36
+ oldNode.replaceWith(node3);
26
37
  }
27
38
  });
28
39
  return node;
@@ -30,24 +41,31 @@ const reactiveTerminalBruhChildToNode = (child) => {
30
41
  function* reactiveIterableBruhChildToNodes(child) {
31
42
  const first = document.createComment("[");
32
43
  const last = document.createComment("]");
44
+ let ownedReactives = first[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
45
+ ownedReactives.add(child);
46
+ let firstWeakRef = new WeakRef(first);
47
+ let lastWeakRef = new WeakRef(last);
33
48
  const stopReacting = child.addReaction(() => {
34
- if (!first.parentNode) {
49
+ const first2 = firstWeakRef.deref();
50
+ const last2 = lastWeakRef.deref();
51
+ if (!first2?.parentNode || !last2?.parentNode) {
35
52
  stopReacting();
36
53
  return;
37
54
  }
38
55
  const range = document.createRange();
39
- range.setStartAfter(first);
40
- if (child.value != null && typeof child.value === "object" && Symbol.iterator in child.value) {
56
+ range.setStartAfter(first2);
57
+ if (isBruhIterable(child.value)) {
41
58
  const child_ = child;
42
- range.setEndBefore(last);
59
+ range.setEndBefore(last2);
43
60
  range.deleteContents();
44
- first.after(...bruhChildrenToNodes(child_.value));
61
+ first2.after(...bruhChildrenToNodes(child_.value));
45
62
  } else {
63
+ ownedReactives.delete(child);
46
64
  const child_ = child;
47
65
  stopReacting();
48
- range.setEndAfter(last);
66
+ range.setEndAfter(last2);
49
67
  range.deleteContents();
50
- first.replaceWith(reactiveTerminalBruhChildToNode(child_));
68
+ first2.replaceWith(reactiveTerminalBruhChildToNode(child_));
51
69
  }
52
70
  });
53
71
  yield first;
@@ -58,12 +76,12 @@ function* bruhChildrenToNodes(children) {
58
76
  const partiallyFlattened = Array.isArray(children) ? children.flat(Infinity) : children;
59
77
  for (const child of partiallyFlattened) {
60
78
  if (!isReactive(child)) {
61
- if (child != null && typeof child === "object" && Symbol.iterator in child)
79
+ if (isBruhIterable(child))
62
80
  yield* bruhChildrenToNodes(child);
63
81
  else
64
82
  yield terminalBruhChildToNode(child);
65
83
  } else {
66
- if (child.value != null && typeof child.value === "object" && Symbol.iterator in child.value)
84
+ if (isBruhIterable(child.value))
67
85
  yield* reactiveIterableBruhChildToNodes(child);
68
86
  else
69
87
  yield reactiveTerminalBruhChildToNode(child);
@@ -75,32 +93,58 @@ const isElementWithStyle = (element) => (
75
93
  element.style instanceof CSSStyleDeclaration
76
94
  );
77
95
  const applyStyles = (element, styles) => {
96
+ const ownedReactives = element[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
97
+ let elementWeakRef = new WeakRef(element);
78
98
  for (const property in styles) {
79
99
  const property_ = property;
80
- reactiveDo(styles[property_], (value) => {
100
+ const maybeReactive = styles[property_];
101
+ if (isReactive(maybeReactive))
102
+ ownedReactives.add(maybeReactive);
103
+ reactiveDo(maybeReactive, (value) => {
104
+ const element2 = elementWeakRef.deref();
105
+ if (!element2)
106
+ return;
81
107
  if (value != null && typeof value !== "boolean")
82
- element.style.setProperty(property, value + "");
108
+ element2.style.setProperty(property, value + "");
83
109
  else
84
- element.style.removeProperty(property);
110
+ element2.style.removeProperty(property);
85
111
  });
86
112
  }
87
113
  };
88
114
  const applyClasses = (element, classes) => {
89
- for (const name in classes)
90
- reactiveDo(classes[name], (value) => {
91
- element.classList.toggle(name, value === true);
115
+ const ownedReactives = element[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
116
+ let elementWeakRef = new WeakRef(element);
117
+ for (const name in classes) {
118
+ const maybeReactive = classes[name];
119
+ if (isReactive(maybeReactive))
120
+ ownedReactives.add(maybeReactive);
121
+ reactiveDo(maybeReactive, (value) => {
122
+ const element2 = elementWeakRef.deref();
123
+ if (!element2)
124
+ return;
125
+ element2.classList.toggle(name, value === true);
92
126
  });
127
+ }
93
128
  };
94
129
  const applyAttributes = (element, attributes) => {
95
- for (const name in attributes)
130
+ const ownedReactives = element[ownedReactivesSymbol] ??= /* @__PURE__ */ new Set();
131
+ let elementWeakRef = new WeakRef(element);
132
+ for (const name in attributes) {
133
+ const maybeReactive = attributes[name];
134
+ if (isReactive(maybeReactive))
135
+ ownedReactives.add(maybeReactive);
96
136
  reactiveDo(attributes[name], (value) => {
137
+ const element2 = elementWeakRef.deref();
138
+ if (!element2)
139
+ return;
97
140
  if (typeof value === "boolean")
98
- element.toggleAttribute(name, value);
141
+ element2.toggleAttribute(name, value);
99
142
  else if (value != null)
100
- element.setAttribute(name, value + "");
143
+ element2.setAttribute(name, value + "");
101
144
  else
102
- element.removeAttribute(name);
145
+ element2.removeAttribute(name);
103
146
  });
147
+ }
104
148
  };
105
149
  const t = (textContent) => {
106
150
  if (!isReactive(textContent))
@@ -131,7 +175,7 @@ const jsx = (nameOrComponent, props_, key) => {
131
175
  if ("children" in props) {
132
176
  element.append(
133
177
  ...bruhChildrenToNodes(
134
- props.children != null && typeof props.children === "object" && Symbol.iterator in props.children ? props.children : [props.children]
178
+ isBruhIterable(props.children) ? props.children : [props.children]
135
179
  )
136
180
  );
137
181
  delete props.children;
@@ -170,5 +214,5 @@ class BruhText extends HTMLElement {
170
214
  }
171
215
  customElements.define("bruh-text", BruhText);
172
216
 
173
- export { BruhText, Fragment, applyAttributes, applyClasses, applyStyles, bruhChildrenToNodes, jsx, t };
217
+ export { BruhText, Fragment, applyAttributes, applyClasses, applyStyles, bruhChildrenToNodes, jsx, ownedReactivesSymbol, t };
174
218
  //# sourceMappingURL=browser.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.mjs","sources":["../src/dom/index.browser.mts"],"sourcesContent":["import { isReactive, reactiveDo } from \"../reactive/index.mts\"\nimport type { Reactive, MaybeReactive } from \"../reactive/index.mts\"\nimport type {\n PropertyWiseOr,\n LikelyAsString,\n LikelyAsAbsent,\n LikelyAsBoolean\n} from \"./types.mts\"\nimport type { PropertiesHyphen as Styles } from \"csstype\"\nimport type {\n ElementType,\n\n HTMLTag,\n SVGTag,\n MathMLTag,\n\n Namespace,\n HTMLNamespace,\n SVGNamespace,\n MathMLNamespace,\n\n ElementToEventMap,\n ElementToAttributes\n} from \"html-info\"\n\nexport type TerminalBruhChild =\n | Node\n | LikelyAsString\n | LikelyAsAbsent\n\nexport type TerminalBruhChildOutputNode<Child extends TerminalBruhChild>\n = Child extends Node\n ? Child\n : Child extends LikelyAsString\n ? Text\n : Child extends LikelyAsAbsent\n ? Comment\n : Node\n\n// disallow a reactive from directly holding a reactive\nexport type FlatBruhChild =\n | MaybeReactive<TerminalBruhChild>\n | Reactive<TerminalBruhChild | Iterable<BruhChild>>\n\nexport type BruhChild =\n | FlatBruhChild\n | Iterable<BruhChild>\n\nexport type StylesToApply = {\n [Property in keyof Styles]: MaybeReactive<Styles[Property] | LikelyAsAbsent>\n}\n\nexport interface ClassesToApply {\n [className: string]: MaybeReactive<LikelyAsBoolean>\n}\n\nexport type AttributesToApply<\n Name extends string,\n NS extends Namespace = HTMLNamespace\n> = {\n [Attribute in keyof ElementToAttributes<Name, NS>]?:\n MaybeReactive<ElementToAttributes<Name, NS>[Attribute] | LikelyAsBoolean>\n}\n\ntype EventMapToListenerProps<EventMap> = {\n [E in ((keyof EventMap) & string) as `on${E}`]:\n (event: EventMap[E]) => void\n}\n\ntype AttributeMapToProps<AttributeMap> = {\n [Attribute in keyof AttributeMap]:\n MaybeReactive<AttributeMap[Attribute] | LikelyAsBoolean>\n | ( Attribute extends \"style\"\n ? StylesToApply\n : Attribute extends \"class\"\n ? ClassesToApply\n : never\n )\n}\n\nexport interface BruhOptions {\n namespace?: Namespace\n}\n\ninterface BaseBruhProps {\n bruh?: BruhOptions,\n children?: BruhChild\n}\n\nexport type BruhProps<\n Name extends string,\n NS extends string = HTMLNamespace\n>\n = BaseBruhProps\n & (\n NS extends HTMLNamespace\n ? { bruh?: { namespace?: HTMLNamespace } }\n : { bruh: { namespace: NS } }\n )\n & Partial<\n EventMapToListenerProps<ElementToEventMap<Name, NS>> &\n AttributeMapToProps<ElementToAttributes<Name, NS>>\n >\n\nexport type HTMLTagToBruhProps = { [Name in HTMLTag]: BruhProps<Name, HTMLNamespace> }\nexport type SVGTagToBruhProps = { [Name in SVGTag]: BruhProps<Name, SVGNamespace> }\nexport type MathMLTagToBruhProps = { [Name in MathMLTag]: BruhProps<Name, MathMLNamespace> }\n\nexport type TagToBruhProps =\n PropertyWiseOr<\n HTMLTagToBruhProps,\n PropertyWiseOr<\n SVGTagToBruhProps,\n MathMLTagToBruhProps\n >\n >\n\nexport namespace JSX {\n /** @see https://www.typescriptlang.org/docs/handbook/jsx.html#children-type-checking */\n export interface ElementChildrenAttribute { children: {} }\n\n /** @see https://www.typescriptlang.org/docs/handbook/jsx.html#attribute-type-checking */\n export interface IntrinsicElements extends TagToBruhProps {}\n\n export type Element = any\n\n export type ElementType = HTMLTag | SVGTag | MathMLTag | ((props: any) => Element)\n}\n\n// Coerces input into a DOM node, if it isn't already one\nconst terminalBruhChildToNode: {\n <Child extends TerminalBruhChild>(child: Child): TerminalBruhChildOutputNode<Child>\n} = (child: TerminalBruhChild): any => {\n // Existing DOM nodes are untouched\n if (child instanceof Node)\n return child\n // Nullish and booleans are ignored\n else if (child == null || typeof child === \"boolean\")\n return document.createComment(child + \"\")\n // Anything else is treated as text\n else\n return document.createTextNode(child + \"\")\n}\n\n// Auto-swapping single reactive node\nconst reactiveTerminalBruhChildToNode: {\n <Child extends TerminalBruhChild>(child: Reactive<Child>): TerminalBruhChildOutputNode<Child>\n} = (child: Reactive<TerminalBruhChild>): any => {\n let node = terminalBruhChildToNode(child.value)\n\n const stopReacting = child.addReaction(() => {\n // Stop swapping if no longer possible\n if (!node.parentNode) {\n stopReacting()\n return\n }\n\n const oldNode = node as typeof node & ChildNode\n const child_ = child as Reactive<BruhChild>\n\n // If an iterable now, stop swapping, then switch to reactive iterable swapping\n if (\n child.value != null &&\n typeof child.value === \"object\" &&\n Symbol.iterator in child.value\n ) {\n stopReacting()\n oldNode.replaceWith(...reactiveIterableBruhChildToNodes(child_ as Reactive<Iterable<BruhChild>>))\n }\n // Normal swap\n else {\n node = terminalBruhChildToNode(child_.value as TerminalBruhChild)\n oldNode.replaceWith(node)\n }\n })\n\n return node\n}\n\n// Auto-swapping reactive iterable of nodes\nfunction * reactiveIterableBruhChildToNodes(child: Reactive<Iterable<BruhChild>>): IterableIterator<Node> {\n // Markers owned by the swapper here itself, so that\n // the values in the iterable can be swapped separately\n const first = document.createComment(\"[\")\n const last = document.createComment(\"]\")\n\n const stopReacting = child.addReaction(() => {\n // Stop swapping if there is no parent to swap within\n if (!first.parentNode) {\n stopReacting()\n return\n }\n\n // Make a range starting after the first marker\n const range = document.createRange()\n range.setStartAfter(first)\n\n // Normal swap, replacing content between the first and last markers\n if (\n child.value != null &&\n typeof child.value === \"object\" &&\n Symbol.iterator in child.value\n ) {\n const child_ = child as Reactive<Iterable<BruhChild>>\n\n range.setEndBefore(last)\n range.deleteContents()\n first.after(...bruhChildrenToNodes(child_.value))\n }\n // Switch to single swapping node by replacing everything\n else {\n const child_ = child as unknown as Reactive<TerminalBruhChild>\n\n stopReacting()\n range.setEndAfter(last)\n range.deleteContents()\n first.replaceWith(reactiveTerminalBruhChildToNode(child_))\n }\n })\n\n yield first\n yield* bruhChildrenToNodes(child.value)\n yield last\n}\n\n// Processes bruh children into an iterable of DOM nodes\n// Reactive values are automatically replaced, so the output must be placed into a parent node\n// before any top level (after flattening iterables) reactions run\nexport function * bruhChildrenToNodes(children: Iterable<BruhChild>): IterableIterator<Node> {\n const partiallyFlattened =\n Array.isArray(children)\n ? children.flat<BruhChild, number>(Infinity)\n : children\n\n for (const child of partiallyFlattened) {\n if (!isReactive(child)) {\n if (\n child != null &&\n typeof child === \"object\" &&\n Symbol.iterator in child\n )\n yield* bruhChildrenToNodes(child)\n else\n yield terminalBruhChildToNode(child)\n }\n else {\n if (\n child.value != null &&\n typeof child.value === \"object\" &&\n Symbol.iterator in child.value\n )\n yield* reactiveIterableBruhChildToNodes(child as Reactive<Iterable<BruhChild>>)\n else\n yield reactiveTerminalBruhChildToNode(child as Reactive<TerminalBruhChild>)\n }\n }\n}\n\n//#endregion\n\n//#region Reactive-aware element helper functions e.g. applyAttributes()\n\ntype ElementWithStyle = HTMLElement | SVGElement | MathMLElement\n\n// https://w3c.github.io/csswg-drafts/cssom/#the-elementcssinlinestyle-mixin\nconst isElementWithStyle = <T extends Element>(element: T): element is (T & ElementWithStyle) =>\n // @ts-ignore\n element.style instanceof CSSStyleDeclaration\n\n/**\n * Style attribute rules from an object with\n * potentially reactive and/or set as absent values\n */\nexport const applyStyles = <E extends ElementWithStyle>(\n element: E,\n styles: StylesToApply\n) => {\n for (const property in styles) {\n const property_ = property as keyof StylesToApply\n reactiveDo(styles[property_], value => {\n if (value != null && typeof value !== \"boolean\")\n element.style.setProperty (property, value + \"\")\n else\n element.style.removeProperty(property)\n })\n }\n}\n\n/**\n * Class list from an object mapping from\n * class names to potentially reactive booleans\n */\nexport const applyClasses = (\n element: Element,\n classes: ClassesToApply\n) => {\n for (const name in classes)\n reactiveDo(classes[name], value => {\n // without coercing to a boolean, `undefined` would toggle instead of forcing removal\n element.classList.toggle(name, value === true)\n })\n}\n\n/**\n * Attributes from an object with\n * potentially reactive and/or set as absent values\n */\nexport const applyAttributes = <\n Name extends string,\n NS extends Namespace = HTMLNamespace\n>(\n element: ElementType<Name, NS>,\n attributes: AttributesToApply<Name, NS>\n) => {\n for (const name in attributes)\n reactiveDo<ElementToAttributes<Name, NS>[typeof name] | LikelyAsBoolean>(attributes[name], value => {\n if (typeof value === \"boolean\")\n element.toggleAttribute(name, value)\n else if (value != null)\n element.setAttribute (name, value + \"\")\n else\n element.removeAttribute(name)\n })\n}\n\n//#endregion\n\n//#region t()\n\n// Text nodes\nexport const t = (textContent: MaybeReactive<LikelyAsString>) => {\n // Non-reactive values are just text nodes\n if (!isReactive(textContent))\n return document.createTextNode(textContent + \"\")\n\n // Reactive values auto-update the node's text content\n const node = document.createTextNode(textContent.value + \"\")\n textContent.addReaction(() => {\n node.textContent = textContent.value + \"\"\n })\n return node\n}\n\n//#endregion\n\n//#region JSX integration\n\ndeclare global {\n interface String {\n startsWith<Prefix extends string>(\n prefix: Prefix,\n position?: 0\n ): this is `${Prefix}${string}`\n }\n}\n\nexport const jsx: {\n /**\n * Create an HTML element\n */\n <\n Name extends HTMLTag\n >(\n name: Name,\n props: HTMLTagToBruhProps[Name],\n key?: string\n ):\n HTMLElementTagNameMap[Name]\n\n /**\n * Create an SVG element\n */\n <\n Name extends SVGTag\n >(\n name: Name,\n props: SVGTagToBruhProps[Name],\n key?: string\n ):\n SVGElementTagNameMap[Name]\n\n /**\n * Create a MathML element\n */\n <\n Name extends MathMLTag\n >(\n name: Name,\n props: MathMLTagToBruhProps[Name],\n key?: string\n ):\n MathMLElementTagNameMap[Name]\n\n /**\n * Create an element\n */\n <\n Name extends string,\n NS extends Namespace = HTMLNamespace\n >(\n name: Name,\n props: BruhProps<Name, NS>,\n key?: string\n ):\n ElementType<Name, NS>\n\n /**\n * Call a function as a JSX component\n */\n <\n Props extends Record<any, unknown>,\n Result\n >(\n component: (props: Props) => Result,\n props: Props,\n key?: string\n ): Result\n} =\n<\n Name extends string,\n NS extends Namespace = HTMLNamespace\n>\n(\n nameOrComponent: Name | Function,\n props_: Record<any, unknown>,\n key?: string\n) => {\n if (key !== undefined)\n props_.key = key\n\n // It must be a component, as bruh components are just functions\n // Due to JSX, this would mean a function with only one parameter - props\n // This object includes all of the normal props and a \"children\" key\n if (typeof nameOrComponent !== \"string\") {\n const component = nameOrComponent\n const props = props_ as Record<any, unknown>\n\n return component(props)\n }\n\n const name = nameOrComponent\n const props = props_ as BruhProps<Name, NS>\n\n // Extract explicit options from the bruh prop\n let options: BruhOptions = {}\n if (typeof props.bruh === \"object\" && !isReactive(props.bruh)) {\n options = props.bruh\n delete props.bruh\n }\n const { namespace } = options\n\n // Make an element with optional namespace\n const element =\n namespace\n ? document.createElementNS(namespace, name) as ElementType<Name, NS>\n : document.createElement ( name) as ElementType<Name, NS>\n\n if (\"children\" in props) {\n element.append(\n ...bruhChildrenToNodes(\n (\n props.children != null &&\n typeof props.children === \"object\" &&\n Symbol.iterator in props.children\n )\n ? props.children\n : [props.children]\n )\n )\n\n delete props.children\n }\n\n // Apply overloaded props, if possible\n\n // Inline style object\n if (\n \"style\" in props &&\n props.style != null &&\n typeof props.style === \"object\" &&\n !isReactive(props.style) &&\n isElementWithStyle(element)\n ) {\n applyStyles(element, props.style)\n delete props.style\n }\n\n // Classes object\n if (\n \"class\" in props &&\n props.class != null &&\n typeof props.class === \"object\" &&\n !isReactive(props.class)\n ) {\n applyClasses(element, props.class)\n delete props.class\n }\n\n for (const name_ in props) {\n const name = name_ as string & (keyof typeof props)\n // Event listener functions\n if (typeof props[name] === \"function\" && name.startsWith(\"on\")) {\n element.addEventListener(name.slice(2), props[name] as any)\n delete props[name]\n }\n }\n\n // The rest of the props are attributes\n applyAttributes(element, props as AttributesToApply<Name, NS>)\n\n return element\n}\n\nexport const Fragment =\n <Children extends BruhChild>\n (props: { children: Children }) => props.children\n\n//#endregion\n\n\nexport class BruhText extends HTMLElement {\n static hydrated: { readonly [tag: string]: ReadonlySet<Text> } = {}\n\n constructor() {\n super()\n\n const textNode = document.createTextNode(this.textContent!)\n\n const tag = this.getAttribute(\"tag\")\n if (tag) {\n const set = (BruhText.hydrated as { [tag: string]: Set<Text> })[tag] ??= new Set()\n set.add(textNode)\n }\n\n this.replaceWith(textNode)\n }\n}\n\ncustomElements.define(\"bruh-text\", BruhText)\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"bruh-text\": BruhText\n }\n}\ndeclare module \"html-info\" {\n interface HTMLTagToAttributes {\n \"bruh-text\": {\n \"tag\"?: string\n }\n }\n}\n"],"names":["props","name"],"mappings":"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"}
1
+ {"version":3,"file":"browser.mjs","sources":["../src/dom/index.browser.mts"],"sourcesContent":["import { isReactive, reactiveDo } from \"../reactive/index.mts\"\nimport type { Reactive, MaybeReactive } from \"../reactive/index.mts\"\nimport type {\n PropertyWiseOr,\n LikelyAsString,\n LikelyAsAbsent,\n LikelyAsBoolean\n} from \"./types.mts\"\nimport type { PropertiesHyphen as Styles } from \"csstype\"\nimport type {\n ElementType,\n\n HTMLTag,\n SVGTag,\n MathMLTag,\n\n Namespace,\n HTMLNamespace,\n SVGNamespace,\n MathMLNamespace,\n\n ElementToEventMap,\n ElementToAttributes\n} from \"html-info\"\n\nexport type TerminalBruhChild =\n | Node\n | LikelyAsString\n | LikelyAsAbsent\n\nexport type TerminalBruhChildOutputNode<Child extends TerminalBruhChild>\n = Child extends Node\n ? Child\n : Child extends LikelyAsString\n ? Text\n : Child extends LikelyAsAbsent\n ? Comment\n : Node\n\n// disallow a reactive from directly holding a reactive\nexport type FlatBruhChild =\n | MaybeReactive<TerminalBruhChild>\n | Reactive<TerminalBruhChild | Iterable<BruhChild>>\n\nexport type BruhChild =\n | FlatBruhChild\n | Iterable<BruhChild>\n\nexport type StylesToApply = {\n [Property in keyof Styles]: MaybeReactive<Styles[Property] | LikelyAsAbsent>\n}\n\nexport interface ClassesToApply {\n [className: string]: MaybeReactive<LikelyAsBoolean>\n}\n\nexport type AttributesToApply<\n Name extends string,\n NS extends Namespace = HTMLNamespace\n> = {\n [Attribute in keyof ElementToAttributes<Name, NS>]?:\n MaybeReactive<ElementToAttributes<Name, NS>[Attribute] | LikelyAsBoolean>\n}\n\ntype EventMapToListenerProps<EventMap> = {\n [E in ((keyof EventMap) & string) as `on${E}`]:\n (event: EventMap[E]) => void\n}\n\ntype AttributeMapToProps<AttributeMap> = {\n [Attribute in keyof AttributeMap]:\n MaybeReactive<AttributeMap[Attribute] | LikelyAsBoolean>\n | ( Attribute extends \"style\"\n ? StylesToApply\n : Attribute extends \"class\"\n ? ClassesToApply\n : never\n )\n}\n\nexport interface BruhOptions {\n namespace?: Namespace\n}\n\ninterface BaseBruhProps {\n bruh?: BruhOptions,\n children?: BruhChild\n}\n\nexport type BruhProps<\n Name extends string,\n NS extends string = HTMLNamespace\n>\n = BaseBruhProps\n & (\n NS extends HTMLNamespace\n ? { bruh?: { namespace?: HTMLNamespace } }\n : { bruh: { namespace: NS } }\n )\n & Partial<\n EventMapToListenerProps<ElementToEventMap<Name, NS>> &\n AttributeMapToProps<ElementToAttributes<Name, NS>>\n >\n\nexport type HTMLTagToBruhProps = { [Name in HTMLTag]: BruhProps<Name, HTMLNamespace> }\nexport type SVGTagToBruhProps = { [Name in SVGTag]: BruhProps<Name, SVGNamespace> }\nexport type MathMLTagToBruhProps = { [Name in MathMLTag]: BruhProps<Name, MathMLNamespace> }\n\nexport type TagToBruhProps =\n PropertyWiseOr<\n HTMLTagToBruhProps,\n PropertyWiseOr<\n SVGTagToBruhProps,\n MathMLTagToBruhProps\n >\n >\n\nexport namespace JSX {\n /** @see https://www.typescriptlang.org/docs/handbook/jsx.html#children-type-checking */\n export interface ElementChildrenAttribute { children: {} }\n\n /** @see https://www.typescriptlang.org/docs/handbook/jsx.html#attribute-type-checking */\n export interface IntrinsicElements extends TagToBruhProps {}\n\n export type Element = any\n\n export type ElementType = HTMLTag | SVGTag | MathMLTag | ((props: any) => Element)\n}\n\n// Coerces input into a DOM node, if it isn't already one\nconst terminalBruhChildToNode: {\n <Child extends TerminalBruhChild>(child: Child): TerminalBruhChildOutputNode<Child>\n} = (child: TerminalBruhChild): any => {\n // Existing DOM nodes are untouched\n if (child instanceof Node)\n return child\n // Nullish and booleans are ignored\n else if (child == null || typeof child === \"boolean\")\n return document.createComment(child + \"\")\n // Anything else is treated as text\n else\n return document.createTextNode(child + \"\")\n}\n\nconst isBruhIterable = (x: unknown): x is Iterable<BruhChild> =>\n x != null &&\n typeof x === \"object\" &&\n !(x instanceof Node) &&\n Symbol.iterator in x\n\nexport const ownedReactivesSymbol = Symbol.for(\"bruh owned reactives\")\n\ndeclare global {\n interface Node {\n [ownedReactivesSymbol]?: Set<Reactive<unknown>>\n }\n}\n\n// Auto-swapping single reactive node\nconst reactiveTerminalBruhChildToNode: {\n <Child extends TerminalBruhChild>(child: Reactive<Child>): TerminalBruhChildOutputNode<Child>\n} = (child: Reactive<TerminalBruhChild>): any => {\n const node = terminalBruhChildToNode(child.value)\n\n let ownedReactives = node[ownedReactivesSymbol] ??= new Set()\n ownedReactives.add(child)\n\n let nodeWeakRef = new WeakRef(node)\n\n const stopReacting = child.addReaction(() => {\n const node = nodeWeakRef.deref()\n\n // Stop swapping if no longer possible\n if (!node?.parentNode) {\n stopReacting()\n return\n }\n\n const oldNode = node as typeof node & ChildNode\n const child_ = child as Reactive<BruhChild>\n\n ownedReactives.delete(child)\n\n // If an iterable now, stop swapping, then switch to reactive iterable swapping\n if (isBruhIterable(child.value)) {\n stopReacting()\n const nodes = reactiveIterableBruhChildToNodes(child_ as Reactive<Iterable<BruhChild>>)\n oldNode.replaceWith(...nodes)\n }\n // Normal swap\n else {\n const node = terminalBruhChildToNode(child_.value as TerminalBruhChild)\n ownedReactives = node[ownedReactivesSymbol] ??= new Set()\n ownedReactives.add(child)\n nodeWeakRef = new WeakRef(node)\n oldNode.replaceWith(node)\n }\n })\n\n return node\n}\n\n// Auto-swapping reactive iterable of nodes\nfunction * reactiveIterableBruhChildToNodes(child: Reactive<Iterable<BruhChild>>): IterableIterator<Node> {\n // Markers owned by the swapper here itself, so that\n // the values in the iterable can be swapped separately\n const first = document.createComment(\"[\")\n const last = document.createComment(\"]\")\n\n let ownedReactives = first[ownedReactivesSymbol] ??= new Set()\n ownedReactives.add(child)\n\n let firstWeakRef = new WeakRef(first)\n let lastWeakRef = new WeakRef(last)\n\n const stopReacting = child.addReaction(() => {\n const first = firstWeakRef.deref()\n const last = lastWeakRef.deref()\n\n // Stop swapping if there is no parent to swap within\n if (!first?.parentNode || !last?.parentNode) {\n stopReacting()\n return\n }\n\n // Make a range starting after the first marker\n const range = document.createRange()\n range.setStartAfter(first)\n\n // Normal swap, replacing content between the first and last markers\n if (isBruhIterable(child.value)) {\n const child_ = child as Reactive<Iterable<BruhChild>>\n\n range.setEndBefore(last)\n range.deleteContents()\n first.after(...bruhChildrenToNodes(child_.value))\n }\n // Switch to single swapping node by replacing everything\n else {\n ownedReactives.delete(child)\n\n const child_ = child as unknown as Reactive<TerminalBruhChild>\n\n stopReacting()\n range.setEndAfter(last)\n range.deleteContents()\n first.replaceWith(reactiveTerminalBruhChildToNode(child_))\n }\n })\n\n yield first\n yield* bruhChildrenToNodes(child.value)\n yield last\n}\n\n// Processes bruh children into an iterable of DOM nodes\n// Reactive values are automatically replaced, so the output must be placed into a parent node\n// before any top level (after flattening iterables) reactions run\nexport function * bruhChildrenToNodes(children: Iterable<BruhChild>): IterableIterator<Node> {\n const partiallyFlattened =\n Array.isArray(children)\n ? children.flat<BruhChild, number>(Infinity)\n : children\n\n for (const child of partiallyFlattened) {\n if (!isReactive(child)) {\n if (isBruhIterable(child))\n yield* bruhChildrenToNodes(child)\n else\n yield terminalBruhChildToNode(child)\n }\n else {\n if (isBruhIterable(child.value))\n yield* reactiveIterableBruhChildToNodes(child as Reactive<Iterable<BruhChild>>)\n else\n yield reactiveTerminalBruhChildToNode(child as Reactive<TerminalBruhChild>)\n }\n }\n}\n\n//#endregion\n\n//#region Reactive-aware element helper functions e.g. applyAttributes()\n\ntype ElementWithStyle = HTMLElement | SVGElement | MathMLElement\n\n// https://w3c.github.io/csswg-drafts/cssom/#the-elementcssinlinestyle-mixin\nconst isElementWithStyle = <T extends Element>(element: T): element is (T & ElementWithStyle) =>\n // @ts-ignore\n element.style instanceof CSSStyleDeclaration\n\n/**\n * Style attribute rules from an object with\n * potentially reactive and/or set as absent values\n */\nexport const applyStyles = <E extends ElementWithStyle>(\n element: E,\n styles: StylesToApply\n) => {\n const ownedReactives = element[ownedReactivesSymbol] ??= new Set()\n let elementWeakRef = new WeakRef(element)\n\n for (const property in styles) {\n const property_ = property as keyof StylesToApply\n const maybeReactive = styles[property_]\n if (isReactive(maybeReactive))\n ownedReactives.add(maybeReactive)\n\n reactiveDo(maybeReactive, value => {\n const element = elementWeakRef.deref()\n if (!element)\n return\n\n if (value != null && typeof value !== \"boolean\")\n element.style.setProperty (property, value + \"\")\n else\n element.style.removeProperty(property)\n })\n }\n}\n\n/**\n * Class list from an object mapping from\n * class names to potentially reactive booleans\n */\nexport const applyClasses = (\n element: Element,\n classes: ClassesToApply\n) => {\n const ownedReactives = element[ownedReactivesSymbol] ??= new Set()\n let elementWeakRef = new WeakRef(element)\n\n for (const name in classes) {\n const maybeReactive = classes[name]\n if (isReactive(maybeReactive))\n ownedReactives.add(maybeReactive)\n\n reactiveDo(maybeReactive, value => {\n const element = elementWeakRef.deref()\n if (!element)\n return\n\n // without coercing to a boolean, `undefined` would toggle instead of forcing removal\n element.classList.toggle(name, value === true)\n })\n }\n}\n\n/**\n * Attributes from an object with\n * potentially reactive and/or set as absent values\n */\nexport const applyAttributes = <\n Name extends string,\n NS extends Namespace = HTMLNamespace\n>(\n element: ElementType<Name, NS>,\n attributes: AttributesToApply<Name, NS>\n) => {\n const ownedReactives = element[ownedReactivesSymbol] ??= new Set()\n let elementWeakRef = new WeakRef(element)\n\n for (const name in attributes) {\n const maybeReactive = attributes[name]\n if (isReactive(maybeReactive))\n ownedReactives.add(maybeReactive)\n\n reactiveDo<ElementToAttributes<Name, NS>[typeof name] | LikelyAsBoolean>(attributes[name], value => {\n const element = elementWeakRef.deref()\n if (!element)\n return\n\n if (typeof value === \"boolean\")\n element.toggleAttribute(name, value)\n else if (value != null)\n element.setAttribute (name, value + \"\")\n else\n element.removeAttribute(name)\n })\n }\n}\n\n//#endregion\n\n//#region t()\n\n// Text nodes\nexport const t = (textContent: MaybeReactive<LikelyAsString>) => {\n // Non-reactive values are just text nodes\n if (!isReactive(textContent))\n return document.createTextNode(textContent + \"\")\n\n // Reactive values auto-update the node's text content\n const node = document.createTextNode(textContent.value + \"\")\n textContent.addReaction(() => {\n node.textContent = textContent.value + \"\"\n })\n return node\n}\n\n//#endregion\n\n//#region JSX integration\n\ndeclare global {\n interface String {\n startsWith<Prefix extends string>(\n prefix: Prefix,\n position?: 0\n ): this is `${Prefix}${string}`\n }\n}\n\nexport const jsx: {\n /**\n * Create an HTML element\n */\n <\n Name extends HTMLTag\n >(\n name: Name,\n props: HTMLTagToBruhProps[Name],\n key?: string\n ):\n HTMLElementTagNameMap[Name]\n\n /**\n * Create an SVG element\n */\n <\n Name extends SVGTag\n >(\n name: Name,\n props: SVGTagToBruhProps[Name],\n key?: string\n ):\n SVGElementTagNameMap[Name]\n\n /**\n * Create a MathML element\n */\n <\n Name extends MathMLTag\n >(\n name: Name,\n props: MathMLTagToBruhProps[Name],\n key?: string\n ):\n MathMLElementTagNameMap[Name]\n\n /**\n * Create an element\n */\n <\n Name extends string,\n NS extends Namespace = HTMLNamespace\n >(\n name: Name,\n props: BruhProps<Name, NS>,\n key?: string\n ):\n ElementType<Name, NS>\n\n /**\n * Call a function as a JSX component\n */\n <\n Props extends Record<any, unknown>,\n Result\n >(\n component: (props: Props) => Result,\n props: Props,\n key?: string\n ): Result\n} =\n<\n Name extends string,\n NS extends Namespace = HTMLNamespace\n>\n(\n nameOrComponent: Name | Function,\n props_: Record<any, unknown>,\n key?: string\n) => {\n if (key !== undefined)\n props_.key = key\n\n // It must be a component, as bruh components are just functions\n // Due to JSX, this would mean a function with only one parameter - props\n // This object includes all of the normal props and a \"children\" key\n if (typeof nameOrComponent !== \"string\") {\n const component = nameOrComponent\n const props = props_ as Record<any, unknown>\n\n return component(props)\n }\n\n const name = nameOrComponent\n const props = props_ as BruhProps<Name, NS>\n\n // Extract explicit options from the bruh prop\n let options: BruhOptions = {}\n if (typeof props.bruh === \"object\" && !isReactive(props.bruh)) {\n options = props.bruh\n delete props.bruh\n }\n const { namespace } = options\n\n // Make an element with optional namespace\n const element =\n namespace\n ? document.createElementNS(namespace, name) as ElementType<Name, NS>\n : document.createElement ( name) as ElementType<Name, NS>\n\n if (\"children\" in props) {\n element.append(\n ...bruhChildrenToNodes(\n isBruhIterable(props.children)\n ? props.children\n : [props.children]\n )\n )\n\n delete props.children\n }\n\n // Apply overloaded props, if possible\n\n // Inline style object\n if (\n \"style\" in props &&\n props.style != null &&\n typeof props.style === \"object\" &&\n !isReactive(props.style) &&\n isElementWithStyle(element)\n ) {\n applyStyles(element, props.style)\n delete props.style\n }\n\n // Classes object\n if (\n \"class\" in props &&\n props.class != null &&\n typeof props.class === \"object\" &&\n !isReactive(props.class)\n ) {\n applyClasses(element, props.class)\n delete props.class\n }\n\n for (const name_ in props) {\n const name = name_ as string & (keyof typeof props)\n // Event listener functions\n if (typeof props[name] === \"function\" && name.startsWith(\"on\")) {\n element.addEventListener(name.slice(2), props[name] as any)\n delete props[name]\n }\n }\n\n // The rest of the props are attributes\n applyAttributes(element, props as AttributesToApply<Name, NS>)\n\n return element\n}\n\nexport const Fragment =\n <Children extends BruhChild>\n (props: { children: Children }) => props.children\n\n//#endregion\n\n\nexport class BruhText extends HTMLElement {\n static hydrated: { readonly [tag: string]: ReadonlySet<Text> } = {}\n\n constructor() {\n super()\n\n const textNode = document.createTextNode(this.textContent!)\n\n const tag = this.getAttribute(\"tag\")\n if (tag) {\n const set = (BruhText.hydrated as { [tag: string]: Set<Text> })[tag] ??= new Set()\n set.add(textNode)\n }\n\n this.replaceWith(textNode)\n }\n}\n\ncustomElements.define(\"bruh-text\", BruhText)\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"bruh-text\": BruhText\n }\n}\ndeclare module \"html-info\" {\n interface HTMLTagToAttributes {\n \"bruh-text\": {\n \"tag\"?: string\n }\n }\n}\n"],"names":["node","first","last","element","props","name"],"mappings":"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"}
@@ -58,6 +58,12 @@ export declare namespace JSX {
58
58
  type Element = any;
59
59
  type ElementType = HTMLTag | SVGTag | MathMLTag | ((props: any) => Element);
60
60
  }
61
+ export declare const ownedReactivesSymbol: unique symbol;
62
+ declare global {
63
+ interface Node {
64
+ [ownedReactivesSymbol]?: Set<Reactive<unknown>>;
65
+ }
66
+ }
61
67
  export declare function bruhChildrenToNodes(children: Iterable<BruhChild>): IterableIterator<Node>;
62
68
  type ElementWithStyle = HTMLElement | SVGElement | MathMLElement;
63
69
  /**
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "library",
11
11
  "modern"
12
12
  ],
13
- "version": "2.0.0-beta.1",
13
+ "version": "2.0.0-beta.3",
14
14
  "license": "MIT",
15
15
  "author": {
16
16
  "name": "Daniel Ethridge",