custom-elements-ts 0.0.17 → 0.1.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/.eslintrc.json +46 -0
- package/.github/workflows/ci.yml +49 -0
- package/.prettierrc +7 -0
- package/LICENSE +20 -0
- package/README.md +270 -35
- package/demos/counter/counter.element.html +1 -0
- package/demos/counter/counter.element.scss +234 -0
- package/demos/counter/counter.element.ts +68 -0
- package/demos/counter/index.html +205 -0
- package/demos/counter/index.ts +1 -0
- package/demos/site/code-example/code-example.element.scss +168 -0
- package/demos/site/code-example/code-example.element.ts +88 -0
- package/demos/site/event-log/event-log.element.scss +179 -0
- package/demos/site/event-log/event-log.element.ts +134 -0
- package/demos/site/favicon.svg +14 -0
- package/demos/site/index.html +346 -0
- package/demos/site/index.ts +13 -0
- package/demos/site/message/message.element.scss +75 -0
- package/demos/site/message/message.element.ts +76 -0
- package/demos/site/og-image.png +0 -0
- package/demos/site/styles/site.css +1023 -0
- package/demos/site/styles/tokens.css +56 -0
- package/demos/site/toast/toast.element.scss +110 -0
- package/demos/site/toast/toast.element.ts +63 -0
- package/demos/todo-dashboard/index.html +141 -0
- package/demos/todo-dashboard/index.ts +4 -0
- package/demos/todo-dashboard/todo-dashboard.element.scss +1145 -0
- package/demos/todo-dashboard/todo-dashboard.element.ts +332 -0
- package/demos/todo-dashboard/todo-filters.element.ts +54 -0
- package/demos/todo-dashboard/todo-item.element.ts +126 -0
- package/demos/todo-dashboard/todo-stats.element.ts +189 -0
- package/package.json +73 -24
- package/src/custom-element.ts +206 -0
- package/{esm5/index.d.ts → src/index.ts} +7 -5
- package/src/listen.ts +70 -0
- package/src/prop.ts +92 -0
- package/src/state.ts +129 -0
- package/src/template-runtime.ts +435 -0
- package/src/toggle.ts +66 -0
- package/src/tsconfig.json +24 -0
- package/src/util.ts +33 -0
- package/src/watch.ts +14 -0
- package/tests/basic.spec.ts +70 -0
- package/tests/custom-element.spec.ts +77 -0
- package/tests/dispatch.spec.ts +52 -0
- package/tests/init.spec.ts +94 -0
- package/tests/listen.spec.ts +118 -0
- package/tests/prop.spec.ts +118 -0
- package/tests/templating-runtime.spec.ts +575 -0
- package/tests/toggle.spec.ts +92 -0
- package/tests/watch.spec.ts +183 -0
- package/tools/build.js +119 -0
- package/tools/bundle.js +167 -0
- package/tools/rollup-config.js +70 -0
- package/tools/start.js +188 -0
- package/tsconfig.json +38 -0
- package/vite.config.mts +30 -0
- package/bundles/custom-elements-ts.umd.js +0 -359
- package/bundles/custom-elements-ts.umd.js.map +0 -1
- package/esm2015/custom-elements-ts.js +0 -283
- package/esm2015/custom-elements-ts.js.map +0 -1
- package/esm5/custom-element.d.ts +0 -12
- package/esm5/custom-elements-ts.js +0 -344
- package/esm5/custom-elements-ts.js.map +0 -1
- package/esm5/listen.d.ts +0 -17
- package/esm5/prop.d.ts +0 -2
- package/esm5/toggle.d.ts +0 -1
- package/esm5/util.d.ts +0 -4
- package/esm5/watch.d.ts +0 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<meta name="theme-color" content="#0a0c10">
|
|
7
|
+
<meta name="color-scheme" content="dark">
|
|
8
|
+
|
|
9
|
+
<title>custom-elements-ts — native Web Components in TypeScript</title>
|
|
10
|
+
<meta name="description" content="Author native Web Components in TypeScript with a tiny set of decorators — @CustomElement, @Prop, @State, @Watch, @Listen, @Dispatch — and a 3 kB runtime. Zero dependencies. Framework-free.">
|
|
11
|
+
|
|
12
|
+
<!-- Open Graph / Facebook -->
|
|
13
|
+
<meta property="og:type" content="website">
|
|
14
|
+
<meta property="og:site_name" content="custom-elements-ts">
|
|
15
|
+
<meta property="og:url" content="https://geocine.github.io/custom-elements-ts/">
|
|
16
|
+
<meta property="og:title" content="custom-elements-ts — Native Web Components in TypeScript">
|
|
17
|
+
<meta property="og:description" content="A 3 kB decorator-based runtime for authoring standards-compliant custom elements in TypeScript. Zero dependencies. Framework-free.">
|
|
18
|
+
<meta property="og:image" content="https://geocine.github.io/custom-elements-ts/og-image.png">
|
|
19
|
+
<meta property="og:image:secure_url" content="https://geocine.github.io/custom-elements-ts/og-image.png">
|
|
20
|
+
<meta property="og:image:type" content="image/png">
|
|
21
|
+
<meta property="og:image:width" content="1200">
|
|
22
|
+
<meta property="og:image:height" content="630">
|
|
23
|
+
<meta property="og:image:alt" content="custom-elements-ts — Native Web Components, written in TypeScript. Sample code window showing a @CustomElement decorator.">
|
|
24
|
+
|
|
25
|
+
<!-- Twitter / X -->
|
|
26
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
27
|
+
<meta name="twitter:title" content="custom-elements-ts — Native Web Components in TypeScript">
|
|
28
|
+
<meta name="twitter:description" content="A 3 kB decorator-based runtime for authoring standards-compliant custom elements in TypeScript. Zero dependencies. Framework-free.">
|
|
29
|
+
<meta name="twitter:image" content="https://geocine.github.io/custom-elements-ts/og-image.png">
|
|
30
|
+
<meta name="twitter:image:alt" content="custom-elements-ts — Native Web Components, written in TypeScript.">
|
|
31
|
+
|
|
32
|
+
<link rel="canonical" href="https://geocine.github.io/custom-elements-ts/">
|
|
33
|
+
<link rel="icon" type="image/svg+xml" href="favicon.svg">
|
|
34
|
+
|
|
35
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
36
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
37
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600&display=swap">
|
|
38
|
+
<link rel="stylesheet" href="styles/site.css">
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
<a class="skip-link" href="#main">Skip to content</a>
|
|
42
|
+
|
|
43
|
+
<!-- ─── Top navigation ──────────────────────────────────────── -->
|
|
44
|
+
<header class="nav">
|
|
45
|
+
<div class="container nav__inner">
|
|
46
|
+
<a class="brand" href="#">
|
|
47
|
+
<span class="brand__mark" aria-hidden="true">
|
|
48
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round">
|
|
49
|
+
<path d="M8.5 7.5 4 12l4.5 4.5"/>
|
|
50
|
+
<path d="M15.5 7.5 20 12l-4.5 4.5"/>
|
|
51
|
+
<path d="M14 5l-4 14"/>
|
|
52
|
+
</svg>
|
|
53
|
+
</span>
|
|
54
|
+
<span class="brand__name">custom-elements<span class="dim">-ts</span></span>
|
|
55
|
+
</a>
|
|
56
|
+
|
|
57
|
+
<nav class="nav__links" aria-label="Primary">
|
|
58
|
+
<a class="nav__link" href="#showcase">Example</a>
|
|
59
|
+
<a class="nav__link" href="#live">Live demos</a>
|
|
60
|
+
<a class="nav__link" href="#api">Decorators</a>
|
|
61
|
+
<a class="nav__link" href="https://github.com/geocine/custom-elements-ts#readme">Docs</a>
|
|
62
|
+
<a class="nav__link" href="https://www.npmjs.com/package/custom-elements-ts">npm</a>
|
|
63
|
+
</nav>
|
|
64
|
+
|
|
65
|
+
<a class="nav__cta" href="https://github.com/geocine/custom-elements-ts" aria-label="View source on GitHub">
|
|
66
|
+
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8a8 8 0 0 0 5.47 7.59c.4.07.55-.17.55-.38v-1.33c-2.23.49-2.7-1.07-2.7-1.07-.36-.92-.89-1.17-.89-1.17-.73-.5.05-.49.05-.49.81.06 1.24.83 1.24.83.72 1.23 1.88.88 2.34.67.07-.52.28-.88.51-1.08-1.78-.2-3.65-.89-3.65-3.96 0-.87.31-1.59.83-2.15-.08-.2-.36-1.02.08-2.13 0 0 .67-.21 2.2.82a7.6 7.6 0 0 1 4 0c1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.93.08 2.13.52.56.83 1.28.83 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48v2.19c0 .21.15.46.55.38A8 8 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
|
|
67
|
+
<span>GitHub</span>
|
|
68
|
+
</a>
|
|
69
|
+
</div>
|
|
70
|
+
</header>
|
|
71
|
+
|
|
72
|
+
<main id="main">
|
|
73
|
+
|
|
74
|
+
<!-- ─── Hero ────────────────────────────────────────────── -->
|
|
75
|
+
<section class="hero">
|
|
76
|
+
<div class="container">
|
|
77
|
+
<div class="hero__pill" role="status">
|
|
78
|
+
<span class="badge">v0.0.16</span>
|
|
79
|
+
<span>TypeScript 5</span>
|
|
80
|
+
<span class="sep">·</span>
|
|
81
|
+
<span>MIT</span>
|
|
82
|
+
<span class="sep">·</span>
|
|
83
|
+
<span>0 deps</span>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<h1 class="hero__title">
|
|
87
|
+
Native Web Components, written in <em class="em">TypeScript</em>.
|
|
88
|
+
</h1>
|
|
89
|
+
|
|
90
|
+
<p class="hero__lede">
|
|
91
|
+
<strong>custom-elements-ts</strong> is a tiny set of decorators —
|
|
92
|
+
<code>@CustomElement</code>, <code>@Prop</code>, <code>@State</code>,
|
|
93
|
+
<code>@Watch</code>, <code>@Listen</code>, <code>@Dispatch</code>,
|
|
94
|
+
<code>@Toggle</code> — plus a 3 kB <code>html</code>/<code>render()</code>
|
|
95
|
+
runtime that lets you author standards-compliant custom elements
|
|
96
|
+
without a framework.
|
|
97
|
+
</p>
|
|
98
|
+
|
|
99
|
+
<div class="hero__actions">
|
|
100
|
+
<a class="btn btn--primary" href="#live">
|
|
101
|
+
See it in action
|
|
102
|
+
<svg class="arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M13 5l7 7-7 7"/></svg>
|
|
103
|
+
</a>
|
|
104
|
+
<a class="btn btn--ghost" href="https://github.com/geocine/custom-elements-ts">
|
|
105
|
+
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" width="14" height="14"><path d="M8 0C3.58 0 0 3.58 0 8a8 8 0 0 0 5.47 7.59c.4.07.55-.17.55-.38v-1.33c-2.23.49-2.7-1.07-2.7-1.07-.36-.92-.89-1.17-.89-1.17-.73-.5.05-.49.05-.49.81.06 1.24.83 1.24.83.72 1.23 1.88.88 2.34.67.07-.52.28-.88.51-1.08-1.78-.2-3.65-.89-3.65-3.96 0-.87.31-1.59.83-2.15-.08-.2-.36-1.02.08-2.13 0 0 .67-.21 2.2.82a7.6 7.6 0 0 1 4 0c1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.93.08 2.13.52.56.83 1.28.83 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48v2.19c0 .21.15.46.55.38A8 8 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
|
|
106
|
+
Source on GitHub
|
|
107
|
+
</a>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div class="hero__meta">
|
|
111
|
+
<span><span class="ok">●</span> Works with any framework — or none</span>
|
|
112
|
+
<span><span class="ok">●</span> ~3 kB min+gzip runtime</span>
|
|
113
|
+
<span><span class="ok">●</span> Shadow DOM & light DOM</span>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</section>
|
|
117
|
+
|
|
118
|
+
<!-- ─── Showcase: code preview + install ────────────────── -->
|
|
119
|
+
<section class="showcase" id="showcase">
|
|
120
|
+
<div class="container">
|
|
121
|
+
<div class="showcase__grid">
|
|
122
|
+
|
|
123
|
+
<article class="window" aria-label="Source for the install pill on the right">
|
|
124
|
+
<header class="window__bar">
|
|
125
|
+
<span class="window__dots" aria-hidden="true">
|
|
126
|
+
<span></span><span></span><span></span>
|
|
127
|
+
</span>
|
|
128
|
+
<span class="window__title">
|
|
129
|
+
message.<span class="accent">element.ts</span>
|
|
130
|
+
</span>
|
|
131
|
+
<span class="window__copy">
|
|
132
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
133
|
+
what runs the install pill →
|
|
134
|
+
</span>
|
|
135
|
+
</header>
|
|
136
|
+
<div class="window__body">
|
|
137
|
+
<cts-code-example></cts-code-example>
|
|
138
|
+
</div>
|
|
139
|
+
</article>
|
|
140
|
+
|
|
141
|
+
<aside class="side-card" aria-label="Install custom-elements-ts">
|
|
142
|
+
<div class="side-card__head">
|
|
143
|
+
<div class="side-card__kicker">Install · live</div>
|
|
144
|
+
<h2 class="side-card__title">Click the pill — see the source.</h2>
|
|
145
|
+
<p class="side-card__sub">
|
|
146
|
+
The component on the left <em>is</em> the component below.
|
|
147
|
+
Click to copy, watch <code>cts:toast</code> fire.
|
|
148
|
+
</p>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<cts-message message="npm install custom-elements-ts"></cts-message>
|
|
152
|
+
|
|
153
|
+
<ul class="checks">
|
|
154
|
+
<li>
|
|
155
|
+
<span class="check" aria-hidden="true">
|
|
156
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
|
|
157
|
+
</span>
|
|
158
|
+
<span><strong>Zero dependencies.</strong> Nothing hides under the hood.</span>
|
|
159
|
+
</li>
|
|
160
|
+
<li>
|
|
161
|
+
<span class="check" aria-hidden="true">
|
|
162
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
|
|
163
|
+
</span>
|
|
164
|
+
<span><strong>Framework-free.</strong> Drop the same element into React, Vue, Svelte, or plain HTML.</span>
|
|
165
|
+
</li>
|
|
166
|
+
<li>
|
|
167
|
+
<span class="check" aria-hidden="true">
|
|
168
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
|
|
169
|
+
</span>
|
|
170
|
+
<span><strong>Tiny surface area.</strong> Seven decorators is the whole API.</span>
|
|
171
|
+
</li>
|
|
172
|
+
</ul>
|
|
173
|
+
|
|
174
|
+
<div class="side-card__foot">
|
|
175
|
+
<a href="https://github.com/geocine/custom-elements-ts#readme">
|
|
176
|
+
Read the full guide
|
|
177
|
+
<svg class="arrow" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M13 5l7 7-7 7"/></svg>
|
|
178
|
+
</a>
|
|
179
|
+
</div>
|
|
180
|
+
</aside>
|
|
181
|
+
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</section>
|
|
185
|
+
|
|
186
|
+
<!-- ─── Live demos ─────────────────────────────────────── -->
|
|
187
|
+
<section class="live" id="live">
|
|
188
|
+
<div class="container">
|
|
189
|
+
|
|
190
|
+
<div class="live__head">
|
|
191
|
+
<div>
|
|
192
|
+
<span class="eyebrow"><span class="dot"></span> Live in your browser</span>
|
|
193
|
+
<h2 class="live__title">Two demos. Real components. Real events.</h2>
|
|
194
|
+
</div>
|
|
195
|
+
<p class="live__lede">
|
|
196
|
+
Each demo below is a custom element built with the same library you'd
|
|
197
|
+
<code>npm install</code>. The event log on the side is itself a
|
|
198
|
+
<code>render()</code> + <code>@State()</code> component — it subscribes
|
|
199
|
+
to the bubbling, composed <code>CustomEvent</code>s the demo dispatches.
|
|
200
|
+
</p>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<!-- Counter playground -->
|
|
204
|
+
<article class="playground playground--counter" aria-labelledby="demo-counter-title">
|
|
205
|
+
<header class="playground__head">
|
|
206
|
+
<div>
|
|
207
|
+
<span class="playground__kicker">Demo · 01</span>
|
|
208
|
+
<h3 id="demo-counter-title" class="playground__title">Counter — <code>@State()</code> + <code>@Dispatch()</code></h3>
|
|
209
|
+
<p class="playground__sub">
|
|
210
|
+
A single-button card that owns an internal count, re-renders via the
|
|
211
|
+
<code>html</code> tagged template, and fires <code>counter.change</code>
|
|
212
|
+
whenever its <code>@State()</code> changes.
|
|
213
|
+
</p>
|
|
214
|
+
</div>
|
|
215
|
+
<a class="playground__source" href="https://github.com/geocine/custom-elements-ts/blob/master/demos/counter/counter.element.ts">
|
|
216
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" width="14" height="14"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
|
217
|
+
View source
|
|
218
|
+
</a>
|
|
219
|
+
</header>
|
|
220
|
+
|
|
221
|
+
<div class="playground__body playground__body--split">
|
|
222
|
+
<div class="stage stage--counter">
|
|
223
|
+
<cts-counter></cts-counter>
|
|
224
|
+
<p class="stage__caption">
|
|
225
|
+
Click the card. <code>@State() count</code> increments and
|
|
226
|
+
re-renders the template; <code>@Watch('count')</code> emits
|
|
227
|
+
the event picked up on the right.
|
|
228
|
+
</p>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<cts-event-log
|
|
232
|
+
class="playground__log"
|
|
233
|
+
label="counter.change"
|
|
234
|
+
events="counter.change"
|
|
235
|
+
empty="Click the counter to dispatch counter.change events."
|
|
236
|
+
></cts-event-log>
|
|
237
|
+
</div>
|
|
238
|
+
</article>
|
|
239
|
+
|
|
240
|
+
<!-- Todo dashboard playground -->
|
|
241
|
+
<article class="playground playground--todo" aria-labelledby="demo-todo-title">
|
|
242
|
+
<header class="playground__head">
|
|
243
|
+
<div>
|
|
244
|
+
<span class="playground__kicker">Demo · 02</span>
|
|
245
|
+
<h3 id="demo-todo-title" class="playground__title">Sprint board — composition, deep state, custom events</h3>
|
|
246
|
+
<p class="playground__sub">
|
|
247
|
+
Four cooperating elements: a parent dashboard that owns the source
|
|
248
|
+
of truth, plus child stats, filters, and item rows that talk back
|
|
249
|
+
through bubbling, composed events. Mutate a nested array — the
|
|
250
|
+
parent re-renders.
|
|
251
|
+
</p>
|
|
252
|
+
</div>
|
|
253
|
+
<a class="playground__source" href="https://github.com/geocine/custom-elements-ts/blob/master/demos/todo-dashboard/todo-dashboard.element.ts">
|
|
254
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" width="14" height="14"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>
|
|
255
|
+
View source
|
|
256
|
+
</a>
|
|
257
|
+
</header>
|
|
258
|
+
|
|
259
|
+
<div class="playground__body playground__body--stack">
|
|
260
|
+
<div class="stage stage--todo">
|
|
261
|
+
<cts-todo-dashboard heading="Sprint board"></cts-todo-dashboard>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<cts-event-log
|
|
265
|
+
class="playground__log playground__log--wide"
|
|
266
|
+
label="todo events"
|
|
267
|
+
events="todo.change,todo.toggle,todo.labelchange,todo.remove,filter.change,goal.change"
|
|
268
|
+
empty="Add, complete, edit, or remove a task to see events bubble up here."
|
|
269
|
+
></cts-event-log>
|
|
270
|
+
</div>
|
|
271
|
+
</article>
|
|
272
|
+
</div>
|
|
273
|
+
</section>
|
|
274
|
+
|
|
275
|
+
<!-- ─── API at-a-glance ─────────────────────────────────── -->
|
|
276
|
+
<section class="api" id="api">
|
|
277
|
+
<div class="container">
|
|
278
|
+
<div class="api__head">
|
|
279
|
+
<div>
|
|
280
|
+
<span class="eyebrow"><span class="dot"></span> The API</span>
|
|
281
|
+
<h2 class="api__title">Seven decorators. That's the whole library.</h2>
|
|
282
|
+
</div>
|
|
283
|
+
<p class="api__lede">
|
|
284
|
+
Every decorator maps directly onto a built-in browser primitive —
|
|
285
|
+
attributes, properties, events, MutationObserver. No magic, no virtual DOM,
|
|
286
|
+
no compiler required.
|
|
287
|
+
</p>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<div class="api__grid">
|
|
291
|
+
<div class="api-card">
|
|
292
|
+
<div class="api-card__name">@CustomElement<span class="target">class</span></div>
|
|
293
|
+
<p class="api-card__desc">Registers the class as a native custom element with a tag, optional template, and styles.</p>
|
|
294
|
+
</div>
|
|
295
|
+
<div class="api-card">
|
|
296
|
+
<div class="api-card__name">@Prop<span class="target">property</span></div>
|
|
297
|
+
<p class="api-card__desc">Reflects a primitive between attribute and property; passes objects and arrays through untouched.</p>
|
|
298
|
+
</div>
|
|
299
|
+
<div class="api-card">
|
|
300
|
+
<div class="api-card__name">@State<span class="target">property</span></div>
|
|
301
|
+
<p class="api-card__desc">Internal reactive state for <code>render()</code> components. Deeply proxied — mutate nested arrays and objects.</p>
|
|
302
|
+
</div>
|
|
303
|
+
<div class="api-card">
|
|
304
|
+
<div class="api-card__name">@Toggle<span class="target">property</span></div>
|
|
305
|
+
<p class="api-card__desc">A boolean attribute that follows the W3C spec — present, "true", or "false".</p>
|
|
306
|
+
</div>
|
|
307
|
+
<div class="api-card">
|
|
308
|
+
<div class="api-card__name">@Watch<span class="target">method</span></div>
|
|
309
|
+
<p class="api-card__desc">Runs a method whenever a watched property changes — from either side of the bridge.</p>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="api-card">
|
|
312
|
+
<div class="api-card__name">@Listen<span class="target">method</span></div>
|
|
313
|
+
<p class="api-card__desc">Wires up DOM events on the host or on any selector inside the shadow tree.</p>
|
|
314
|
+
</div>
|
|
315
|
+
<div class="api-card">
|
|
316
|
+
<div class="api-card__name">@Dispatch<span class="target">property</span></div>
|
|
317
|
+
<p class="api-card__desc">Declares a typed CustomEvent emitter — call <code>.emit()</code> to fire it with a payload.</p>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
</section>
|
|
322
|
+
</main>
|
|
323
|
+
|
|
324
|
+
<!-- ─── Footer ────────────────────────────────────────────── -->
|
|
325
|
+
<footer class="footer">
|
|
326
|
+
<div class="container footer__inner">
|
|
327
|
+
<span>MIT licensed · Maintained by Aivan Monceller & contributors</span>
|
|
328
|
+
<span class="footer__links">
|
|
329
|
+
<a href="https://github.com/geocine/custom-elements-ts">GitHub</a>
|
|
330
|
+
<a href="https://www.npmjs.com/package/custom-elements-ts">npm</a>
|
|
331
|
+
<a href="https://github.com/geocine/custom-elements-ts/issues">Issues</a>
|
|
332
|
+
<a href="https://github.com/geocine/custom-elements-ts/blob/master/LICENSE">License</a>
|
|
333
|
+
</span>
|
|
334
|
+
</div>
|
|
335
|
+
</footer>
|
|
336
|
+
|
|
337
|
+
<!-- Toast notifications (the element subscribes to `cts:toast` document events) -->
|
|
338
|
+
<cts-toast></cts-toast>
|
|
339
|
+
|
|
340
|
+
<script src="//unpkg.com/prismjs@1.29.0/prism.js"></script>
|
|
341
|
+
<script src="//unpkg.com/prismjs@1.29.0/components/prism-typescript.min.js"></script>
|
|
342
|
+
<script src="//unpkg.com/@webcomponents/custom-elements/src/native-shim.js"></script>
|
|
343
|
+
<script src="//unpkg.com/@webcomponents/custom-elements/custom-elements.min.js"></script>
|
|
344
|
+
<script src="site.umd.js"></script>
|
|
345
|
+
</body>
|
|
346
|
+
</html>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Site-only chrome / utility components.
|
|
2
|
+
export * from './code-example/code-example.element';
|
|
3
|
+
export * from './message/message.element';
|
|
4
|
+
export * from './toast/toast.element';
|
|
5
|
+
export * from './event-log/event-log.element';
|
|
6
|
+
|
|
7
|
+
// Live demos hosted from sibling demo folders so visitors can interact
|
|
8
|
+
// with the real components, not screenshots.
|
|
9
|
+
export * from '../counter/counter.element';
|
|
10
|
+
export * from '../todo-dashboard/todo-stats.element';
|
|
11
|
+
export * from '../todo-dashboard/todo-filters.element';
|
|
12
|
+
export * from '../todo-dashboard/todo-item.element';
|
|
13
|
+
export * from '../todo-dashboard/todo-dashboard.element';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
margin: 18px 22px 6px;
|
|
4
|
+
--bg: rgba(255, 255, 255, 0.025);
|
|
5
|
+
--bg-hover: rgba(255, 255, 255, 0.05);
|
|
6
|
+
--line: rgba(255, 255, 255, 0.10);
|
|
7
|
+
--line-hover: rgba(49, 120, 198, 0.45);
|
|
8
|
+
--fg: #e6ecf3;
|
|
9
|
+
--fg-prompt: #9cc0eb;
|
|
10
|
+
--fg-icon: #5b6370;
|
|
11
|
+
--fg-icon-hover: #9cc0eb;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.row {
|
|
15
|
+
display: grid;
|
|
16
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
17
|
+
align-items: center;
|
|
18
|
+
gap: 10px;
|
|
19
|
+
padding: 13px 14px;
|
|
20
|
+
font-family: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular,
|
|
21
|
+
Menlo, Consolas, "Courier New", monospace;
|
|
22
|
+
font-size: 13.5px;
|
|
23
|
+
font-weight: 500;
|
|
24
|
+
color: var(--fg);
|
|
25
|
+
background: var(--bg);
|
|
26
|
+
border: 1px solid var(--line);
|
|
27
|
+
border-radius: 3px;
|
|
28
|
+
cursor: pointer;
|
|
29
|
+
user-select: none;
|
|
30
|
+
outline: none;
|
|
31
|
+
transition: background 220ms cubic-bezier(0.22, 1, 0.36, 1),
|
|
32
|
+
border-color 220ms cubic-bezier(0.22, 1, 0.36, 1),
|
|
33
|
+
transform 140ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.row:hover {
|
|
37
|
+
background: var(--bg-hover);
|
|
38
|
+
border-color: var(--line-hover);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.row:active { transform: translateY(1px); }
|
|
42
|
+
|
|
43
|
+
.row:focus-visible {
|
|
44
|
+
border-color: var(--line-hover);
|
|
45
|
+
box-shadow: 0 0 0 2px rgba(49, 120, 198, 0.45);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.prompt {
|
|
49
|
+
color: var(--fg-prompt);
|
|
50
|
+
font-weight: 600;
|
|
51
|
+
opacity: 0.95;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.cmd {
|
|
55
|
+
font-family: inherit;
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
text-overflow: ellipsis;
|
|
58
|
+
white-space: nowrap;
|
|
59
|
+
min-width: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.icon {
|
|
63
|
+
display: grid;
|
|
64
|
+
place-items: center;
|
|
65
|
+
color: var(--fg-icon);
|
|
66
|
+
transition: color 220ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.icon svg {
|
|
70
|
+
width: 14px;
|
|
71
|
+
height: 14px;
|
|
72
|
+
display: block;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.row:hover .icon { color: var(--fg-icon-hover); }
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { CustomElement, Prop, Listen, Dispatch, DispatchEmitter } from 'custom-elements-ts';
|
|
2
|
+
|
|
3
|
+
@CustomElement({
|
|
4
|
+
tag: 'cts-message',
|
|
5
|
+
template: `
|
|
6
|
+
<div class="row" role="button" tabindex="0" aria-label="Copy install command to clipboard">
|
|
7
|
+
<span class="prompt" aria-hidden="true">$</span>
|
|
8
|
+
<code class="cmd"></code>
|
|
9
|
+
<span class="icon" aria-hidden="true">
|
|
10
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
11
|
+
<rect x="9" y="9" width="13" height="13" rx="1"/>
|
|
12
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
|
13
|
+
</svg>
|
|
14
|
+
</span>
|
|
15
|
+
</div>
|
|
16
|
+
`,
|
|
17
|
+
styleUrl: './message.element.scss',
|
|
18
|
+
})
|
|
19
|
+
export class MessageElement extends HTMLElement {
|
|
20
|
+
@Prop() message!: string;
|
|
21
|
+
|
|
22
|
+
@Dispatch('cts:toast') toast!: DispatchEmitter;
|
|
23
|
+
|
|
24
|
+
@Listen('click')
|
|
25
|
+
async handleClick() {
|
|
26
|
+
const text = this.message ?? '';
|
|
27
|
+
let ok = true;
|
|
28
|
+
try {
|
|
29
|
+
if (navigator.clipboard?.writeText) {
|
|
30
|
+
await navigator.clipboard.writeText(text);
|
|
31
|
+
} else {
|
|
32
|
+
ok = this.fallbackCopy(text);
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
ok = false;
|
|
36
|
+
}
|
|
37
|
+
this.toast.emit({
|
|
38
|
+
bubbles: true,
|
|
39
|
+
composed: true,
|
|
40
|
+
detail: ok
|
|
41
|
+
? { title: 'Copied to clipboard', message: text, kind: 'success' }
|
|
42
|
+
: { title: "Couldn't copy", message: 'Copy the command manually.', kind: 'error' },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Listen('keydown')
|
|
47
|
+
handleKey(e: KeyboardEvent) {
|
|
48
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
this.handleClick();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
connectedCallback() {
|
|
55
|
+
const cmd = this.shadowRoot?.querySelector('.cmd');
|
|
56
|
+
if (cmd) cmd.textContent = this.message;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Older browsers / non-secure contexts: fall back to the legacy copy path.
|
|
60
|
+
private fallbackCopy(text: string): boolean {
|
|
61
|
+
const ta = document.createElement('textarea');
|
|
62
|
+
ta.value = text;
|
|
63
|
+
ta.style.position = 'fixed';
|
|
64
|
+
ta.style.opacity = '0';
|
|
65
|
+
document.body.appendChild(ta);
|
|
66
|
+
ta.select();
|
|
67
|
+
let ok = false;
|
|
68
|
+
try {
|
|
69
|
+
ok = document.execCommand('copy');
|
|
70
|
+
} catch {
|
|
71
|
+
ok = false;
|
|
72
|
+
}
|
|
73
|
+
document.body.removeChild(ta);
|
|
74
|
+
return ok;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
Binary file
|