tinkiet 0.11.0 → 0.11.4

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/radio/index.js CHANGED
@@ -109,6 +109,61 @@ let TkBox = class TkBox extends external_lit_.LitElement {
109
109
  * Activate ripple
110
110
  */
111
111
  this.ripple = false;
112
+ this.showRipple = (event) => {
113
+ const x = event.clientX;
114
+ const y = event.clientY;
115
+ const { offsetWidth, offsetHeight } = this;
116
+ const container = document.createElement("span");
117
+ container.classList.add("ripple", "open");
118
+ const element = document.createElement("span");
119
+ container.appendChild(element);
120
+ this.shadowRoot.appendChild(container);
121
+ const rect = this.getBoundingClientRect();
122
+ const diameter = Math.max(offsetWidth, offsetWidth) * 2;
123
+ element.style.width = element.style.height = diameter + "px";
124
+ element.style.left = x - rect.left - diameter / 2 + "px";
125
+ element.style.top = y - rect.top - diameter / 2 + "px";
126
+ const inAnimation = element.animate({
127
+ transform: [`scale(0)`, `scale(1)`]
128
+ }, {
129
+ easing: "ease-out",
130
+ fill: "both",
131
+ duration: 500
132
+ });
133
+ inAnimation.onfinish = () => {
134
+ container.classList.remove("open");
135
+ const outAnimation = element.animate({
136
+ opacity: ["0.5", "0"]
137
+ }, {
138
+ easing: "ease-in",
139
+ fill: "both",
140
+ duration: 300
141
+ });
142
+ outAnimation.onfinish = () => {
143
+ requestAnimationFrame(() => {
144
+ container.remove();
145
+ });
146
+ };
147
+ };
148
+ };
149
+ this.hideRipple = (event) => {
150
+ var _a;
151
+ (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(".ripple:not([open])").forEach(container => {
152
+ const element = container.querySelector("span");
153
+ const outAnimation = element.animate({
154
+ opacity: ["0.5", "0"]
155
+ }, {
156
+ easing: "ease-out",
157
+ fill: "both",
158
+ duration: 300
159
+ });
160
+ outAnimation.onfinish = () => {
161
+ requestAnimationFrame(() => {
162
+ container.remove();
163
+ });
164
+ };
165
+ });
166
+ };
112
167
  }
113
168
  static get styles() {
114
169
  return [
@@ -124,14 +179,14 @@ let TkBox = class TkBox extends external_lit_.LitElement {
124
179
  }
125
180
  connectedCallback() {
126
181
  if (this.ripple) {
127
- this.addEventListener("mousedown", this.showRipple.bind(this), { passive: true });
128
- this.addEventListener("mouseup", this.hideRipple.bind(this), { passive: true });
182
+ this.addEventListener("mousedown", this.showRipple, { passive: true });
183
+ this.addEventListener("mouseup", this.hideRipple, { passive: true });
129
184
  }
130
185
  super.connectedCallback();
131
186
  }
132
187
  disconnectedCallback() {
133
188
  this.removeEventListener("mousedown", this.showRipple);
134
- this.addEventListener("mouseup", this.hideRipple);
189
+ this.removeEventListener("mouseup", this.hideRipple);
135
190
  super.disconnectedCallback();
136
191
  }
137
192
  updated(props) {
@@ -141,61 +196,6 @@ let TkBox = class TkBox extends external_lit_.LitElement {
141
196
  // this.style.setProperty("color", this.color.toString());
142
197
  super.updated(props);
143
198
  }
144
- showRipple(event) {
145
- const x = event.clientX;
146
- const y = event.clientY;
147
- const { offsetWidth, offsetHeight } = this;
148
- const container = document.createElement("span");
149
- container.classList.add("ripple", "open");
150
- const element = document.createElement("span");
151
- container.appendChild(element);
152
- this.shadowRoot.appendChild(container);
153
- const rect = this.getBoundingClientRect();
154
- const diameter = Math.max(offsetWidth, offsetWidth) * 2;
155
- element.style.width = element.style.height = diameter + "px";
156
- element.style.left = x - rect.left - diameter / 2 + "px";
157
- element.style.top = y - rect.top - diameter / 2 + "px";
158
- const inAnimation = element.animate({
159
- transform: [`scale(0)`, `scale(1)`]
160
- }, {
161
- easing: "ease-out",
162
- fill: "both",
163
- duration: 500
164
- });
165
- inAnimation.onfinish = () => {
166
- container.classList.remove("open");
167
- const outAnimation = element.animate({
168
- opacity: ["0.5", "0"]
169
- }, {
170
- easing: "ease-in",
171
- fill: "both",
172
- duration: 300
173
- });
174
- outAnimation.onfinish = () => {
175
- requestAnimationFrame(() => {
176
- container.remove();
177
- });
178
- };
179
- };
180
- }
181
- hideRipple(event) {
182
- var _a;
183
- (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(".ripple:not([open])").forEach(container => {
184
- const element = container.querySelector("span");
185
- const outAnimation = element.animate({
186
- opacity: ["0.5", "0"]
187
- }, {
188
- easing: "ease-out",
189
- fill: "both",
190
- duration: 300
191
- });
192
- outAnimation.onfinish = () => {
193
- requestAnimationFrame(() => {
194
- container.remove();
195
- });
196
- };
197
- });
198
- }
199
199
  };
200
200
  __decorate([
201
201
  (0,decorators_js_.property)({ type: Boolean })
@@ -276,6 +276,25 @@ let TkRadio = class TkRadio extends external_lit_.LitElement {
276
276
  this.required = false;
277
277
  this.disabled = false;
278
278
  this.readonly = false;
279
+ this.onKeyDown = (e) => {
280
+ if (this.disabled)
281
+ return;
282
+ if (e.code === "Space" || e.code === "Enter") {
283
+ e.preventDefault();
284
+ e.stopPropagation();
285
+ this.click();
286
+ }
287
+ };
288
+ this.handleClick = () => {
289
+ if (this.disabled)
290
+ return;
291
+ this.checked = !this.checked;
292
+ if (this.checked && this.name)
293
+ this.getRootNode()
294
+ .querySelectorAll(`tk-radio[name="${this.name}"]`)
295
+ .forEach(el => (el != this ? (el.checked = false) : null));
296
+ setTimeout(() => this.dispatchEvent(new Event("change", { bubbles: true, composed: true })));
297
+ };
279
298
  }
280
299
  render() {
281
300
  return (0,external_lit_.html) `
@@ -305,8 +324,8 @@ let TkRadio = class TkRadio extends external_lit_.LitElement {
305
324
  }
306
325
  connectedCallback() {
307
326
  super.connectedCallback();
308
- this.addEventListener("click", this.handleClick.bind(this));
309
- this.addEventListener("keydown", this.onKeyDown.bind(this));
327
+ this.addEventListener("click", this.handleClick);
328
+ this.addEventListener("keydown", this.onKeyDown);
310
329
  }
311
330
  disconnectedCallback() {
312
331
  super.disconnectedCallback();
@@ -316,25 +335,6 @@ let TkRadio = class TkRadio extends external_lit_.LitElement {
316
335
  firstUpdated() {
317
336
  this.appendChild(this.$input);
318
337
  }
319
- onKeyDown(e) {
320
- if (this.disabled)
321
- return;
322
- if (e.code === "Space" || e.code === "Enter") {
323
- e.preventDefault();
324
- e.stopPropagation();
325
- this.click();
326
- }
327
- }
328
- handleClick() {
329
- if (this.disabled)
330
- return;
331
- this.checked = !this.checked;
332
- if (this.checked && this.name)
333
- this.getRootNode()
334
- .querySelectorAll(`tk-radio[name="${this.name}"]`)
335
- .forEach(el => (el != this ? (el.checked = false) : null));
336
- setTimeout(() => this.dispatchEvent(new Event("change", { bubbles: true, composed: true })));
337
- }
338
338
  };
339
339
  TkRadio.styles = (0,external_lit_.css) `
340
340
  ${(0,external_lit_.unsafeCSS)(radio_radio)}
package/radio/radio.d.ts CHANGED
@@ -14,8 +14,8 @@ export declare class TkRadio extends LitElement {
14
14
  connectedCallback(): void;
15
15
  disconnectedCallback(): void;
16
16
  firstUpdated(): void;
17
- protected onKeyDown(e: KeyboardEvent): void;
18
- protected handleClick(): void;
17
+ protected onKeyDown: (e: KeyboardEvent) => void;
18
+ protected handleClick: () => void;
19
19
  }
20
20
  declare global {
21
21
  interface HTMLElementTagNameMap {
package/router/index.d.ts CHANGED
@@ -21,6 +21,8 @@ export interface NavigateOptions {
21
21
  export declare class Router {
22
22
  private layers;
23
23
  private started;
24
+ private handlePopState;
25
+ private handleDocumentClick;
24
26
  /**
25
27
  * Global middleware:
26
28
  * router.use((ctx, next) => { ... })
@@ -39,6 +41,10 @@ export declare class Router {
39
41
  * Start listening to history and link clicks, and dispatch initial URL.
40
42
  */
41
43
  start(): void;
44
+ /**
45
+ * Stop listening to history and link clicks.
46
+ */
47
+ stop(): void;
42
48
  /**
43
49
  * Shortcut to go back in history.
44
50
  */
package/router/index.js CHANGED
@@ -30,22 +30,35 @@ function matchPath(pattern, pathname) {
30
30
  const patternParts = pattern.split('/').filter(Boolean);
31
31
  const pathParts = pathname.split('/').filter(Boolean);
32
32
  const params = {};
33
+ let pathIndex = 0;
33
34
  for (let i = 0; i < patternParts.length; i++) {
34
35
  const pat = patternParts[i];
35
- const isLastPatternSegment = i === patternParts.length - 1;
36
- const segment = pathParts[i];
36
+ const segment = pathParts[pathIndex];
37
+ const isRestParam = pat.startsWith(':') && pat.endsWith('*');
38
+ const isOptionalParam = pat.startsWith(':') && pat.endsWith('?');
39
+ const isParam = pat.startsWith(':') && !isRestParam && !isOptionalParam;
37
40
  // Wildcard "rest" param: :name*
38
- if (pat.startsWith(':') && pat.endsWith('*')) {
41
+ if (isRestParam) {
39
42
  const name = pat.slice(1, -1); // remove ':' and '*'
40
- params[name] = decodeURIComponent(pathParts.slice(i).join('/'));
43
+ params[name] = decodeURIComponent(pathParts.slice(pathIndex).join('/'));
41
44
  return { params };
42
45
  }
46
+ // Optional param :name?
47
+ if (isOptionalParam) {
48
+ if (segment != null) {
49
+ const name = pat.slice(1, -1); // remove ':' and '?'
50
+ params[name] = decodeURIComponent(segment);
51
+ pathIndex += 1;
52
+ }
53
+ continue;
54
+ }
43
55
  // Simple param :name
44
- if (pat.startsWith(':')) {
56
+ if (isParam) {
45
57
  if (segment == null)
46
58
  return null;
47
59
  const name = pat.slice(1);
48
60
  params[name] = decodeURIComponent(segment);
61
+ pathIndex += 1;
49
62
  continue;
50
63
  }
51
64
  // Plain wildcard "*"
@@ -56,13 +69,10 @@ function matchPath(pattern, pathname) {
56
69
  if (segment !== pat) {
57
70
  return null;
58
71
  }
59
- // If we are at end of pattern but still have more path segments
60
- if (isLastPatternSegment && pathParts.length > patternParts.length) {
61
- return null;
62
- }
72
+ pathIndex += 1;
63
73
  }
64
- // Extra segments in path not matched by a rest param => no match
65
- if (pathParts.length > patternParts.length) {
74
+ // Extra segments in path not matched by optional/rest params => no match
75
+ if (pathIndex < pathParts.length) {
66
76
  return null;
67
77
  }
68
78
  return { params };
@@ -84,6 +94,46 @@ class Router {
84
94
  constructor() {
85
95
  this.layers = [];
86
96
  this.started = false;
97
+ this.handlePopState = () => {
98
+ const url = new URL(window.location.href);
99
+ void this.dispatch(url);
100
+ };
101
+ this.handleDocumentClick = (event) => {
102
+ var _a, _b;
103
+ const path = event.composedPath();
104
+ // Find first element in the composed path that exposes an href
105
+ let linkEl = null;
106
+ let href = null;
107
+ for (const el of path) {
108
+ if (!(el instanceof HTMLElement))
109
+ continue;
110
+ const elHref = (_a = el.href) !== null && _a !== void 0 ? _a : el.getAttribute('href');
111
+ if (elHref) {
112
+ linkEl = el;
113
+ href = elHref;
114
+ break;
115
+ }
116
+ }
117
+ if (!linkEl || !href)
118
+ return;
119
+ const url = new URL(href, window.location.origin);
120
+ const sameOrigin = url.origin === window.location.origin;
121
+ const targetAttr = (_b = linkEl.target) !== null && _b !== void 0 ? _b : linkEl.getAttribute('target');
122
+ const isModifiedClick = event.metaKey ||
123
+ event.ctrlKey ||
124
+ event.shiftKey ||
125
+ event.altKey ||
126
+ event.button !== 0;
127
+ if (!sameOrigin || isModifiedClick || targetAttr === '_blank')
128
+ return;
129
+ if (linkEl.hasAttribute('download'))
130
+ return;
131
+ // Let browser handle hash-only navigation on same path
132
+ if (url.pathname === window.location.pathname && url.hash !== '')
133
+ return;
134
+ event.preventDefault();
135
+ this.navigate(url.pathname + url.search + url.hash);
136
+ };
87
137
  }
88
138
  use(patternOrHandler, handler) {
89
139
  if (typeof patternOrHandler === 'string') {
@@ -133,50 +183,22 @@ class Router {
133
183
  return;
134
184
  this.started = true;
135
185
  // Back/forward
136
- window.addEventListener('popstate', () => {
137
- const url = new URL(window.location.href);
138
- void this.dispatch(url);
139
- });
186
+ window.addEventListener('popstate', this.handlePopState);
140
187
  // Intercept <a> clicks for SPA navigation
141
- document.addEventListener('click', (event) => {
142
- var _a, _b;
143
- const path = event.composedPath();
144
- // Find first element in the composed path that exposes an href
145
- let linkEl = null;
146
- let href = null;
147
- for (const el of path) {
148
- if (!(el instanceof HTMLElement))
149
- continue;
150
- const elHref = (_a = el.href) !== null && _a !== void 0 ? _a : el.getAttribute('href');
151
- if (elHref) {
152
- linkEl = el;
153
- href = elHref;
154
- break;
155
- }
156
- }
157
- if (!linkEl || !href)
158
- return;
159
- const url = new URL(href, window.location.origin);
160
- const sameOrigin = url.origin === window.location.origin;
161
- const targetAttr = (_b = linkEl.target) !== null && _b !== void 0 ? _b : linkEl.getAttribute('target');
162
- const isModifiedClick = event.metaKey ||
163
- event.ctrlKey ||
164
- event.shiftKey ||
165
- event.altKey ||
166
- event.button !== 0;
167
- if (!sameOrigin || isModifiedClick || targetAttr === '_blank')
168
- return;
169
- if (linkEl.hasAttribute('download'))
170
- return;
171
- // Let browser handle hash-only navigation on same path
172
- if (url.pathname === window.location.pathname && url.hash !== '')
173
- return;
174
- event.preventDefault();
175
- this.navigate(url.pathname + url.search + url.hash);
176
- });
188
+ document.addEventListener('click', this.handleDocumentClick);
177
189
  const initialUrl = new URL(window.location.href);
178
190
  void this.dispatch(initialUrl);
179
191
  }
192
+ /**
193
+ * Stop listening to history and link clicks.
194
+ */
195
+ stop() {
196
+ if (!this.started)
197
+ return;
198
+ window.removeEventListener('popstate', this.handlePopState);
199
+ document.removeEventListener('click', this.handleDocumentClick);
200
+ this.started = false;
201
+ }
180
202
  /**
181
203
  * Shortcut to go back in history.
182
204
  */
package/select/index.js CHANGED
@@ -109,6 +109,61 @@ let TkBox = class TkBox extends external_lit_.LitElement {
109
109
  * Activate ripple
110
110
  */
111
111
  this.ripple = false;
112
+ this.showRipple = (event) => {
113
+ const x = event.clientX;
114
+ const y = event.clientY;
115
+ const { offsetWidth, offsetHeight } = this;
116
+ const container = document.createElement("span");
117
+ container.classList.add("ripple", "open");
118
+ const element = document.createElement("span");
119
+ container.appendChild(element);
120
+ this.shadowRoot.appendChild(container);
121
+ const rect = this.getBoundingClientRect();
122
+ const diameter = Math.max(offsetWidth, offsetWidth) * 2;
123
+ element.style.width = element.style.height = diameter + "px";
124
+ element.style.left = x - rect.left - diameter / 2 + "px";
125
+ element.style.top = y - rect.top - diameter / 2 + "px";
126
+ const inAnimation = element.animate({
127
+ transform: [`scale(0)`, `scale(1)`]
128
+ }, {
129
+ easing: "ease-out",
130
+ fill: "both",
131
+ duration: 500
132
+ });
133
+ inAnimation.onfinish = () => {
134
+ container.classList.remove("open");
135
+ const outAnimation = element.animate({
136
+ opacity: ["0.5", "0"]
137
+ }, {
138
+ easing: "ease-in",
139
+ fill: "both",
140
+ duration: 300
141
+ });
142
+ outAnimation.onfinish = () => {
143
+ requestAnimationFrame(() => {
144
+ container.remove();
145
+ });
146
+ };
147
+ };
148
+ };
149
+ this.hideRipple = (event) => {
150
+ var _a;
151
+ (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(".ripple:not([open])").forEach(container => {
152
+ const element = container.querySelector("span");
153
+ const outAnimation = element.animate({
154
+ opacity: ["0.5", "0"]
155
+ }, {
156
+ easing: "ease-out",
157
+ fill: "both",
158
+ duration: 300
159
+ });
160
+ outAnimation.onfinish = () => {
161
+ requestAnimationFrame(() => {
162
+ container.remove();
163
+ });
164
+ };
165
+ });
166
+ };
112
167
  }
113
168
  static get styles() {
114
169
  return [
@@ -124,14 +179,14 @@ let TkBox = class TkBox extends external_lit_.LitElement {
124
179
  }
125
180
  connectedCallback() {
126
181
  if (this.ripple) {
127
- this.addEventListener("mousedown", this.showRipple.bind(this), { passive: true });
128
- this.addEventListener("mouseup", this.hideRipple.bind(this), { passive: true });
182
+ this.addEventListener("mousedown", this.showRipple, { passive: true });
183
+ this.addEventListener("mouseup", this.hideRipple, { passive: true });
129
184
  }
130
185
  super.connectedCallback();
131
186
  }
132
187
  disconnectedCallback() {
133
188
  this.removeEventListener("mousedown", this.showRipple);
134
- this.addEventListener("mouseup", this.hideRipple);
189
+ this.removeEventListener("mouseup", this.hideRipple);
135
190
  super.disconnectedCallback();
136
191
  }
137
192
  updated(props) {
@@ -141,61 +196,6 @@ let TkBox = class TkBox extends external_lit_.LitElement {
141
196
  // this.style.setProperty("color", this.color.toString());
142
197
  super.updated(props);
143
198
  }
144
- showRipple(event) {
145
- const x = event.clientX;
146
- const y = event.clientY;
147
- const { offsetWidth, offsetHeight } = this;
148
- const container = document.createElement("span");
149
- container.classList.add("ripple", "open");
150
- const element = document.createElement("span");
151
- container.appendChild(element);
152
- this.shadowRoot.appendChild(container);
153
- const rect = this.getBoundingClientRect();
154
- const diameter = Math.max(offsetWidth, offsetWidth) * 2;
155
- element.style.width = element.style.height = diameter + "px";
156
- element.style.left = x - rect.left - diameter / 2 + "px";
157
- element.style.top = y - rect.top - diameter / 2 + "px";
158
- const inAnimation = element.animate({
159
- transform: [`scale(0)`, `scale(1)`]
160
- }, {
161
- easing: "ease-out",
162
- fill: "both",
163
- duration: 500
164
- });
165
- inAnimation.onfinish = () => {
166
- container.classList.remove("open");
167
- const outAnimation = element.animate({
168
- opacity: ["0.5", "0"]
169
- }, {
170
- easing: "ease-in",
171
- fill: "both",
172
- duration: 300
173
- });
174
- outAnimation.onfinish = () => {
175
- requestAnimationFrame(() => {
176
- container.remove();
177
- });
178
- };
179
- };
180
- }
181
- hideRipple(event) {
182
- var _a;
183
- (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll(".ripple:not([open])").forEach(container => {
184
- const element = container.querySelector("span");
185
- const outAnimation = element.animate({
186
- opacity: ["0.5", "0"]
187
- }, {
188
- easing: "ease-out",
189
- fill: "both",
190
- duration: 300
191
- });
192
- outAnimation.onfinish = () => {
193
- requestAnimationFrame(() => {
194
- container.remove();
195
- });
196
- };
197
- });
198
- }
199
199
  };
200
200
  __decorate([
201
201
  (0,decorators_js_.property)({ type: Boolean })