custom-elements-ts 0.0.17 → 0.2.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.
Files changed (82) hide show
  1. package/.eslintrc.json +47 -0
  2. package/.github/workflows/ci.yml +49 -0
  3. package/.prettierrc +7 -0
  4. package/LICENSE +20 -0
  5. package/README.md +279 -37
  6. package/assets/readme-header.png +0 -0
  7. package/assets/social-preview.jpg +0 -0
  8. package/demos/counter/counter.element.html +1 -0
  9. package/demos/counter/counter.element.scss +234 -0
  10. package/demos/counter/counter.element.ts +68 -0
  11. package/demos/counter/index.html +205 -0
  12. package/demos/counter/index.ts +1 -0
  13. package/demos/site/code-example/code-example.element.scss +168 -0
  14. package/demos/site/code-example/code-example.element.ts +88 -0
  15. package/demos/site/event-log/event-log.element.scss +179 -0
  16. package/demos/site/event-log/event-log.element.ts +123 -0
  17. package/demos/site/favicon.svg +14 -0
  18. package/demos/site/index.html +368 -0
  19. package/demos/site/index.ts +19 -0
  20. package/demos/site/llms.txt +53 -0
  21. package/demos/site/message/message.element.scss +75 -0
  22. package/demos/site/message/message.element.ts +76 -0
  23. package/demos/site/og-image.png +0 -0
  24. package/demos/site/scroll-restoration.ts +28 -0
  25. package/demos/site/source-toggle/source-toggle.ts +88 -0
  26. package/demos/site/source-viewer/source-viewer.element.scss +292 -0
  27. package/demos/site/source-viewer/source-viewer.element.ts +138 -0
  28. package/demos/site/source-viewer/sources.generated.ts +11 -0
  29. package/demos/site/styles/site.css +1135 -0
  30. package/demos/site/styles/tokens.css +56 -0
  31. package/demos/site/toast/toast.element.scss +110 -0
  32. package/demos/site/toast/toast.element.ts +63 -0
  33. package/demos/todo-dashboard/index.html +141 -0
  34. package/demos/todo-dashboard/index.ts +4 -0
  35. package/demos/todo-dashboard/todo-dashboard.element.scss +1150 -0
  36. package/demos/todo-dashboard/todo-dashboard.element.ts +333 -0
  37. package/demos/todo-dashboard/todo-filters.element.ts +54 -0
  38. package/demos/todo-dashboard/todo-item.element.ts +127 -0
  39. package/demos/todo-dashboard/todo-stats.element.ts +189 -0
  40. package/package.json +73 -24
  41. package/src/custom-element.ts +206 -0
  42. package/src/index.ts +8 -0
  43. package/src/listen.ts +70 -0
  44. package/src/prop.ts +92 -0
  45. package/src/signal.ts +66 -0
  46. package/src/state.ts +141 -0
  47. package/src/template-runtime.ts +783 -0
  48. package/src/toggle.ts +66 -0
  49. package/src/tsconfig.json +24 -0
  50. package/src/util.ts +33 -0
  51. package/src/watch.ts +14 -0
  52. package/tests/basic.spec.ts +70 -0
  53. package/tests/custom-element.spec.ts +77 -0
  54. package/tests/dispatch.spec.ts +52 -0
  55. package/tests/init.spec.ts +94 -0
  56. package/tests/listen.spec.ts +118 -0
  57. package/tests/map-shallow-state.spec.ts +184 -0
  58. package/tests/prop.spec.ts +118 -0
  59. package/tests/signal.spec.ts +117 -0
  60. package/tests/templating-runtime.spec.ts +575 -0
  61. package/tests/toggle.spec.ts +92 -0
  62. package/tests/watch.spec.ts +183 -0
  63. package/tools/build.js +128 -0
  64. package/tools/bundle.js +167 -0
  65. package/tools/generate-sources.js +76 -0
  66. package/tools/rollup-config.js +70 -0
  67. package/tools/start.js +194 -0
  68. package/tsconfig.json +38 -0
  69. package/vite.config.mts +30 -0
  70. package/bundles/custom-elements-ts.umd.js +0 -359
  71. package/bundles/custom-elements-ts.umd.js.map +0 -1
  72. package/esm2015/custom-elements-ts.js +0 -283
  73. package/esm2015/custom-elements-ts.js.map +0 -1
  74. package/esm5/custom-element.d.ts +0 -12
  75. package/esm5/custom-elements-ts.js +0 -344
  76. package/esm5/custom-elements-ts.js.map +0 -1
  77. package/esm5/index.d.ts +0 -5
  78. package/esm5/listen.d.ts +0 -17
  79. package/esm5/prop.d.ts +0 -2
  80. package/esm5/toggle.d.ts +0 -1
  81. package/esm5/util.d.ts +0 -4
  82. package/esm5/watch.d.ts +0 -1
@@ -0,0 +1,56 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ Design tokens
3
+ Single source of truth for color, type, radius, motion.
4
+ ───────────────────────────────────────────────────────────── */
5
+
6
+ :root {
7
+ /* Surfaces — cool off-black tinted toward blue, never pure #000 */
8
+ --bg: #0a0c10;
9
+ --bg-elev-1: #11141a;
10
+ --bg-elev-2: #161a22;
11
+ --bg-elev-3: #1c2230;
12
+
13
+ /* Lines */
14
+ --line: rgba(255, 255, 255, 0.06);
15
+ --line-strong: rgba(255, 255, 255, 0.10);
16
+ --line-accent: rgba(49, 120, 198, 0.32);
17
+
18
+ /* Type */
19
+ --fg: #e6ecf3;
20
+ --fg-muted: #97a1ad;
21
+ --fg-subtle: #5b6370;
22
+
23
+ /* Accent — TypeScript brand blue. One color, used sparingly. */
24
+ --accent: #3178c6; /* canonical TS logo blue */
25
+ --accent-bright: #4189d6; /* hover / focus */
26
+ --accent-fg: #9cc0eb; /* legible accent on dark bg */
27
+ --accent-soft: rgba(49, 120, 198, 0.14);
28
+ --accent-line: rgba(49, 120, 198, 0.42);
29
+
30
+ /* Status — kept neutral (no second hue competing with the accent) */
31
+ --ok: var(--accent-fg);
32
+ --ok-soft: var(--accent-soft);
33
+
34
+ /* Type scale */
35
+ --font-sans: "Geist", "Inter", ui-sans-serif, system-ui, -apple-system,
36
+ "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
37
+ --font-mono: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular,
38
+ Menlo, Consolas, "Courier New", monospace;
39
+
40
+ /* Radii — minimal. Sharp, technical, editorial. */
41
+ --radius-xs: 2px;
42
+ --radius-sm: 2px;
43
+ --radius-md: 3px;
44
+ --radius-lg: 4px;
45
+ --radius-xl: 4px;
46
+
47
+ /* Motion */
48
+ --ease: cubic-bezier(0.22, 1, 0.36, 1);
49
+ --dur-fast: 140ms;
50
+ --dur: 220ms;
51
+ --dur-slow: 420ms;
52
+
53
+ /* Container */
54
+ --container: 1180px;
55
+ --gutter: clamp(20px, 4vw, 40px);
56
+ }
@@ -0,0 +1,110 @@
1
+ :host {
2
+ position: fixed;
3
+ bottom: 24px;
4
+ right: 24px;
5
+ z-index: 1000;
6
+ pointer-events: none;
7
+ font-family: "Geist", "Inter", system-ui, -apple-system, "Segoe UI", Roboto,
8
+ "Helvetica Neue", Arial, sans-serif;
9
+ -webkit-font-smoothing: antialiased;
10
+ }
11
+
12
+ .toast {
13
+ display: grid;
14
+ grid-template-columns: auto auto 1fr;
15
+ align-items: center;
16
+ gap: 12px;
17
+ min-width: 280px;
18
+ max-width: 380px;
19
+ padding: 13px 18px 13px 13px;
20
+ background: linear-gradient(180deg, #181d27 0%, #11141a 100%);
21
+ border: 1px solid rgba(255, 255, 255, 0.10);
22
+ border-radius: 4px;
23
+ color: #e6ecf3;
24
+ box-shadow:
25
+ 0 1px 0 rgba(255, 255, 255, 0.04) inset,
26
+ 0 16px 40px -16px rgba(0, 0, 0, 0.65),
27
+ 0 4px 10px -4px rgba(0, 0, 0, 0.45);
28
+
29
+ /* Hidden state — slide up + fade in on enter */
30
+ opacity: 0;
31
+ transform: translateY(12px) scale(0.985);
32
+ transition:
33
+ opacity 220ms cubic-bezier(0.22, 1, 0.36, 1),
34
+ transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
35
+ pointer-events: auto;
36
+ will-change: transform, opacity;
37
+ }
38
+
39
+ .toast[data-show] {
40
+ opacity: 1;
41
+ transform: translateY(0) scale(1);
42
+ }
43
+
44
+ .bar {
45
+ width: 3px;
46
+ align-self: stretch;
47
+ background: #3178c6;
48
+ border-radius: 2px;
49
+ }
50
+ .toast[data-kind="error"] .bar { background: #f47067; }
51
+ .toast[data-kind="info"] .bar { background: #9cc0eb; }
52
+
53
+ .icon {
54
+ width: 22px;
55
+ height: 22px;
56
+ display: grid;
57
+ place-items: center;
58
+ color: #9cc0eb;
59
+ }
60
+ .toast[data-kind="error"] .icon { color: #f47067; }
61
+
62
+ .icon svg {
63
+ width: 16px;
64
+ height: 16px;
65
+ display: none;
66
+ }
67
+ .toast[data-kind="success"] .icon-success,
68
+ .toast[data-kind="info"] .icon-success { display: block; }
69
+ .toast[data-kind="error"] .icon-error { display: block; }
70
+
71
+ .body {
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: 2px;
75
+ min-width: 0;
76
+ }
77
+
78
+ .title {
79
+ font-size: 13px;
80
+ font-weight: 600;
81
+ color: #e6ecf3;
82
+ letter-spacing: -0.005em;
83
+ }
84
+
85
+ .msg {
86
+ font-family: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular,
87
+ Menlo, Consolas, "Courier New", monospace;
88
+ font-size: 12px;
89
+ color: #97a1ad;
90
+ overflow: hidden;
91
+ text-overflow: ellipsis;
92
+ white-space: nowrap;
93
+ }
94
+
95
+ @media (max-width: 540px) {
96
+ :host {
97
+ bottom: 16px;
98
+ right: 16px;
99
+ left: 16px;
100
+ }
101
+ .toast { max-width: none; }
102
+ }
103
+
104
+ @media (prefers-reduced-motion: reduce) {
105
+ .toast {
106
+ transition: opacity 120ms linear;
107
+ transform: none;
108
+ }
109
+ .toast[data-show] { transform: none; }
110
+ }
@@ -0,0 +1,63 @@
1
+ import { CustomElement } from 'custom-elements-ts';
2
+
3
+ interface ToastDetail {
4
+ title: string;
5
+ message?: string;
6
+ kind?: 'success' | 'error' | 'info';
7
+ }
8
+
9
+ @CustomElement({
10
+ tag: 'cts-toast',
11
+ template: `
12
+ <div class="toast" role="status" aria-live="polite" aria-atomic="true">
13
+ <span class="bar" aria-hidden="true"></span>
14
+ <span class="icon" aria-hidden="true">
15
+ <svg class="icon-success" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
16
+ <svg class="icon-error" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
17
+ </span>
18
+ <div class="body">
19
+ <strong class="title"></strong>
20
+ <span class="msg"></span>
21
+ </div>
22
+ </div>
23
+ `,
24
+ styleUrl: './toast.element.scss',
25
+ })
26
+ export class ToastElement extends HTMLElement {
27
+ private timer: number | undefined;
28
+ private handler = (e: Event) => this.show((e as CustomEvent<ToastDetail>).detail);
29
+
30
+ connectedCallback() {
31
+ document.addEventListener('cts:toast', this.handler);
32
+ }
33
+
34
+ disconnectedCallback() {
35
+ document.removeEventListener('cts:toast', this.handler);
36
+ if (this.timer) window.clearTimeout(this.timer);
37
+ }
38
+
39
+ show(detail: ToastDetail) {
40
+ if (!detail || !this.shadowRoot) return;
41
+ const root = this.shadowRoot;
42
+ const toast = root.querySelector('.toast') as HTMLElement;
43
+ const titleEl = root.querySelector('.title') as HTMLElement;
44
+ const msgEl = root.querySelector('.msg') as HTMLElement;
45
+
46
+ titleEl.textContent = detail.title || '';
47
+ msgEl.textContent = detail.message || '';
48
+ toast.dataset.kind = detail.kind || 'success';
49
+
50
+ // Re-trigger the entry transition cleanly
51
+ delete toast.dataset.show;
52
+ requestAnimationFrame(() => {
53
+ requestAnimationFrame(() => {
54
+ toast.dataset.show = '';
55
+ });
56
+ });
57
+
58
+ if (this.timer) window.clearTimeout(this.timer);
59
+ this.timer = window.setTimeout(() => {
60
+ delete toast.dataset.show;
61
+ }, 2800);
62
+ }
63
+ }
@@ -0,0 +1,141 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <base href="/" />
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
7
+ <meta name="theme-color" content="#0a0c10" />
8
+ <meta name="description" content="Demo of cts-todo-dashboard — a custom element built with custom-elements-ts." />
9
+ <title>Taskboard — custom-elements-ts demo</title>
10
+
11
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
13
+ <link
14
+ rel="stylesheet"
15
+ href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap"
16
+ />
17
+
18
+ <style>
19
+ :root {
20
+ --bg: #0a0c10;
21
+ }
22
+
23
+ * {
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ html,
28
+ body {
29
+ margin: 0;
30
+ background: var(--bg);
31
+ color: #e6ecf3;
32
+ }
33
+
34
+ body {
35
+ position: relative;
36
+ min-height: 100dvh;
37
+ display: flex;
38
+ justify-content: center;
39
+ align-items: flex-start;
40
+ padding: 80px 24px 64px;
41
+ background:
42
+ radial-gradient(60% 50% at 18% -10%, rgba(49, 120, 198, 0.18) 0%, transparent 60%),
43
+ radial-gradient(50% 40% at 90% 110%, rgba(49, 120, 198, 0.10) 0%, transparent 60%),
44
+ var(--bg);
45
+ font-family:
46
+ 'Geist',
47
+ 'Inter',
48
+ ui-sans-serif,
49
+ system-ui,
50
+ -apple-system,
51
+ BlinkMacSystemFont,
52
+ 'Segoe UI',
53
+ sans-serif;
54
+ -webkit-font-smoothing: antialiased;
55
+ text-rendering: optimizeLegibility;
56
+ overflow-x: hidden;
57
+ }
58
+
59
+ body::before {
60
+ content: '';
61
+ position: fixed;
62
+ inset: 0;
63
+ pointer-events: none;
64
+ background-image:
65
+ linear-gradient(to right, rgba(255, 255, 255, 0.025) 1px, transparent 1px),
66
+ linear-gradient(to bottom, rgba(255, 255, 255, 0.025) 1px, transparent 1px);
67
+ background-size: 56px 56px;
68
+ mask-image: radial-gradient(ellipse 70% 60% at 50% 0%, #000 0%, transparent 75%);
69
+ -webkit-mask-image: radial-gradient(ellipse 70% 60% at 50% 0%, #000 0%, transparent 75%);
70
+ z-index: 0;
71
+ }
72
+
73
+ cts-todo-dashboard {
74
+ position: relative;
75
+ z-index: 1;
76
+ }
77
+
78
+ .corner {
79
+ position: fixed;
80
+ top: 24px;
81
+ left: 24px;
82
+ z-index: 1;
83
+ display: inline-flex;
84
+ align-items: center;
85
+ gap: 10px;
86
+ color: #97a1ad;
87
+ font-family: 'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular,
88
+ Menlo, Consolas, monospace;
89
+ font-size: 11px;
90
+ letter-spacing: 0.08em;
91
+ text-transform: uppercase;
92
+ text-decoration: none;
93
+ }
94
+
95
+ .corner svg {
96
+ color: #3178c6;
97
+ }
98
+
99
+ .corner:hover {
100
+ color: #e6ecf3;
101
+ }
102
+
103
+ @media (max-width: 560px) {
104
+ body {
105
+ padding: 64px 16px 32px;
106
+ }
107
+ .corner {
108
+ top: 16px;
109
+ left: 16px;
110
+ }
111
+ }
112
+ </style>
113
+ </head>
114
+ <body>
115
+ <a class="corner" href="https://github.com/geocine/custom-elements-ts">
116
+ <svg
117
+ viewBox="0 0 24 24"
118
+ fill="none"
119
+ stroke="currentColor"
120
+ stroke-width="2.25"
121
+ stroke-linecap="round"
122
+ stroke-linejoin="round"
123
+ width="14"
124
+ height="14"
125
+ aria-hidden="true"
126
+ >
127
+ <path d="M8.5 7.5 4 12l4.5 4.5" />
128
+ <path d="M15.5 7.5 20 12l-4.5 4.5" />
129
+ <path d="M14 5l-4 14" />
130
+ </svg>
131
+ custom-elements-ts · taskboard demo
132
+ </a>
133
+
134
+ <cts-todo-dashboard heading="Sprint board"></cts-todo-dashboard>
135
+
136
+ <script src="node_modules/@webcomponents/custom-elements/src/native-shim.js"></script>
137
+ <script src="node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
138
+ <script src="todo-dashboard.umd.js"></script>
139
+ </body>
140
+ </html>
141
+ <!-- livereload -->
@@ -0,0 +1,4 @@
1
+ export * from './todo-stats.element';
2
+ export * from './todo-filters.element';
3
+ export * from './todo-item.element';
4
+ export * from './todo-dashboard.element';