@matthewp/zebra 0.0.1 → 0.0.2

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/list.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { View } from './view.ts';
2
+ type ViewConstructor = new () => View;
3
+ export declare class List<T = Record<string, unknown>> {
4
+ private ViewClass;
5
+ private keyFn;
6
+ private views;
7
+ private keys;
8
+ private container;
9
+ constructor(ViewClass: ViewConstructor, keyFn: (item: T) => unknown);
10
+ template(items?: T[]): string;
11
+ mount(container: HTMLElement, items?: T[]): void;
12
+ private updateView;
13
+ update(items: T[]): void;
14
+ }
15
+ export {};
16
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,KAAK,eAAe,GAAG,UAAU,IAAI,CAAC;AAEtC,qBAAa,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3C,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,SAAS,CAA4B;gBAEjC,SAAS,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO;IAKnE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,GAAG,MAAM;IAQ7B,KAAK,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE;IAczC,OAAO,CAAC,UAAU;IAIlB,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE;CAuGlB"}
package/dist/list.js ADDED
@@ -0,0 +1,140 @@
1
+ export class List {
2
+ ViewClass;
3
+ keyFn;
4
+ views = [];
5
+ keys = [];
6
+ container = null;
7
+ constructor(ViewClass, keyFn) {
8
+ this.ViewClass = ViewClass;
9
+ this.keyFn = keyFn;
10
+ }
11
+ template(items) {
12
+ if (!items || items.length === 0)
13
+ return '';
14
+ return items.map(item => {
15
+ let view = new this.ViewClass();
16
+ return view.template(item);
17
+ }).join('');
18
+ }
19
+ mount(container, items) {
20
+ this.container = container;
21
+ let children = Array.from(container.children);
22
+ if (children.length > 0 && items) {
23
+ for (let i = 0; i < children.length && i < items.length; i++) {
24
+ let view = new this.ViewClass();
25
+ view.mount(children[i]);
26
+ view.update(items[i]);
27
+ this.views.push(view);
28
+ this.keys.push(this.keyFn(items[i]));
29
+ }
30
+ }
31
+ }
32
+ updateView(view, item) {
33
+ view.update(item);
34
+ }
35
+ update(items) {
36
+ let container = this.container;
37
+ let oldViews = this.views;
38
+ let oldKeys = this.keys;
39
+ let newKeys = items.map(this.keyFn);
40
+ let newViews = new Array(items.length).fill(null);
41
+ let oldHead = 0;
42
+ let oldTail = oldViews.length - 1;
43
+ let newHead = 0;
44
+ let newTail = items.length - 1;
45
+ let oldKeyToIndex;
46
+ while (oldHead <= oldTail && newHead <= newTail) {
47
+ if (oldViews[oldHead] === null) {
48
+ oldHead++;
49
+ }
50
+ else if (oldViews[oldTail] === null) {
51
+ oldTail--;
52
+ }
53
+ else if (oldKeys[oldHead] === newKeys[newHead]) {
54
+ // Head-Head match
55
+ newViews[newHead] = oldViews[oldHead];
56
+ this.updateView(oldViews[oldHead], items[newHead]);
57
+ oldHead++;
58
+ newHead++;
59
+ }
60
+ else if (oldKeys[oldTail] === newKeys[newTail]) {
61
+ // Tail-Tail match
62
+ newViews[newTail] = oldViews[oldTail];
63
+ this.updateView(oldViews[oldTail], items[newTail]);
64
+ oldTail--;
65
+ newTail--;
66
+ }
67
+ else if (oldKeys[oldHead] === newKeys[newTail]) {
68
+ // Head-Tail match: move old head to after old tail
69
+ newViews[newTail] = oldViews[oldHead];
70
+ this.updateView(oldViews[oldHead], items[newTail]);
71
+ oldViews[oldTail].el.after(oldViews[oldHead].el);
72
+ oldHead++;
73
+ newTail--;
74
+ }
75
+ else if (oldKeys[oldTail] === newKeys[newHead]) {
76
+ // Tail-Head match: move old tail to before old head
77
+ newViews[newHead] = oldViews[oldTail];
78
+ this.updateView(oldViews[oldTail], items[newHead]);
79
+ oldViews[oldHead].el.before(oldViews[oldTail].el);
80
+ oldTail--;
81
+ newHead++;
82
+ }
83
+ else {
84
+ // Build map lazily
85
+ if (!oldKeyToIndex) {
86
+ oldKeyToIndex = new Map();
87
+ for (let i = oldHead; i <= oldTail; i++) {
88
+ if (oldViews[i] !== null) {
89
+ oldKeyToIndex.set(oldKeys[i], i);
90
+ }
91
+ }
92
+ }
93
+ let oldIndex = oldKeyToIndex.get(newKeys[newHead]);
94
+ if (oldIndex === undefined) {
95
+ // New item
96
+ let view = new this.ViewClass();
97
+ view.createAndMount();
98
+ newViews[newHead] = view;
99
+ oldViews[oldHead].el.before(view.el);
100
+ this.updateView(view, items[newHead]);
101
+ }
102
+ else {
103
+ // Move existing
104
+ let view = oldViews[oldIndex];
105
+ this.updateView(view, items[newHead]);
106
+ newViews[newHead] = view;
107
+ oldViews[oldHead].el.before(view.el);
108
+ oldViews[oldIndex] = null;
109
+ }
110
+ newHead++;
111
+ }
112
+ }
113
+ // Remove remaining old items
114
+ while (oldHead <= oldTail) {
115
+ if (oldViews[oldHead] !== null) {
116
+ oldViews[oldHead].el.remove();
117
+ }
118
+ oldHead++;
119
+ }
120
+ // Add remaining new items
121
+ while (newHead <= newTail) {
122
+ let view = new this.ViewClass();
123
+ view.createAndMount();
124
+ newViews[newHead] = view;
125
+ let el = view.el;
126
+ let ref = newViews[newTail + 1]?.el;
127
+ if (ref) {
128
+ ref.before(el);
129
+ }
130
+ else {
131
+ container.append(el);
132
+ }
133
+ this.updateView(view, items[newHead]);
134
+ newHead++;
135
+ }
136
+ this.views = newViews;
137
+ this.keys = newKeys;
138
+ }
139
+ }
140
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { View } from './view.ts';
2
+ export declare function renderToString(view: View, props?: Record<string, unknown>): string;
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAElF"}
package/dist/server.js ADDED
@@ -0,0 +1,4 @@
1
+ export function renderToString(view, props) {
2
+ return view.template(props);
3
+ }
4
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAAC,IAAU,EAAE,KAA+B;IACxE,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC"}
package/dist/view.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare class View {
2
+ el: HTMLElement;
3
+ createElement(): HTMLElement;
4
+ template(_props?: any): string;
5
+ createAndMount(): void;
6
+ mount(el: HTMLElement): void;
7
+ update(_data?: Record<string, unknown>): HTMLElement;
8
+ }
9
+ interface Slottable {
10
+ template(props?: any): string;
11
+ }
12
+ export declare function slot<T extends Slottable>(target: T, ...args: Parameters<T['template']>): string;
13
+ export {};
14
+ //# sourceMappingURL=view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"AAAA,qBAAa,IAAI;IACf,EAAE,EAAG,WAAW,CAAC;IAEjB,aAAa,IAAI,WAAW;IAO5B,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,MAAM;IAI9B,cAAc,IAAI,IAAI;IAItB,KAAK,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI;IAI5B,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW;CAGrD;AAED,UAAU,SAAS;IACjB,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,GAAG,MAAM,CAAC;CAC/B;AAED,wBAAgB,IAAI,CAAC,CAAC,SAAS,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,MAAM,CAE/F"}
package/dist/view.js ADDED
@@ -0,0 +1,25 @@
1
+ export class View {
2
+ el;
3
+ createElement() {
4
+ let tpl = document.createElement('template');
5
+ tpl.innerHTML = this.template();
6
+ this.el = document.importNode(tpl.content, true).firstElementChild;
7
+ return this.el;
8
+ }
9
+ template(_props) {
10
+ return '';
11
+ }
12
+ createAndMount() {
13
+ this.mount(this.createElement());
14
+ }
15
+ mount(el) {
16
+ this.el = el;
17
+ }
18
+ update(_data) {
19
+ return this.el;
20
+ }
21
+ }
22
+ export function slot(target, ...args) {
23
+ return target.template(...args);
24
+ }
25
+ //# sourceMappingURL=view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,IAAI;IACf,EAAE,CAAe;IAEjB,aAAa;QACX,IAAI,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QAC7C,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,CAAC,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,iBAAgC,CAAC;QAClF,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,QAAQ,CAAC,MAAY;QACnB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,EAAe;QACnB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED,MAAM,CAAC,KAA+B;QACpC,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;CACF;AAMD,MAAM,UAAU,IAAI,CAAsB,MAAS,EAAE,GAAG,IAA+B;IACrF,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;AAClC,CAAC"}
package/package.json CHANGED
@@ -1,10 +1,26 @@
1
1
  {
2
2
  "name": "@matthewp/zebra",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "type": "module",
5
5
  "exports": {
6
- ".": "./src/view.ts",
7
- "./list": "./src/list.ts",
8
- "./server": "./src/server.ts"
6
+ ".": {
7
+ "types": "./dist/view.d.ts",
8
+ "default": "./dist/view.js"
9
+ },
10
+ "./list": {
11
+ "types": "./dist/list.d.ts",
12
+ "default": "./dist/list.js"
13
+ },
14
+ "./server": {
15
+ "types": "./dist/server.d.ts",
16
+ "default": "./dist/server.js"
17
+ }
18
+ },
19
+ "files": ["dist"],
20
+ "scripts": {
21
+ "build": "tsc"
22
+ },
23
+ "devDependencies": {
24
+ "typescript": "^5.8.0"
9
25
  }
10
26
  }
package/src/list.ts DELETED
@@ -1,146 +0,0 @@
1
- import { View } from './view.ts';
2
-
3
- type ViewConstructor = new () => View;
4
-
5
- export class List<T = Record<string, unknown>> {
6
- private ViewClass: ViewConstructor;
7
- private keyFn: (item: T) => unknown;
8
- private views: (View | null)[] = [];
9
- private keys: unknown[] = [];
10
- private container: HTMLElement | null = null;
11
-
12
- constructor(ViewClass: ViewConstructor, keyFn: (item: T) => unknown) {
13
- this.ViewClass = ViewClass;
14
- this.keyFn = keyFn;
15
- }
16
-
17
- template(items?: T[]): string {
18
- if (!items || items.length === 0) return '';
19
- return items.map(item => {
20
- let view = new this.ViewClass();
21
- return view.template(item as unknown as Record<string, unknown>);
22
- }).join('');
23
- }
24
-
25
- mount(container: HTMLElement, items?: T[]) {
26
- this.container = container;
27
- let children = Array.from(container.children) as HTMLElement[];
28
- if (children.length > 0 && items) {
29
- for (let i = 0; i < children.length && i < items.length; i++) {
30
- let view = new this.ViewClass();
31
- view.mount(children[i]);
32
- view.update(items[i] as unknown as Record<string, unknown>);
33
- this.views.push(view);
34
- this.keys.push(this.keyFn(items[i]));
35
- }
36
- }
37
- }
38
-
39
- private updateView(view: View, item: T) {
40
- view.update(item as unknown as Record<string, unknown>);
41
- }
42
-
43
- update(items: T[]) {
44
- let container = this.container!;
45
- let oldViews = this.views;
46
- let oldKeys = this.keys;
47
- let newKeys = items.map(this.keyFn);
48
- let newViews: (View | null)[] = new Array(items.length).fill(null);
49
-
50
- let oldHead = 0;
51
- let oldTail = oldViews.length - 1;
52
- let newHead = 0;
53
- let newTail = items.length - 1;
54
-
55
- let oldKeyToIndex: Map<unknown, number> | undefined;
56
-
57
- while (oldHead <= oldTail && newHead <= newTail) {
58
- if (oldViews[oldHead] === null) {
59
- oldHead++;
60
- } else if (oldViews[oldTail] === null) {
61
- oldTail--;
62
- } else if (oldKeys[oldHead] === newKeys[newHead]) {
63
- // Head-Head match
64
- newViews[newHead] = oldViews[oldHead];
65
- this.updateView(oldViews[oldHead]!, items[newHead]);
66
- oldHead++;
67
- newHead++;
68
- } else if (oldKeys[oldTail] === newKeys[newTail]) {
69
- // Tail-Tail match
70
- newViews[newTail] = oldViews[oldTail];
71
- this.updateView(oldViews[oldTail]!, items[newTail]);
72
- oldTail--;
73
- newTail--;
74
- } else if (oldKeys[oldHead] === newKeys[newTail]) {
75
- // Head-Tail match: move old head to after old tail
76
- newViews[newTail] = oldViews[oldHead];
77
- this.updateView(oldViews[oldHead]!, items[newTail]);
78
- oldViews[oldTail]!.el.after(oldViews[oldHead]!.el);
79
- oldHead++;
80
- newTail--;
81
- } else if (oldKeys[oldTail] === newKeys[newHead]) {
82
- // Tail-Head match: move old tail to before old head
83
- newViews[newHead] = oldViews[oldTail];
84
- this.updateView(oldViews[oldTail]!, items[newHead]);
85
- oldViews[oldHead]!.el.before(oldViews[oldTail]!.el);
86
- oldTail--;
87
- newHead++;
88
- } else {
89
- // Build map lazily
90
- if (!oldKeyToIndex) {
91
- oldKeyToIndex = new Map();
92
- for (let i = oldHead; i <= oldTail; i++) {
93
- if (oldViews[i] !== null) {
94
- oldKeyToIndex.set(oldKeys[i], i);
95
- }
96
- }
97
- }
98
-
99
- let oldIndex = oldKeyToIndex.get(newKeys[newHead]);
100
- if (oldIndex === undefined) {
101
- // New item
102
- let view = new this.ViewClass();
103
- view.createAndMount();
104
- newViews[newHead] = view;
105
- oldViews[oldHead]!.el.before(view.el);
106
- this.updateView(view, items[newHead]);
107
- } else {
108
- // Move existing
109
- let view = oldViews[oldIndex]!;
110
- this.updateView(view, items[newHead]);
111
- newViews[newHead] = view;
112
- oldViews[oldHead]!.el.before(view.el);
113
- oldViews[oldIndex] = null;
114
- }
115
- newHead++;
116
- }
117
- }
118
-
119
- // Remove remaining old items
120
- while (oldHead <= oldTail) {
121
- if (oldViews[oldHead] !== null) {
122
- oldViews[oldHead]!.el.remove();
123
- }
124
- oldHead++;
125
- }
126
-
127
- // Add remaining new items
128
- while (newHead <= newTail) {
129
- let view = new this.ViewClass();
130
- view.createAndMount();
131
- newViews[newHead] = view;
132
- let el = view.el;
133
- let ref = newViews[newTail + 1]?.el;
134
- if (ref) {
135
- ref.before(el);
136
- } else {
137
- container.append(el);
138
- }
139
- this.updateView(view, items[newHead]);
140
- newHead++;
141
- }
142
-
143
- this.views = newViews;
144
- this.keys = newKeys;
145
- }
146
- }
package/src/server.ts DELETED
@@ -1,5 +0,0 @@
1
- import type { View } from './view.ts';
2
-
3
- export function renderToString(view: View, props?: Record<string, unknown>): string {
4
- return view.template(props);
5
- }
package/src/view.ts DELETED
@@ -1,34 +0,0 @@
1
- export class View {
2
- el!: HTMLElement;
3
-
4
- createElement(): HTMLElement {
5
- let tpl = document.createElement('template');
6
- tpl.innerHTML = this.template();
7
- this.el = document.importNode(tpl.content, true).firstElementChild as HTMLElement;
8
- return this.el;
9
- }
10
-
11
- template(_props?: any): string {
12
- return '';
13
- }
14
-
15
- createAndMount(): void {
16
- this.mount(this.createElement());
17
- }
18
-
19
- mount(el: HTMLElement): void {
20
- this.el = el;
21
- }
22
-
23
- update(_data?: Record<string, unknown>): HTMLElement {
24
- return this.el;
25
- }
26
- }
27
-
28
- interface Slottable {
29
- template(props?: any): string;
30
- }
31
-
32
- export function slot<T extends Slottable>(target: T, ...args: Parameters<T['template']>): string {
33
- return target.template(...args);
34
- }