@xtia/jel 0.2.1 → 0.3.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.
package/README.md CHANGED
@@ -5,9 +5,12 @@ Jel is a thin layer over the DOM to simplify element structure creation, manipul
5
5
 
6
6
  See [demo/index.ts](https://github.com/tiadrop/jel-ts/blob/main/demo/index.ts) for example operation. Compare with [resulting page](https://aleta.codes/jel-ts-demo/).
7
7
 
8
- ## `$` basic use:
8
+ ## `$` Basic Use:
9
9
 
10
10
  `$.[tagname](details)` produces an element of `<tagname>`. `details` can be content of various types or a descriptor object.
11
+ ```
12
+ $ npm i @xtia/jel
13
+ ```
11
14
 
12
15
  ```ts
13
16
  import { $ } from "@xtia/jel";
@@ -44,4 +47,85 @@ body.append([
44
47
  )
45
48
  ])
46
49
 
50
+ ```
51
+
52
+ ## `DOMContent`
53
+
54
+ Content can be string, Text, HTMLElement, JelEntity or arbitrarily nested array of content. Typing as DOMContent where possible enables flexibility.
55
+
56
+ ```ts
57
+ function showDialogue(content: DOMContent) => {
58
+ const element = $.div({
59
+ classes: "dialogue",
60
+ content: [
61
+ content,
62
+ $.div({
63
+ classes: "buttons",
64
+ // content: [...]
65
+ })
66
+ ]
67
+ });
68
+ // ...
69
+ }
70
+
71
+ interface Job {
72
+ name: string;
73
+ completionMessage: DOMContent;
74
+ }
75
+
76
+ showDialogue("Hello, world");
77
+ showDialogue(["Hello, ", $.i("world")]);
78
+ showDialogue([
79
+ $.h2(`${job.name} Complete`),
80
+ $.p(job.completionMessage),
81
+ ]);
82
+ ```
83
+
84
+ ## `ElementClassDescriptor`
85
+
86
+ Element classes can be specified as string, `{ [className]: boolean }` and arbitrarily nested array thereof.
87
+
88
+ ```ts
89
+ function renderFancyButton(
90
+ caption: DOMContent,
91
+ onClick: () => void,
92
+ classes: ElementClassDefinition = []
93
+ ) {
94
+ return $.button({
95
+ content: caption,
96
+ classes: ["fancy-button", classes],
97
+ // ...
98
+ });
99
+ }
100
+
101
+ function showDialogue(content: DOMContent, danger: boolean = false) {
102
+ const element = $.div({
103
+ // ...
104
+ classes: "dialogue",
105
+ content: [
106
+ content,
107
+ renderFancyButton("OK", close, ["ok-button", { danger }]),
108
+ ]
109
+ });
110
+ // ...
111
+ }
112
+ ```
113
+
114
+ ## Jel-Wrapped Elements
115
+
116
+ Jel wraps its elements in an interface for common operations plus an `append()` method that accepts `DOMContent`.
117
+
118
+ For other operations the element is accessible via `ent.element`:
119
+
120
+ ```ts
121
+ const button = $.button();
122
+ const rect = button.element.getBoundingClientRect();
123
+ ```
124
+
125
+ ## Shorthand
126
+
127
+ If you need an element with just a class, id and/or content you can use `tag#id.classes` notation, ie `$("div#someId.class1.class2", content?)`.
128
+
129
+ ```ts
130
+ showDialogue(["Hello ", $("span.green", "world")]);
47
131
  ```
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type ElementClassSpec = string | Record<string, boolean> | ElementClassSpec[];
1
+ export type ElementClassDescriptor = string | Record<string, boolean> | ElementClassDescriptor[];
2
2
  export type DOMContent = number | null | string | Element | JelEntity<object, any> | Text | DOMContent[];
3
3
  export type DomEntity<T extends HTMLElement> = JelEntity<ElementAPI<T>, HTMLElementEventMap>;
4
4
  type JelConstructor<Spec, API, EventDataMap> = (spec?: Partial<Spec & CommonOptions<EventDataMap>>) => JelEntity<API, EventDataMap>;
@@ -11,17 +11,18 @@ type JelEntity<API, EventDataMap> = API & {
11
11
  on<E extends keyof EventDataMap>(eventId: E, handler: (this: JelEntity<API, EventDataMap>, data: EventDataMap[E]) => void): void;
12
12
  readonly [componentDataSymbol]: JelComponentData;
13
13
  };
14
- type Styles = Partial<{
15
- [key in keyof CSSStyleDeclaration]: string | number;
16
- }> & Record<`--${string}`, string | number>;
14
+ type StylesDescriptor = Partial<{
15
+ [key in keyof CSSStyleDeclaration]: CSSValue;
16
+ }>;
17
17
  interface ElementDescriptor {
18
- classes?: ElementClassSpec;
18
+ classes?: ElementClassDescriptor;
19
19
  content?: DOMContent;
20
20
  attribs?: Record<string, string | number | boolean>;
21
21
  on?: Partial<{
22
22
  [E in keyof HTMLElementEventMap]: (event: HTMLElementEventMap[E]) => void;
23
23
  }>;
24
- style?: Styles;
24
+ style?: StylesDescriptor;
25
+ cssVariables?: Record<string, CSSValue>;
25
26
  }
26
27
  type DomHelper = ((<T extends keyof HTMLElementTagNameMap>(tagName: T, descriptor: ElementDescriptor) => DomEntity<HTMLElementTagNameMap[T]>) & (<T extends keyof HTMLElementTagNameMap>(selector: `${T}#${string}`, content?: DOMContent) => DomEntity<HTMLElementTagNameMap[T]>) & (<T extends keyof HTMLElementTagNameMap>(selector: `${T}.${string}`, content?: DOMContent) => DomEntity<HTMLElementTagNameMap[T]>) & (<T extends keyof HTMLElementTagNameMap>(selector: T, content?: DOMContent) => DomEntity<HTMLElementTagNameMap[T]>) & (<T extends HTMLElement>(element: T) => DomEntity<T>) & {
27
28
  [T in keyof HTMLElementTagNameMap]: (descriptor: ElementDescriptor) => DomEntity<HTMLElementTagNameMap[T]>;
@@ -31,6 +32,7 @@ type DomHelper = ((<T extends keyof HTMLElementTagNameMap>(tagName: T, descripto
31
32
  type JelComponentData = {
32
33
  dom: DOMContent;
33
34
  };
35
+ type CSSValue = string | number;
34
36
  type ElementAPI<T extends HTMLElement> = {
35
37
  readonly element: T;
36
38
  content: DOMContent;
@@ -38,12 +40,16 @@ type ElementAPI<T extends HTMLElement> = {
38
40
  attribs: {
39
41
  [key: string]: string | null;
40
42
  };
41
- style: CSSStyleDeclaration;
43
+ style: StylesDescriptor & ((styles: StylesDescriptor) => void);
42
44
  innerHTML: string;
45
+ setCSSVariable(table: Record<string, CSSValue>): void;
46
+ setCSSVariable(variableName: string, value: CSSValue): void;
43
47
  qsa(selector: string): DomEntity<any>[];
44
48
  append(...content: DOMContent[]): void;
45
49
  remove(): void;
46
- };
50
+ } & (T extends HTMLInputElement ? {
51
+ value: string;
52
+ } : {});
47
53
  type OptionalKeys<T> = {
48
54
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
49
55
  }[keyof T];
package/index.js CHANGED
@@ -13,6 +13,22 @@ var __assign = (this && this.__assign) || function () {
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.$ = void 0;
15
15
  exports.definePart = definePart;
16
+ var elementProxy = {
17
+ get: function (style, prop) {
18
+ return style[prop];
19
+ },
20
+ set: function (style, prop, value) {
21
+ style[prop] = value;
22
+ return true;
23
+ },
24
+ apply: function (style, _, _a) {
25
+ var styles = _a[0];
26
+ Object.entries(styles).forEach(function (_a) {
27
+ var prop = _a[0], val = _a[1];
28
+ return style[prop] = val;
29
+ });
30
+ },
31
+ };
16
32
  function createElement(tag, descriptor) {
17
33
  if (descriptor === void 0) { descriptor = {}; }
18
34
  if (isContent(descriptor))
@@ -42,17 +58,18 @@ function createElement(tag, descriptor) {
42
58
  ent.element.setAttribute(k, v === true ? k : v);
43
59
  });
44
60
  }
45
- if (descriptor.content)
61
+ if (descriptor.content !== undefined)
46
62
  recursiveAppend(ent.element, descriptor.content);
47
63
  if (descriptor.style) {
48
64
  Object.entries(descriptor.style).forEach(function (_a) {
49
65
  var prop = _a[0], val = _a[1];
50
- if (/\-/.test(prop)) {
51
- ent.element.style.setProperty(prop, val.toString());
52
- }
53
- else {
54
- ent.element.style[prop] = val;
55
- }
66
+ ent.element.style[prop] = val;
67
+ });
68
+ }
69
+ if (descriptor.cssVariables) {
70
+ Object.entries(descriptor.cssVariables).forEach(function (_a) {
71
+ var prop = _a[0], val = _a[1];
72
+ ent.element.style.setProperty("--" + prop, val);
56
73
  });
57
74
  }
58
75
  if (descriptor.on) {
@@ -156,6 +173,16 @@ function getWrappedElement(element) {
156
173
  _a.remove = function () {
157
174
  element.remove();
158
175
  },
176
+ _a.setCSSVariable = function (variableNameOrTable, value) {
177
+ if (typeof variableNameOrTable == "object") {
178
+ Object.entries(variableNameOrTable).forEach(function (_a) {
179
+ var k = _a[0], v = _a[1];
180
+ element.style.setProperty("--" + k, v);
181
+ });
182
+ return;
183
+ }
184
+ element.style.setProperty("--" + variableNameOrTable, value);
185
+ },
159
186
  _a.classes = element.classList,
160
187
  _a.qsa = function (selector) {
161
188
  return [].slice.call(element.querySelectorAll(selector)).map(function (el) { return getWrappedElement(el); });
@@ -186,7 +213,17 @@ function getWrappedElement(element) {
186
213
  enumerable: false,
187
214
  configurable: true
188
215
  }),
189
- _a.style = element.style,
216
+ Object.defineProperty(_a, "value", {
217
+ get: function () {
218
+ return element.value;
219
+ },
220
+ set: function (v) {
221
+ element.value = v;
222
+ },
223
+ enumerable: false,
224
+ configurable: true
225
+ }),
226
+ _a.style = new Proxy(element.style, elementProxy),
190
227
  _a);
191
228
  elementWrapCache.set(element, domEntity_1);
192
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xtia/jel",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "repository": {
5
5
  "url": "https://github.com/tiadrop/jel-ts",
6
6
  "type": "github"