@esportsplus/template 0.40.2 → 0.40.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.
@@ -126,7 +126,46 @@ function generateTemplateCode(ctx, { html, slots }, exprTexts, exprNodes, templa
126
126
  for (let j = 0, m = names.length; j < m; j++) {
127
127
  let name = names[j];
128
128
  if (name === TYPES.Attributes) {
129
- code.push(`${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`);
129
+ let exprNode = exprNodes[index];
130
+ if (exprNode && ts.isObjectLiteralExpression(exprNode)) {
131
+ let canExpand = true, props = exprNode.properties;
132
+ for (let k = 0, p = props.length; k < p; k++) {
133
+ let prop = props[k];
134
+ if (ts.isSpreadAssignment(prop) ||
135
+ (ts.isPropertyAssignment(prop) && ts.isComputedPropertyName(prop.name)) ||
136
+ (ts.isShorthandPropertyAssignment(prop) && prop.objectAssignmentInitializer)) {
137
+ canExpand = false;
138
+ break;
139
+ }
140
+ }
141
+ if (canExpand) {
142
+ for (let k = 0, p = props.length; k < p; k++) {
143
+ let prop = props[k];
144
+ if (ts.isPropertyAssignment(prop)) {
145
+ let propName = ts.isIdentifier(prop.name)
146
+ ? prop.name.text
147
+ : ts.isStringLiteral(prop.name)
148
+ ? prop.name.text
149
+ : null;
150
+ if (propName) {
151
+ code.push(generateAttributeBinding(element, propName, rewriteExpression(ctx, prop.initializer), ''));
152
+ }
153
+ }
154
+ else if (ts.isShorthandPropertyAssignment(prop)) {
155
+ code.push(generateAttributeBinding(element, prop.name.text, prop.name.text, ''));
156
+ }
157
+ else if (ts.isMethodDeclaration(prop) && ts.isIdentifier(prop.name)) {
158
+ code.push(generateAttributeBinding(element, prop.name.text, printer.printNode(ts.EmitHint.Expression, prop, ctx.sourceFile), ''));
159
+ }
160
+ }
161
+ }
162
+ else {
163
+ code.push(`${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`);
164
+ }
165
+ }
166
+ else {
167
+ code.push(`${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`);
168
+ }
130
169
  index++;
131
170
  }
132
171
  else {
@@ -1,10 +1,10 @@
1
- import { Element } from '../types.js';
1
+ import { Attributes, Element } from '../types.js';
2
2
  import onconnect from './onconnect.js';
3
3
  import onresize from './onresize.js';
4
4
  import ontick from './ontick.js';
5
- declare const delegate: (element: Element, event: string, listener: Function) => void;
6
- declare const on: (element: Element, event: string, listener: Function) => void;
7
- declare const ondisconnect: (element: Element, listener: Function) => void;
8
- declare const onrender: (element: Element, listener: Function) => void;
9
- declare const runtime: (element: Element, event: `on${string}`, listener: Function) => void;
5
+ declare const delegate: <E extends string>(element: Element, event: E, listener: Attributes[`on${E}`]) => void;
6
+ declare const on: <E extends string>(element: Element, event: E, listener: Attributes[`on${E}`]) => void;
7
+ declare const ondisconnect: (element: Element, listener: NonNullable<Attributes[`ondisconnect`]>) => void;
8
+ declare const onrender: (element: Element, listener: NonNullable<Attributes[`onrender`]>) => void;
9
+ declare const runtime: <E extends `on${string}`>(element: Element, event: E, listener: Attributes[E]) => void;
10
10
  export { delegate, on, onconnect, ondisconnect, onrender, onresize, ontick, runtime };
@@ -1,3 +1,3 @@
1
- import { Element } from '../types.js';
2
- declare const _default: (element: Element, listener: Function) => void;
1
+ import { Attributes, Element } from '../types.js';
2
+ declare const _default: (element: Element, listener: NonNullable<Attributes["onconnect"]>) => void;
3
3
  export default _default;
@@ -1,3 +1,3 @@
1
- import { Element } from '../types.js';
2
- declare const _default: (element: Element, listener: Function) => void;
1
+ import { Attributes, Element } from '../types.js';
2
+ declare const _default: (element: Element, listener: NonNullable<Attributes["onresize"]>) => void;
3
3
  export default _default;
@@ -1,6 +1,6 @@
1
- import { Element } from '../types.js';
1
+ import { Attributes, Element } from '../types.js';
2
2
  declare const add: (task: VoidFunction) => void;
3
3
  declare const remove: (task: VoidFunction) => void;
4
- declare const _default: (element: Element, listener: Function) => void;
4
+ declare const _default: (element: Element, listener: NonNullable<Attributes["ontick"]>) => void;
5
5
  export default _default;
6
6
  export { add, remove };
package/package.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "author": "ICJR",
3
3
  "dependencies": {
4
4
  "@esportsplus/queue": "^0.2.0",
5
- "@esportsplus/reactivity": "^0.29.11",
6
- "@esportsplus/typescript": "^0.27.2",
5
+ "@esportsplus/reactivity": "^0.29.12",
6
+ "@esportsplus/typescript": "^0.27.3",
7
7
  "@esportsplus/utilities": "^0.27.2",
8
8
  "serve": "^14.2.5"
9
9
  },
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "type": "module",
41
41
  "types": "./build/index.d.ts",
42
- "version": "0.40.2",
42
+ "version": "0.40.4",
43
43
  "scripts": {
44
44
  "build": "tsc",
45
45
  "build:test": "vite build --config test/vite.config.ts",
@@ -233,9 +233,83 @@ function generateTemplateCode(
233
233
  let name = names[j];
234
234
 
235
235
  if (name === TYPES.Attributes) {
236
- code.push(
237
- `${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`
238
- );
236
+ let exprNode = exprNodes[index];
237
+
238
+ // Object literals can be expanded at compile time
239
+ if (exprNode && ts.isObjectLiteralExpression(exprNode)) {
240
+ let canExpand = true,
241
+ props = exprNode.properties;
242
+
243
+ // Check if all properties can be statically analyzed
244
+ for (let k = 0, p = props.length; k < p; k++) {
245
+ let prop = props[k];
246
+
247
+ if (
248
+ ts.isSpreadAssignment(prop) ||
249
+ (ts.isPropertyAssignment(prop) && ts.isComputedPropertyName(prop.name)) ||
250
+ (ts.isShorthandPropertyAssignment(prop) && prop.objectAssignmentInitializer)
251
+ ) {
252
+ canExpand = false;
253
+ break;
254
+ }
255
+ }
256
+
257
+ if (canExpand) {
258
+ for (let k = 0, p = props.length; k < p; k++) {
259
+ let prop = props[k];
260
+
261
+ if (ts.isPropertyAssignment(prop)) {
262
+ let propName = ts.isIdentifier(prop.name)
263
+ ? prop.name.text
264
+ : ts.isStringLiteral(prop.name)
265
+ ? prop.name.text
266
+ : null;
267
+
268
+ if (propName) {
269
+ code.push(
270
+ generateAttributeBinding(
271
+ element,
272
+ propName,
273
+ rewriteExpression(ctx, prop.initializer),
274
+ ''
275
+ )
276
+ );
277
+ }
278
+ }
279
+ else if (ts.isShorthandPropertyAssignment(prop)) {
280
+ code.push(
281
+ generateAttributeBinding(
282
+ element,
283
+ prop.name.text,
284
+ prop.name.text,
285
+ ''
286
+ )
287
+ );
288
+ }
289
+ else if (ts.isMethodDeclaration(prop) && ts.isIdentifier(prop.name)) {
290
+ code.push(
291
+ generateAttributeBinding(
292
+ element,
293
+ prop.name.text,
294
+ printer.printNode(ts.EmitHint.Expression, prop, ctx.sourceFile),
295
+ ''
296
+ )
297
+ );
298
+ }
299
+ }
300
+ }
301
+ else {
302
+ code.push(
303
+ `${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`
304
+ );
305
+ }
306
+ }
307
+ else {
308
+ code.push(
309
+ `${NAMESPACE}.setProperties(${element}, ${exprTexts[index] || 'undefined'});`
310
+ );
311
+ }
312
+
239
313
  index++;
240
314
  }
241
315
  else {
@@ -1,6 +1,6 @@
1
1
  import { root } from '@esportsplus/reactivity';
2
2
  import { defineProperty } from '@esportsplus/utilities';
3
- import { Element } from '~/types';
3
+ import { Attributes, Element } from '~/types';
4
4
  import { ondisconnect as disconnect } from '~/slot/cleanup';
5
5
  import onconnect from './onconnect';
6
6
  import onresize from './onresize';
@@ -88,13 +88,13 @@ function register(element: Element, event: string) {
88
88
  }
89
89
 
90
90
 
91
- const delegate = (element: Element, event: string, listener: Function): void => {
91
+ const delegate = <E extends string>(element: Element, event: E, listener: Attributes[`on${E}`]): void => {
92
92
  element[ keys[event] || register(element, event) ] = listener;
93
93
  };
94
94
 
95
95
  // DIRECT_ATTACH_EVENTS in ./constants.ts tells compiler to use this function
96
- const on = (element: Element, event: string, listener: Function): void => {
97
- let handler = (e: Event) => listener.call(element, e);
96
+ const on = <E extends string>(element: Element, event: E, listener: Attributes[`on${E}`]): void => {
97
+ let handler = (e: Event) => (listener as Function).call(element, e);
98
98
 
99
99
  element.addEventListener(event, handler, {
100
100
  passive: passive.has(event)
@@ -105,31 +105,31 @@ const on = (element: Element, event: string, listener: Function): void => {
105
105
  });
106
106
  };
107
107
 
108
- const ondisconnect = (element: Element, listener: Function) => {
108
+ const ondisconnect = (element: Element, listener: NonNullable<Attributes[`ondisconnect`]>) => {
109
109
  disconnect(element, () => listener(element));
110
110
  };
111
111
 
112
- const onrender = (element: Element, listener: Function) => {
112
+ const onrender = (element: Element, listener: NonNullable<Attributes[`onrender`]>) => {
113
113
  root(() => listener(element));
114
114
  };
115
115
 
116
116
  const lifecycle = { onconnect, ondisconnect, onrender, onresize, ontick };
117
117
 
118
- const runtime = (element: Element, event: `on${string}`, listener: Function): void => {
118
+ const runtime = <E extends `on${string}`>(element: Element, event: E, listener: Attributes[E]): void => {
119
119
  let key = event.toLowerCase();
120
120
 
121
121
  if (LIFECYCLE_EVENTS.has(key)) {
122
- lifecycle[key as keyof typeof lifecycle](element, listener);
122
+ lifecycle[key as keyof typeof lifecycle](element, listener as any);
123
123
  return;
124
124
  }
125
125
 
126
126
  let e = event.slice(2).toLowerCase();
127
127
 
128
128
  if (DIRECT_ATTACH_EVENTS.has(key)) {
129
- on(element, e, listener);
129
+ on(element, e, listener as Attributes[`on${typeof e}`]);
130
130
  }
131
131
  else {
132
- delegate(element, e, listener);
132
+ delegate(element, e, listener as Attributes[`on${typeof e}`]);
133
133
  }
134
134
  };
135
135
 
@@ -1,9 +1,9 @@
1
1
  import { root } from '@esportsplus/reactivity';
2
2
  import { add, remove } from './ontick';
3
- import { Element } from '~/types';
3
+ import { Attributes, Element } from '~/types';
4
4
 
5
5
 
6
- export default (element: Element, listener: Function) => {
6
+ export default (element: Element, listener: NonNullable<Attributes['onconnect']>) => {
7
7
  let fn = () => {
8
8
  retry--;
9
9
 
@@ -1,5 +1,5 @@
1
1
  import { onCleanup } from '@esportsplus/reactivity';
2
- import { Element } from '~/types';
2
+ import { Attributes, Element } from '~/types';
3
3
 
4
4
 
5
5
  let listeners = new Map<Element, Function>(),
@@ -23,7 +23,7 @@ function onresize() {
23
23
  }
24
24
 
25
25
 
26
- export default (element: Element, listener: Function) => {
26
+ export default (element: Element, listener: NonNullable<Attributes['onresize']>) => {
27
27
  listeners.set(element, listener);
28
28
 
29
29
  onCleanup(() => {
@@ -1,4 +1,4 @@
1
- import { Element } from '~/types';
1
+ import { Attributes, Element } from '~/types';
2
2
  import { raf } from '~/utilities';
3
3
 
4
4
 
@@ -33,7 +33,7 @@ const remove = (task: VoidFunction) => {
33
33
  };
34
34
 
35
35
 
36
- export default (element: Element, listener: Function) => {
36
+ export default (element: Element, listener: NonNullable<Attributes['ontick']>) => {
37
37
  let connected = false,
38
38
  fn = () => {
39
39
  if (connected === false) {
package/test/range.ts ADDED
@@ -0,0 +1,44 @@
1
+ import { reactive, root } from '@esportsplus/reactivity';
2
+ import { html, type Attributes } from '@esportsplus/template';
3
+ // import form from '~/components/form';
4
+
5
+
6
+ export default function(
7
+ this: { attributes?: Attributes } | any,
8
+ attributes: Attributes & { max: number, min: number, state?: { active: boolean, error: string, value: number } }
9
+ ) {
10
+ let { max, min } = attributes,
11
+ state = attributes.state || reactive({
12
+ active: false,
13
+ error: '',
14
+ value: 0
15
+ });
16
+
17
+ if (attributes?.value) {
18
+ state.value = Number( attributes.value );
19
+ }
20
+
21
+ // @ts-ignore
22
+ return html`
23
+ <input
24
+ class='range --border-state --border-black'
25
+ style='${() => `--thumb-position: ${((state.value - min) / (max - min)) * 100}%`}'
26
+ type='range'
27
+ ${this?.attributes}
28
+ ${attributes}
29
+ ${{
30
+ class: () => state.active && '--active',
31
+ onfocusin: () => {
32
+ state.active = true;
33
+ },
34
+ onfocusout: () => {
35
+ state.active = false;
36
+ },
37
+ oninput: (e: any) => {
38
+ state.value = Number((e.target as HTMLInputElement).value);
39
+ },
40
+ value: root(() => (attributes?.value as number) || state.value || 0)
41
+ }}
42
+ />
43
+ `;
44
+ };
@@ -14,6 +14,7 @@ export default defineConfig({
14
14
  'events': resolve(__dirname, 'events.ts'),
15
15
  'imported-values': resolve(__dirname, 'imported-values.ts'),
16
16
  'nested': resolve(__dirname, 'nested.ts'),
17
+ 'range': resolve(__dirname, 'range.ts'),
17
18
  'slots': resolve(__dirname, 'slots.ts'),
18
19
  'spread': resolve(__dirname, 'spread.ts'),
19
20
  'static': resolve(__dirname, 'static.ts'),