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.
- package/.eslintrc.json +47 -0
- package/.github/workflows/ci.yml +49 -0
- package/.prettierrc +7 -0
- package/LICENSE +20 -0
- package/README.md +279 -37
- package/assets/readme-header.png +0 -0
- package/assets/social-preview.jpg +0 -0
- 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 +123 -0
- package/demos/site/favicon.svg +14 -0
- package/demos/site/index.html +368 -0
- package/demos/site/index.ts +19 -0
- package/demos/site/llms.txt +53 -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/scroll-restoration.ts +28 -0
- package/demos/site/source-toggle/source-toggle.ts +88 -0
- package/demos/site/source-viewer/source-viewer.element.scss +292 -0
- package/demos/site/source-viewer/source-viewer.element.ts +138 -0
- package/demos/site/source-viewer/sources.generated.ts +11 -0
- package/demos/site/styles/site.css +1135 -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 +1150 -0
- package/demos/todo-dashboard/todo-dashboard.element.ts +333 -0
- package/demos/todo-dashboard/todo-filters.element.ts +54 -0
- package/demos/todo-dashboard/todo-item.element.ts +127 -0
- package/demos/todo-dashboard/todo-stats.element.ts +189 -0
- package/package.json +73 -24
- package/src/custom-element.ts +206 -0
- package/src/index.ts +8 -0
- package/src/listen.ts +70 -0
- package/src/prop.ts +92 -0
- package/src/signal.ts +66 -0
- package/src/state.ts +141 -0
- package/src/template-runtime.ts +783 -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/map-shallow-state.spec.ts +184 -0
- package/tests/prop.spec.ts +118 -0
- package/tests/signal.spec.ts +117 -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 +128 -0
- package/tools/bundle.js +167 -0
- package/tools/generate-sources.js +76 -0
- package/tools/rollup-config.js +70 -0
- package/tools/start.js +194 -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/index.d.ts +0 -5
- 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,234 @@
|
|
|
1
|
+
/* ─────────────────────────────────────────────────────────────
|
|
2
|
+
cts-counter
|
|
3
|
+
A single-button card showcasing @State(). Sharp radii, Geist
|
|
4
|
+
typography, TS-blue accent. Aligned with demos/site tokens.
|
|
5
|
+
───────────────────────────────────────────────────────────── */
|
|
6
|
+
|
|
7
|
+
:host {
|
|
8
|
+
--bg: #0a0c10;
|
|
9
|
+
--bg-elev-1: #11141a;
|
|
10
|
+
--line: rgba(255, 255, 255, 0.06);
|
|
11
|
+
--line-strong: rgba(255, 255, 255, 0.10);
|
|
12
|
+
--fg: #e6ecf3;
|
|
13
|
+
--fg-muted: #97a1ad;
|
|
14
|
+
--fg-subtle: #5b6370;
|
|
15
|
+
--accent: #3178c6;
|
|
16
|
+
--accent-bright: #4189d6;
|
|
17
|
+
--accent-fg: #9cc0eb;
|
|
18
|
+
--accent-soft: rgba(49, 120, 198, 0.14);
|
|
19
|
+
--accent-line: rgba(49, 120, 198, 0.42);
|
|
20
|
+
|
|
21
|
+
--radius-xs: 2px;
|
|
22
|
+
--radius-sm: 2px;
|
|
23
|
+
--radius-md: 3px;
|
|
24
|
+
--radius-lg: 4px;
|
|
25
|
+
|
|
26
|
+
--ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
27
|
+
--dur-fast: 140ms;
|
|
28
|
+
--dur: 220ms;
|
|
29
|
+
|
|
30
|
+
display: inline-block;
|
|
31
|
+
color: var(--fg);
|
|
32
|
+
font-family:
|
|
33
|
+
"Geist", "Inter", ui-sans-serif, system-ui, -apple-system,
|
|
34
|
+
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
35
|
+
-webkit-font-smoothing: antialiased;
|
|
36
|
+
text-rendering: optimizeLegibility;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
* {
|
|
40
|
+
box-sizing: border-box;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* ── Card button ─────────────────────────────────────────── */
|
|
44
|
+
|
|
45
|
+
.card {
|
|
46
|
+
position: relative;
|
|
47
|
+
display: grid;
|
|
48
|
+
gap: 18px;
|
|
49
|
+
width: 320px;
|
|
50
|
+
padding: 22px 24px 22px;
|
|
51
|
+
margin: 0;
|
|
52
|
+
border: 1px solid var(--line-strong);
|
|
53
|
+
border-radius: var(--radius-lg);
|
|
54
|
+
background:
|
|
55
|
+
radial-gradient(120% 100% at 0% 0%, rgba(49, 120, 198, 0.07) 0%, transparent 55%),
|
|
56
|
+
linear-gradient(180deg, var(--bg-elev-1) 0%, #0f131b 100%);
|
|
57
|
+
color: inherit;
|
|
58
|
+
font: inherit;
|
|
59
|
+
text-align: left;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
box-shadow:
|
|
63
|
+
0 1px 0 rgba(255, 255, 255, 0.04) inset,
|
|
64
|
+
0 22px 50px -28px rgba(0, 0, 0, 0.65),
|
|
65
|
+
0 8px 24px -12px rgba(0, 0, 0, 0.45);
|
|
66
|
+
transition:
|
|
67
|
+
border-color var(--dur-fast) var(--ease),
|
|
68
|
+
box-shadow var(--dur-fast) var(--ease),
|
|
69
|
+
transform var(--dur-fast) var(--ease);
|
|
70
|
+
animation: card-in 520ms var(--ease) both;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.card::before {
|
|
74
|
+
content: "";
|
|
75
|
+
position: absolute;
|
|
76
|
+
left: 0;
|
|
77
|
+
right: 0;
|
|
78
|
+
bottom: 0;
|
|
79
|
+
height: 2px;
|
|
80
|
+
background: linear-gradient(
|
|
81
|
+
90deg,
|
|
82
|
+
transparent 0%,
|
|
83
|
+
var(--accent-line) 30%,
|
|
84
|
+
var(--accent-bright) 50%,
|
|
85
|
+
var(--accent-line) 70%,
|
|
86
|
+
transparent 100%
|
|
87
|
+
);
|
|
88
|
+
opacity: 0;
|
|
89
|
+
transform: translateX(-30%);
|
|
90
|
+
transition:
|
|
91
|
+
opacity var(--dur) var(--ease),
|
|
92
|
+
transform var(--dur-slow, 420ms) var(--ease);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.card:hover {
|
|
96
|
+
border-color: rgba(255, 255, 255, 0.18);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.card:hover::before {
|
|
100
|
+
opacity: 1;
|
|
101
|
+
transform: translateX(0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.card:focus-visible {
|
|
105
|
+
outline: none;
|
|
106
|
+
border-color: var(--accent-line);
|
|
107
|
+
box-shadow:
|
|
108
|
+
0 1px 0 rgba(255, 255, 255, 0.04) inset,
|
|
109
|
+
0 0 0 3px var(--accent-soft),
|
|
110
|
+
0 22px 50px -28px rgba(0, 0, 0, 0.65);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.card:active {
|
|
114
|
+
transform: translateY(1px);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@keyframes card-in {
|
|
118
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
119
|
+
to { opacity: 1; transform: translateY(0); }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* ── Kicker ──────────────────────────────────────────────── */
|
|
123
|
+
|
|
124
|
+
.kicker {
|
|
125
|
+
display: inline-flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
gap: 8px;
|
|
128
|
+
color: var(--fg-muted);
|
|
129
|
+
font-family: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular,
|
|
130
|
+
Menlo, Consolas, "Courier New", monospace;
|
|
131
|
+
font-size: 11px;
|
|
132
|
+
font-weight: 500;
|
|
133
|
+
letter-spacing: 0.14em;
|
|
134
|
+
text-transform: uppercase;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.kicker-dot {
|
|
138
|
+
width: 6px;
|
|
139
|
+
height: 6px;
|
|
140
|
+
background: var(--accent);
|
|
141
|
+
box-shadow: 0 0 10px var(--accent-line);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ── Display ─────────────────────────────────────────────── */
|
|
145
|
+
|
|
146
|
+
.display {
|
|
147
|
+
display: inline-flex;
|
|
148
|
+
align-items: baseline;
|
|
149
|
+
gap: 0;
|
|
150
|
+
font-family: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular,
|
|
151
|
+
Menlo, Consolas, "Courier New", monospace;
|
|
152
|
+
font-size: 76px;
|
|
153
|
+
font-weight: 600;
|
|
154
|
+
line-height: 0.95;
|
|
155
|
+
letter-spacing: -0.045em;
|
|
156
|
+
font-variant-numeric: tabular-nums;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.display-mute {
|
|
160
|
+
color: var(--fg-subtle);
|
|
161
|
+
opacity: 0.35;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.display-num {
|
|
165
|
+
color: var(--fg);
|
|
166
|
+
background: linear-gradient(180deg, var(--fg) 0%, #c8d3e0 100%);
|
|
167
|
+
-webkit-background-clip: text;
|
|
168
|
+
background-clip: text;
|
|
169
|
+
-webkit-text-fill-color: transparent;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* ── Hint row ────────────────────────────────────────────── */
|
|
173
|
+
|
|
174
|
+
.hint {
|
|
175
|
+
display: inline-flex;
|
|
176
|
+
align-items: center;
|
|
177
|
+
justify-content: space-between;
|
|
178
|
+
gap: 12px;
|
|
179
|
+
padding-top: 14px;
|
|
180
|
+
border-top: 1px solid var(--line);
|
|
181
|
+
color: var(--fg-muted);
|
|
182
|
+
font-size: 12px;
|
|
183
|
+
letter-spacing: -0.005em;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.hint-text {
|
|
187
|
+
color: var(--fg-muted);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.hint-chip {
|
|
191
|
+
display: inline-flex;
|
|
192
|
+
align-items: center;
|
|
193
|
+
height: 22px;
|
|
194
|
+
padding: 0 8px;
|
|
195
|
+
border: 1px solid var(--line-strong);
|
|
196
|
+
border-radius: var(--radius-xs);
|
|
197
|
+
background: rgba(255, 255, 255, 0.02);
|
|
198
|
+
color: var(--accent-fg);
|
|
199
|
+
font-family: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular,
|
|
200
|
+
Menlo, Consolas, "Courier New", monospace;
|
|
201
|
+
font-size: 11px;
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
letter-spacing: 0.02em;
|
|
204
|
+
transition:
|
|
205
|
+
border-color var(--dur-fast) var(--ease),
|
|
206
|
+
background var(--dur-fast) var(--ease),
|
|
207
|
+
transform var(--dur-fast) var(--ease);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.card:hover .hint-chip {
|
|
211
|
+
border-color: var(--accent-line);
|
|
212
|
+
background: var(--accent-soft);
|
|
213
|
+
transform: translateY(-1px);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.card:active .hint-chip {
|
|
217
|
+
transform: translateY(0);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* ── Responsive ──────────────────────────────────────────── */
|
|
221
|
+
|
|
222
|
+
@media (max-width: 380px) {
|
|
223
|
+
.card { width: 100%; }
|
|
224
|
+
.display { font-size: 64px; }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@media (prefers-reduced-motion: reduce) {
|
|
228
|
+
.card,
|
|
229
|
+
.card::before,
|
|
230
|
+
.hint-chip {
|
|
231
|
+
animation: none;
|
|
232
|
+
transition: none;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CustomElement,
|
|
3
|
+
Dispatch,
|
|
4
|
+
DispatchEmitter,
|
|
5
|
+
State,
|
|
6
|
+
TemplateResult,
|
|
7
|
+
Watch,
|
|
8
|
+
html,
|
|
9
|
+
} from 'custom-elements-ts';
|
|
10
|
+
|
|
11
|
+
@CustomElement({
|
|
12
|
+
tag: 'cts-counter',
|
|
13
|
+
styleUrl: './counter.element.scss',
|
|
14
|
+
})
|
|
15
|
+
export class CounterElement extends HTMLElement {
|
|
16
|
+
@State() count = 0;
|
|
17
|
+
|
|
18
|
+
@Dispatch('counter.change') counterChange!: DispatchEmitter;
|
|
19
|
+
|
|
20
|
+
@Watch('count')
|
|
21
|
+
handleCountChange(change: { new: number }) {
|
|
22
|
+
this.counterChange.emit({
|
|
23
|
+
bubbles: true,
|
|
24
|
+
composed: true,
|
|
25
|
+
detail: {
|
|
26
|
+
count: change.new,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
render(): TemplateResult {
|
|
32
|
+
const display = this.format(this.count);
|
|
33
|
+
return html`
|
|
34
|
+
<button class="card" type="button" @click=${this.increment}>
|
|
35
|
+
<span class="kicker">
|
|
36
|
+
<span class="kicker-dot"></span>
|
|
37
|
+
counter · @State()
|
|
38
|
+
</span>
|
|
39
|
+
|
|
40
|
+
<span class="display" aria-live="polite">
|
|
41
|
+
<span class="display-mute">${display.mute}</span
|
|
42
|
+
><span class="display-num">${display.num}</span>
|
|
43
|
+
</span>
|
|
44
|
+
|
|
45
|
+
<span class="hint">
|
|
46
|
+
<span class="hint-text">tap anywhere to count</span>
|
|
47
|
+
<span class="hint-chip">+1</span>
|
|
48
|
+
</span>
|
|
49
|
+
</button>
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private increment() {
|
|
54
|
+
this.count++;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Pad to at least 4 digits and split into a muted lead and a bright tail
|
|
58
|
+
// so the number reads like a stopwatch — e.g. 0042, 0420, 4200, 42000.
|
|
59
|
+
private format(value: number): { mute: string; num: string } {
|
|
60
|
+
const raw = String(value);
|
|
61
|
+
const padded = raw.length >= 4 ? raw : `${'0000'.slice(raw.length)}${raw}`;
|
|
62
|
+
const tail = padded.length - raw.length;
|
|
63
|
+
return {
|
|
64
|
+
mute: padded.slice(0, tail),
|
|
65
|
+
num: padded.slice(tail),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
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
|
+
<title>Counter — custom-elements-ts demo</title>
|
|
9
|
+
|
|
10
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
11
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
12
|
+
<link
|
|
13
|
+
rel="stylesheet"
|
|
14
|
+
href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600;700&display=swap"
|
|
15
|
+
/>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
:root {
|
|
19
|
+
--bg: #0a0c10;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
* {
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
html,
|
|
27
|
+
body {
|
|
28
|
+
margin: 0;
|
|
29
|
+
background: var(--bg);
|
|
30
|
+
color: #e6ecf3;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
body {
|
|
34
|
+
position: relative;
|
|
35
|
+
min-height: 100dvh;
|
|
36
|
+
display: grid;
|
|
37
|
+
place-items: center;
|
|
38
|
+
padding: 32px 24px;
|
|
39
|
+
background:
|
|
40
|
+
radial-gradient(60% 50% at 50% -10%, rgba(49, 120, 198, 0.18) 0%, transparent 60%),
|
|
41
|
+
radial-gradient(40% 40% at 50% 110%, rgba(49, 120, 198, 0.1) 0%, transparent 60%),
|
|
42
|
+
var(--bg);
|
|
43
|
+
font-family:
|
|
44
|
+
'Geist',
|
|
45
|
+
'Inter',
|
|
46
|
+
ui-sans-serif,
|
|
47
|
+
system-ui,
|
|
48
|
+
-apple-system,
|
|
49
|
+
BlinkMacSystemFont,
|
|
50
|
+
'Segoe UI',
|
|
51
|
+
sans-serif;
|
|
52
|
+
-webkit-font-smoothing: antialiased;
|
|
53
|
+
text-rendering: optimizeLegibility;
|
|
54
|
+
overflow-x: hidden;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
body::before {
|
|
58
|
+
content: '';
|
|
59
|
+
position: fixed;
|
|
60
|
+
inset: 0;
|
|
61
|
+
pointer-events: none;
|
|
62
|
+
background-image:
|
|
63
|
+
linear-gradient(to right, rgba(255, 255, 255, 0.025) 1px, transparent 1px),
|
|
64
|
+
linear-gradient(to bottom, rgba(255, 255, 255, 0.025) 1px, transparent 1px);
|
|
65
|
+
background-size: 56px 56px;
|
|
66
|
+
mask-image: radial-gradient(ellipse 60% 60% at 50% 50%, #000 0%, transparent 75%);
|
|
67
|
+
-webkit-mask-image: radial-gradient(ellipse 60% 60% at 50% 50%, #000 0%, transparent 75%);
|
|
68
|
+
z-index: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.stage {
|
|
72
|
+
position: relative;
|
|
73
|
+
z-index: 1;
|
|
74
|
+
display: grid;
|
|
75
|
+
gap: 22px;
|
|
76
|
+
justify-items: center;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.corner {
|
|
80
|
+
position: fixed;
|
|
81
|
+
top: 24px;
|
|
82
|
+
left: 24px;
|
|
83
|
+
z-index: 2;
|
|
84
|
+
display: inline-flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
gap: 10px;
|
|
87
|
+
color: #97a1ad;
|
|
88
|
+
font-family:
|
|
89
|
+
'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
90
|
+
font-size: 11px;
|
|
91
|
+
letter-spacing: 0.08em;
|
|
92
|
+
text-transform: uppercase;
|
|
93
|
+
text-decoration: none;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.corner svg {
|
|
97
|
+
color: #3178c6;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.corner:hover {
|
|
101
|
+
color: #e6ecf3;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.caption {
|
|
105
|
+
max-width: 320px;
|
|
106
|
+
margin: 0;
|
|
107
|
+
color: #5b6370;
|
|
108
|
+
font-size: 12px;
|
|
109
|
+
line-height: 1.55;
|
|
110
|
+
text-align: center;
|
|
111
|
+
text-wrap: balance;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.caption code {
|
|
115
|
+
padding: 1px 5px;
|
|
116
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
117
|
+
border-radius: 2px;
|
|
118
|
+
background: rgba(255, 255, 255, 0.02);
|
|
119
|
+
color: #97a1ad;
|
|
120
|
+
font-family:
|
|
121
|
+
'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
122
|
+
font-size: 11px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.observer {
|
|
126
|
+
display: inline-flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
gap: 8px;
|
|
129
|
+
min-height: 32px;
|
|
130
|
+
padding: 0 10px;
|
|
131
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
132
|
+
border-radius: 2px;
|
|
133
|
+
background: rgba(255, 255, 255, 0.02);
|
|
134
|
+
color: #97a1ad;
|
|
135
|
+
font-family:
|
|
136
|
+
'Geist Mono', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
137
|
+
font-size: 11px;
|
|
138
|
+
letter-spacing: 0.02em;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.observer output {
|
|
142
|
+
color: #9cc0eb;
|
|
143
|
+
font-weight: 600;
|
|
144
|
+
font-variant-numeric: tabular-nums;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
cts-counter {
|
|
148
|
+
position: relative;
|
|
149
|
+
z-index: 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@media (max-width: 380px) {
|
|
153
|
+
.corner {
|
|
154
|
+
top: 16px;
|
|
155
|
+
left: 16px;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
159
|
+
</head>
|
|
160
|
+
<body>
|
|
161
|
+
<a class="corner" href="https://github.com/geocine/custom-elements-ts">
|
|
162
|
+
<svg
|
|
163
|
+
viewBox="0 0 24 24"
|
|
164
|
+
fill="none"
|
|
165
|
+
stroke="currentColor"
|
|
166
|
+
stroke-width="2.25"
|
|
167
|
+
stroke-linecap="round"
|
|
168
|
+
stroke-linejoin="round"
|
|
169
|
+
width="14"
|
|
170
|
+
height="14"
|
|
171
|
+
aria-hidden="true"
|
|
172
|
+
>
|
|
173
|
+
<path d="M8.5 7.5 4 12l4.5 4.5" />
|
|
174
|
+
<path d="M15.5 7.5 20 12l-4.5 4.5" />
|
|
175
|
+
<path d="M14 5l-4 14" />
|
|
176
|
+
</svg>
|
|
177
|
+
custom-elements-ts · counter demo
|
|
178
|
+
</a>
|
|
179
|
+
|
|
180
|
+
<div class="stage">
|
|
181
|
+
<cts-counter></cts-counter>
|
|
182
|
+
<div class="observer" aria-live="polite">
|
|
183
|
+
<span>counter.change</span>
|
|
184
|
+
<output id="counter-signal">0</output>
|
|
185
|
+
</div>
|
|
186
|
+
<p class="caption">
|
|
187
|
+
Click the card. The element owns <code>@State()</code> and emits
|
|
188
|
+
<code>counter.change</code> for outside consumers.
|
|
189
|
+
</p>
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
<script src="node_modules/@webcomponents/custom-elements/src/native-shim.js"></script>
|
|
193
|
+
<script src="node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
|
|
194
|
+
<script src="counter.umd.js"></script>
|
|
195
|
+
<script>
|
|
196
|
+
const counter = document.querySelector('cts-counter');
|
|
197
|
+
const signal = document.querySelector('#counter-signal');
|
|
198
|
+
|
|
199
|
+
counter.addEventListener('counter.change', (event) => {
|
|
200
|
+
signal.value = event.detail.count;
|
|
201
|
+
});
|
|
202
|
+
</script>
|
|
203
|
+
</body>
|
|
204
|
+
</html>
|
|
205
|
+
<!-- livereload -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './counter.element';
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/* Lives inside <article class="window">; the chrome owns background + radius. */
|
|
2
|
+
|
|
3
|
+
:host {
|
|
4
|
+
display: block;
|
|
5
|
+
padding: 22px 26px 26px;
|
|
6
|
+
font-family: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular,
|
|
7
|
+
Menlo, Consolas, "Courier New", monospace;
|
|
8
|
+
font-size: 13.5px;
|
|
9
|
+
line-height: 1.65;
|
|
10
|
+
color: #d6dce5;
|
|
11
|
+
background: transparent;
|
|
12
|
+
overflow-x: auto;
|
|
13
|
+
max-height: 520px;
|
|
14
|
+
-webkit-font-smoothing: antialiased;
|
|
15
|
+
|
|
16
|
+
@media (min-width: 980px) {
|
|
17
|
+
font-size: 14px;
|
|
18
|
+
padding: 28px 32px 32px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Custom scrollbar so the code area doesn't ship a chunky default bar */
|
|
22
|
+
scrollbar-width: thin;
|
|
23
|
+
scrollbar-color: rgba(255, 255, 255, 0.10) transparent;
|
|
24
|
+
|
|
25
|
+
&::-webkit-scrollbar {
|
|
26
|
+
height: 8px;
|
|
27
|
+
width: 8px;
|
|
28
|
+
}
|
|
29
|
+
&::-webkit-scrollbar-thumb {
|
|
30
|
+
background: rgba(255, 255, 255, 0.10);
|
|
31
|
+
border-radius: 2px;
|
|
32
|
+
}
|
|
33
|
+
&::-webkit-scrollbar-track { background: transparent; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pre,
|
|
37
|
+
pre[class*="language-"] {
|
|
38
|
+
margin: 0;
|
|
39
|
+
padding: 0;
|
|
40
|
+
background: transparent;
|
|
41
|
+
border: 0;
|
|
42
|
+
border-radius: 0;
|
|
43
|
+
color: inherit;
|
|
44
|
+
font-family: inherit;
|
|
45
|
+
font-size: inherit;
|
|
46
|
+
line-height: inherit;
|
|
47
|
+
text-shadow: none;
|
|
48
|
+
white-space: pre;
|
|
49
|
+
word-spacing: normal;
|
|
50
|
+
word-break: normal;
|
|
51
|
+
-moz-tab-size: 2;
|
|
52
|
+
-o-tab-size: 2;
|
|
53
|
+
tab-size: 2;
|
|
54
|
+
hyphens: none;
|
|
55
|
+
overflow: visible;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
code,
|
|
59
|
+
code[class*="language-"] {
|
|
60
|
+
background: transparent;
|
|
61
|
+
color: inherit;
|
|
62
|
+
font-family: inherit;
|
|
63
|
+
font-size: inherit;
|
|
64
|
+
line-height: inherit;
|
|
65
|
+
text-shadow: none;
|
|
66
|
+
white-space: pre;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* ─── Prism token theme — VS Code "Dark Modern" inspired, TS blue led ─── */
|
|
70
|
+
|
|
71
|
+
.token {
|
|
72
|
+
&.comment,
|
|
73
|
+
&.prolog,
|
|
74
|
+
&.doctype,
|
|
75
|
+
&.cdata {
|
|
76
|
+
color: #6e7681;
|
|
77
|
+
font-style: italic;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&.punctuation {
|
|
81
|
+
color: #c9d1d9;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* identifier-blue — properties, tags, attribute-name (e.g. tag:, template:) */
|
|
85
|
+
&.property,
|
|
86
|
+
&.tag,
|
|
87
|
+
&.attr-name {
|
|
88
|
+
color: #9cdcfe;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* TS-blue — keywords (import, export, class, extends, new) */
|
|
92
|
+
&.keyword,
|
|
93
|
+
&.boolean {
|
|
94
|
+
color: #4189d6;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* teal — class names, types */
|
|
98
|
+
&.class-name {
|
|
99
|
+
color: #4ec9b0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
&.constant,
|
|
103
|
+
&.symbol {
|
|
104
|
+
color: #4189d6;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* light green — numbers */
|
|
108
|
+
&.number {
|
|
109
|
+
color: #b5cea8;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* warm muted — strings (the only "warm" color, used sparingly) */
|
|
113
|
+
&.string,
|
|
114
|
+
&.char,
|
|
115
|
+
&.attr-value,
|
|
116
|
+
&.builtin,
|
|
117
|
+
&.inserted {
|
|
118
|
+
color: #ce9178;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* selectors stay muted */
|
|
122
|
+
&.selector {
|
|
123
|
+
color: #d7ba7d;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&.variable {
|
|
127
|
+
color: #d6dce5;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&.operator,
|
|
131
|
+
&.entity {
|
|
132
|
+
color: #d6dce5;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
&.url {
|
|
136
|
+
color: #4189d6;
|
|
137
|
+
text-decoration: underline;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
&.atrule {
|
|
141
|
+
color: #c586c0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* function names — soft cream so they don't fight the blues */
|
|
145
|
+
&.function {
|
|
146
|
+
color: #dcdcaa;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
&.regex {
|
|
150
|
+
color: #d16969;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
&.important {
|
|
154
|
+
color: #f47067;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
&.bold { font-weight: 600; }
|
|
159
|
+
&.italic { font-style: italic; }
|
|
160
|
+
&.deleted { color: #f47067; }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.namespace { opacity: .65; }
|
|
164
|
+
|
|
165
|
+
.language-css .token.string,
|
|
166
|
+
.style .token.string {
|
|
167
|
+
color: #ce9178;
|
|
168
|
+
}
|