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,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 -->
|