@transcodes/ui-components 0.3.0 → 0.3.1

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 (46) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +124 -79
  3. package/dist/controllers/animation.controller.js +32 -0
  4. package/dist/controllers/base.controller.js +8 -0
  5. package/dist/controllers/form-validation.controller.js +49 -0
  6. package/dist/controllers/history.controller.js +26 -0
  7. package/dist/controllers/loading.controller.js +27 -0
  8. package/dist/controllers/match-media.controller.js +20 -0
  9. package/dist/controllers/message-bus.controller.js +45 -0
  10. package/dist/controllers/storage.controller.js +40 -0
  11. package/dist/index.js +80 -0
  12. package/dist/primitives/tc-box.js +38 -0
  13. package/dist/primitives/tc-button.js +167 -0
  14. package/dist/primitives/tc-callout.js +86 -0
  15. package/dist/primitives/tc-card.js +76 -0
  16. package/dist/primitives/tc-chip.js +79 -0
  17. package/dist/primitives/tc-container.js +62 -0
  18. package/dist/primitives/tc-divider.js +76 -0
  19. package/dist/primitives/tc-error-message.js +74 -0
  20. package/dist/primitives/tc-form-header.js +120 -0
  21. package/dist/primitives/tc-icon.js +95 -0
  22. package/dist/primitives/tc-input-with-chip.js +242 -0
  23. package/dist/primitives/tc-input.js +262 -0
  24. package/dist/primitives/tc-item-button.js +168 -0
  25. package/dist/primitives/tc-item.js +93 -0
  26. package/dist/primitives/tc-otp-input.js +230 -0
  27. package/dist/primitives/tc-section.js +48 -0
  28. package/dist/primitives/tc-spinner.js +87 -0
  29. package/dist/primitives/tc-symbol.js +56 -0
  30. package/dist/primitives/tc-text.js +145 -0
  31. package/dist/primitives/tc-toast.js +189 -0
  32. package/dist/screens/tc-error-screen.js +119 -0
  33. package/dist/screens/tc-loading-screen.js +77 -0
  34. package/dist/screens/tc-success-screen.js +192 -0
  35. package/dist/styles/shared.js +7 -0
  36. package/dist/widgets/tc-authenticator-card.js +213 -0
  37. package/dist/widgets/tc-floating-button.js +132 -0
  38. package/dist/widgets/tc-iframe-modal.js +263 -0
  39. package/dist/widgets/tc-installation-banner.js +234 -0
  40. package/dist/widgets/tc-ios-installation-guide.js +240 -0
  41. package/dist/widgets/tc-notification-modal.js +230 -0
  42. package/dist/widgets/tc-offline-modal.js +202 -0
  43. package/dist/widgets/tc-page-decoration.js +126 -0
  44. package/package.json +25 -7
  45. package/dist/ui-components.css +0 -1
  46. package/dist/ui-components.js +0 -4981
@@ -0,0 +1,213 @@
1
+ import { LitElement as p, html as l, css as h } from "lit";
2
+ import { property as s, customElement as m } from "lit/decorators.js";
3
+ import { classMap as u } from "lit/directives/class-map.js";
4
+ var b = Object.defineProperty, v = Object.getOwnPropertyDescriptor, a = (r, i, c, n) => {
5
+ for (var t = n > 1 ? void 0 : n ? v(i, c) : i, o = r.length - 1, d; o >= 0; o--)
6
+ (d = r[o]) && (t = (n ? d(i, c, t) : d(t)) || t);
7
+ return n && t && b(i, c, t), t;
8
+ };
9
+ let e = class extends p {
10
+ constructor() {
11
+ super(...arguments), this.name = "", this.type = "passkey", this.lastUsed = "", this.enabled = !0, this.deletable = !0, this.clickable = !1;
12
+ }
13
+ getIconName() {
14
+ switch (this.type) {
15
+ case "passkey":
16
+ return "key";
17
+ case "totp":
18
+ return "totp";
19
+ case "usb":
20
+ return "device";
21
+ case "phone":
22
+ return "phone";
23
+ case "email":
24
+ return "email";
25
+ default:
26
+ return "key";
27
+ }
28
+ }
29
+ handleCardClick() {
30
+ this.clickable && this.enabled && this.dispatchEvent(
31
+ new CustomEvent("tc-click", {
32
+ bubbles: !0,
33
+ composed: !0
34
+ })
35
+ );
36
+ }
37
+ handleDelete(r) {
38
+ r.stopPropagation(), this.dispatchEvent(
39
+ new CustomEvent("tc-delete", {
40
+ bubbles: !0,
41
+ composed: !0,
42
+ detail: { name: this.name, type: this.type }
43
+ })
44
+ );
45
+ }
46
+ render() {
47
+ const r = {
48
+ card: !0,
49
+ clickable: this.clickable && this.enabled,
50
+ disabled: !this.enabled
51
+ };
52
+ return l`
53
+ <div
54
+ part="card"
55
+ class=${u(r)}
56
+ @click=${this.handleCardClick}
57
+ role=${this.clickable ? "button" : "article"}
58
+ tabindex=${this.clickable && this.enabled ? "0" : "-1"}
59
+ >
60
+ <div part="icon" class="icon-container">
61
+ <tc-icon name=${this.getIconName()} size="1.25rem" color="var(--ink-dark)"></tc-icon>
62
+ </div>
63
+ <div part="content" class="content">
64
+ <div part="name" class="name">${this.name}</div>
65
+ ${this.lastUsed ? l`
66
+ <div part="meta" class="meta">
67
+ <span class="last-used">${this.lastUsed}</span>
68
+ </div>
69
+ ` : ""}
70
+ </div>
71
+ <div part="actions" class="actions">
72
+ ${this.enabled ? "" : l`
73
+ <tc-chip class="status-chip" variant="default" size="sm">Disabled</tc-chip>
74
+ `}
75
+ ${this.deletable ? l`
76
+ <button
77
+ part="delete"
78
+ class="delete-btn"
79
+ @click=${this.handleDelete}
80
+ aria-label="Delete ${this.name}"
81
+ >
82
+ <tc-icon name="x" size="1rem"></tc-icon>
83
+ </button>
84
+ ` : ""}
85
+ </div>
86
+ </div>
87
+ `;
88
+ }
89
+ };
90
+ e.styles = h`
91
+ :host {
92
+ display: block;
93
+ width: 100%;
94
+ }
95
+
96
+ .card {
97
+ display: flex;
98
+ align-items: center;
99
+ gap: var(--space-md);
100
+ padding: var(--space-md);
101
+ background: var(--paper-cream);
102
+ border-radius: var(--radius-md);
103
+ transition: var(--transition-fast);
104
+ }
105
+
106
+ .card.clickable {
107
+ cursor: pointer;
108
+ }
109
+
110
+ .card.clickable:hover {
111
+ background: var(--paper-warm);
112
+ }
113
+
114
+ .card.disabled {
115
+ opacity: var(--opacity-disabled);
116
+ }
117
+
118
+ .icon-container {
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ width: 2.5rem;
123
+ height: 2.5rem;
124
+ background: var(--paper-white);
125
+ border-radius: var(--radius-md);
126
+ flex-shrink: 0;
127
+ }
128
+
129
+ .content {
130
+ flex: 1;
131
+ min-width: 0;
132
+ }
133
+
134
+ .name {
135
+ font-weight: 500;
136
+ color: var(--ink-black);
137
+ white-space: nowrap;
138
+ overflow: hidden;
139
+ text-overflow: ellipsis;
140
+ }
141
+
142
+ .meta {
143
+ display: flex;
144
+ align-items: center;
145
+ gap: var(--space-sm);
146
+ margin-top: var(--space-xs);
147
+ }
148
+
149
+ .last-used {
150
+ font-size: var(--font-size-sm);
151
+ color: var(--ink-light);
152
+ }
153
+
154
+ .actions {
155
+ display: flex;
156
+ align-items: center;
157
+ gap: var(--space-sm);
158
+ flex-shrink: 0;
159
+ }
160
+
161
+ .delete-btn {
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ width: 2rem;
166
+ height: 2rem;
167
+ padding: 0;
168
+ background: transparent;
169
+ border: none;
170
+ border-radius: var(--radius-sm);
171
+ color: var(--ink-light);
172
+ cursor: pointer;
173
+ transition: var(--transition-fast);
174
+ }
175
+
176
+ .delete-btn:hover {
177
+ background: var(--alpha-ink04);
178
+ color: var(--error-base);
179
+ }
180
+
181
+ .delete-btn:focus-visible {
182
+ outline: 2px solid var(--accent-primary);
183
+ outline-offset: 2px;
184
+ }
185
+
186
+ .status-chip {
187
+ flex-shrink: 0;
188
+ }
189
+ `;
190
+ a([
191
+ s({ type: String })
192
+ ], e.prototype, "name", 2);
193
+ a([
194
+ s({ type: String })
195
+ ], e.prototype, "type", 2);
196
+ a([
197
+ s({ type: String, attribute: "last-used" })
198
+ ], e.prototype, "lastUsed", 2);
199
+ a([
200
+ s({ type: Boolean })
201
+ ], e.prototype, "enabled", 2);
202
+ a([
203
+ s({ type: Boolean })
204
+ ], e.prototype, "deletable", 2);
205
+ a([
206
+ s({ type: Boolean })
207
+ ], e.prototype, "clickable", 2);
208
+ e = a([
209
+ m("tc-authenticator-card")
210
+ ], e);
211
+ export {
212
+ e as TcAuthenticatorCard
213
+ };
@@ -0,0 +1,132 @@
1
+ import { LitElement as c, html as b, css as d } from "lit";
2
+ import { property as a, customElement as h } from "lit/decorators.js";
3
+ import { styleMap as u } from "lit/directives/style-map.js";
4
+ import { sharedStyles as m } from "../styles/shared.js";
5
+ var v = Object.defineProperty, f = Object.getOwnPropertyDescriptor, s = (e, i, n, r) => {
6
+ for (var t = r > 1 ? void 0 : r ? f(i, n) : i, p = e.length - 1, l; p >= 0; p--)
7
+ (l = e[p]) && (t = (r ? l(i, n, t) : l(t)) || t);
8
+ return r && t && v(i, n, t), t;
9
+ };
10
+ let o = class extends c {
11
+ constructor() {
12
+ super(...arguments), this.position = "bottom-right", this.size = "3.5rem", this.disabled = !1, this.sx = {};
13
+ }
14
+ connectedCallback() {
15
+ super.connectedCallback(), this.setAttribute("position", this.position);
16
+ }
17
+ updated(e) {
18
+ e.has("position") && this.setAttribute("position", this.position);
19
+ }
20
+ handleClick() {
21
+ this.disabled || this.dispatchEvent(
22
+ new CustomEvent("tc-click", {
23
+ bubbles: !0,
24
+ composed: !0
25
+ })
26
+ );
27
+ }
28
+ render() {
29
+ const e = {
30
+ "--fab-size": this.size,
31
+ ...this.sx
32
+ };
33
+ return b`
34
+ <button
35
+ part="button"
36
+ class="button"
37
+ ?disabled=${this.disabled}
38
+ style=${u(e)}
39
+ @click=${this.handleClick}
40
+ aria-label="Floating action"
41
+ >
42
+ <slot></slot>
43
+ </button>
44
+ `;
45
+ }
46
+ };
47
+ o.styles = [
48
+ m,
49
+ d`
50
+ :host {
51
+ display: block;
52
+ position: fixed;
53
+ z-index: 100;
54
+ }
55
+
56
+ :host([position='bottom-right']) {
57
+ bottom: var(--space-lg);
58
+ right: var(--space-lg);
59
+ }
60
+
61
+ :host([position='bottom-left']) {
62
+ bottom: var(--space-lg);
63
+ left: var(--space-lg);
64
+ }
65
+
66
+ :host([position='top-right']) {
67
+ top: var(--space-lg);
68
+ right: var(--space-lg);
69
+ }
70
+
71
+ :host([position='top-left']) {
72
+ top: var(--space-lg);
73
+ left: var(--space-lg);
74
+ }
75
+
76
+ .button {
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ width: var(--fab-size);
81
+ height: var(--fab-size);
82
+ padding: 0;
83
+ border: none;
84
+ border-radius: var(--radius-full);
85
+ background: var(--accent-primary);
86
+ color: white;
87
+ cursor: pointer;
88
+ box-shadow: 0 4px 12px var(--alpha-primary40);
89
+ transition: var(--transition-smooth);
90
+ }
91
+
92
+ .button:hover:not(:disabled) {
93
+ background: var(--accent-primary-hover);
94
+ transform: scale(1.05);
95
+ box-shadow: 0 6px 16px var(--alpha-primary50);
96
+ }
97
+
98
+ .button:active:not(:disabled) {
99
+ background: var(--accent-primary-pressed);
100
+ transform: scale(0.98);
101
+ }
102
+
103
+ .button:focus-visible {
104
+ outline: 2px solid var(--accent-primary);
105
+ outline-offset: 3px;
106
+ }
107
+
108
+ .button:disabled {
109
+ opacity: var(--opacity-disabled);
110
+ cursor: not-allowed;
111
+ box-shadow: none;
112
+ }
113
+ `
114
+ ];
115
+ s([
116
+ a({ type: String })
117
+ ], o.prototype, "position", 2);
118
+ s([
119
+ a({ type: String })
120
+ ], o.prototype, "size", 2);
121
+ s([
122
+ a({ type: Boolean })
123
+ ], o.prototype, "disabled", 2);
124
+ s([
125
+ a({ type: Object })
126
+ ], o.prototype, "sx", 2);
127
+ o = s([
128
+ h("tc-floating-button")
129
+ ], o);
130
+ export {
131
+ o as TcFloatingButton
132
+ };
@@ -0,0 +1,263 @@
1
+ import { LitElement as c, html as h, css as p } from "lit";
2
+ import { property as r, state as m, customElement as v } from "lit/decorators.js";
3
+ import { AnimationController as y } from "../controllers/animation.controller.js";
4
+ import { HistoryController as u } from "../controllers/history.controller.js";
5
+ import { MessageBusController as f } from "../controllers/message-bus.controller.js";
6
+ import { sharedStyles as g } from "../styles/shared.js";
7
+ var b = Object.defineProperty, w = Object.getOwnPropertyDescriptor, s = (t, i, o, n) => {
8
+ for (var a = n > 1 ? void 0 : n ? w(i, o) : i, l = t.length - 1, d; l >= 0; l--)
9
+ (d = t[l]) && (a = (n ? d(i, o, a) : d(a)) || a);
10
+ return n && a && b(i, o, a), a;
11
+ };
12
+ let e = class extends c {
13
+ constructor() {
14
+ super(...arguments), this.src = "", this.title = "", this.closeOnEscape = !0, this.closeOnOverlay = !0, this.useHistory = !0, this.isLoading = !0, this.animation = new y(this, {
15
+ showDuration: 300,
16
+ hideDuration: 200
17
+ }), this.history = new u(this), this.messageBus = new f(this), this.handleKeyDown = (t) => {
18
+ t.key === "Escape" && this.animation.state === "visible" && this.close();
19
+ };
20
+ }
21
+ connectedCallback() {
22
+ super.connectedCallback(), this.updateDataState(), this.closeOnEscape && document.addEventListener("keydown", this.handleKeyDown), this.messageBus.onMessage("*", (t, i) => {
23
+ this.dispatchEvent(
24
+ new CustomEvent("tc-message", {
25
+ bubbles: !0,
26
+ composed: !0,
27
+ detail: { type: t, payload: i }
28
+ })
29
+ );
30
+ }), this.useHistory && this.history.onPopState(() => {
31
+ this.animation.state === "visible" && this.close();
32
+ });
33
+ }
34
+ disconnectedCallback() {
35
+ super.disconnectedCallback(), document.removeEventListener("keydown", this.handleKeyDown);
36
+ }
37
+ updateDataState() {
38
+ this.dataset.state = this.animation.state;
39
+ }
40
+ async open() {
41
+ this.useHistory && this.history.push({ modal: "iframe" }), this.isLoading = !0, await this.animation.show(), this.updateDataState();
42
+ }
43
+ async close() {
44
+ this.useHistory && this.history.pop(), await this.animation.hide(), this.updateDataState(), this.dispatchEvent(
45
+ new CustomEvent("tc-close", {
46
+ bubbles: !0,
47
+ composed: !0
48
+ })
49
+ );
50
+ }
51
+ handleOverlayClick(t) {
52
+ this.closeOnOverlay && t.target === t.currentTarget && this.close();
53
+ }
54
+ handleIframeLoad() {
55
+ this.isLoading = !1, this.dispatchEvent(
56
+ new CustomEvent("tc-load", {
57
+ bubbles: !0,
58
+ composed: !0
59
+ })
60
+ );
61
+ }
62
+ sendMessage(t, i) {
63
+ const o = this.shadowRoot?.querySelector("iframe");
64
+ o?.contentWindow && this.messageBus.send(t, i, o.contentWindow);
65
+ }
66
+ render() {
67
+ return h`
68
+ <div part="overlay" class="overlay" @click=${this.handleOverlayClick}>
69
+ <div part="modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
70
+ ${this.title ? h`
71
+ <div part="header" class="header">
72
+ <h2 id="modal-title" class="title">${this.title}</h2>
73
+ <button
74
+ part="close"
75
+ class="close"
76
+ @click=${this.close}
77
+ aria-label="Close modal"
78
+ >
79
+ <svg
80
+ viewBox="0 0 24 24"
81
+ fill="none"
82
+ stroke="currentColor"
83
+ stroke-width="2"
84
+ stroke-linecap="round"
85
+ stroke-linejoin="round"
86
+ >
87
+ <line x1="18" y1="6" x2="6" y2="18"></line>
88
+ <line x1="6" y1="6" x2="18" y2="18"></line>
89
+ </svg>
90
+ </button>
91
+ </div>
92
+ ` : ""}
93
+ <div part="content" class="content">
94
+ <iframe
95
+ part="iframe"
96
+ class="iframe"
97
+ src=${this.src}
98
+ @load=${this.handleIframeLoad}
99
+ title=${this.title || "Modal content"}
100
+ ></iframe>
101
+ <div part="loader" class="loader ${this.isLoading ? "" : "hidden"}">
102
+ <tc-spinner size="2rem"></tc-spinner>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ `;
108
+ }
109
+ };
110
+ e.styles = [
111
+ g,
112
+ p`
113
+ :host {
114
+ display: contents;
115
+ }
116
+
117
+ .overlay {
118
+ position: fixed;
119
+ inset: 0;
120
+ z-index: 1000;
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ padding: var(--space-lg);
125
+ background: var(--overlay-dim);
126
+ opacity: 0;
127
+ visibility: hidden;
128
+ transition: opacity var(--transition-smooth), visibility var(--transition-smooth);
129
+ }
130
+
131
+ :host([data-state='showing']) .overlay,
132
+ :host([data-state='visible']) .overlay {
133
+ opacity: 1;
134
+ visibility: visible;
135
+ }
136
+
137
+ :host([data-state='hiding']) .overlay {
138
+ opacity: 0;
139
+ }
140
+
141
+ :host([data-state='hidden']) .overlay {
142
+ visibility: hidden;
143
+ }
144
+
145
+ .modal {
146
+ display: flex;
147
+ flex-direction: column;
148
+ width: 100%;
149
+ max-width: 600px;
150
+ max-height: 90vh;
151
+ max-height: 90dvh;
152
+ background: var(--paper-white);
153
+ border-radius: var(--radius-lg);
154
+ box-shadow: 0 20px 60px var(--overlay-shadow-strong);
155
+ overflow: hidden;
156
+ transform: scale(0.95) translateY(20px);
157
+ transition: transform var(--transition-smooth);
158
+ }
159
+
160
+ :host([data-state='showing']) .modal,
161
+ :host([data-state='visible']) .modal {
162
+ transform: scale(1) translateY(0);
163
+ }
164
+
165
+ :host([data-state='hiding']) .modal {
166
+ transform: scale(0.95) translateY(20px);
167
+ }
168
+
169
+ .header {
170
+ display: flex;
171
+ align-items: center;
172
+ justify-content: space-between;
173
+ padding: var(--space-md) var(--space-lg);
174
+ border-bottom: 1px solid var(--paper-warm);
175
+ }
176
+
177
+ .title {
178
+ font-family: var(--font-body);
179
+ font-size: var(--font-size-lg);
180
+ font-weight: 600;
181
+ color: var(--ink-dark);
182
+ margin: 0;
183
+ }
184
+
185
+ .close {
186
+ display: flex;
187
+ align-items: center;
188
+ justify-content: center;
189
+ width: var(--size-close-button);
190
+ height: var(--size-close-button);
191
+ padding: 0;
192
+ border: none;
193
+ background: transparent;
194
+ color: var(--ink-medium);
195
+ cursor: pointer;
196
+ border-radius: var(--radius-sm);
197
+ transition: var(--transition-fast);
198
+ }
199
+
200
+ .close:hover {
201
+ background: var(--paper-warm);
202
+ color: var(--ink-dark);
203
+ }
204
+
205
+ .close svg {
206
+ width: 1.25rem;
207
+ height: 1.25rem;
208
+ }
209
+
210
+ .content {
211
+ position: relative;
212
+ flex: 1;
213
+ overflow: hidden;
214
+ }
215
+
216
+ .iframe {
217
+ width: 100%;
218
+ height: 100%;
219
+ min-height: 400px;
220
+ border: none;
221
+ }
222
+
223
+ .loader {
224
+ position: absolute;
225
+ inset: 0;
226
+ display: flex;
227
+ align-items: center;
228
+ justify-content: center;
229
+ background: var(--paper-white);
230
+ opacity: 1;
231
+ transition: opacity var(--transition-smooth);
232
+ }
233
+
234
+ .loader.hidden {
235
+ opacity: 0;
236
+ pointer-events: none;
237
+ }
238
+ `
239
+ ];
240
+ s([
241
+ r({ type: String })
242
+ ], e.prototype, "src", 2);
243
+ s([
244
+ r({ type: String })
245
+ ], e.prototype, "title", 2);
246
+ s([
247
+ r({ type: Boolean, attribute: "close-on-escape" })
248
+ ], e.prototype, "closeOnEscape", 2);
249
+ s([
250
+ r({ type: Boolean, attribute: "close-on-overlay" })
251
+ ], e.prototype, "closeOnOverlay", 2);
252
+ s([
253
+ r({ type: Boolean, attribute: "use-history" })
254
+ ], e.prototype, "useHistory", 2);
255
+ s([
256
+ m()
257
+ ], e.prototype, "isLoading", 2);
258
+ e = s([
259
+ v("tc-iframe-modal")
260
+ ], e);
261
+ export {
262
+ e as TcIframeModal
263
+ };