tyrell-components 1.0.0-TC47 → 1.0.0-TC49
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/css/tyrell-theme.css +88 -92
- package/css/tyrell.css +19 -0
- package/dist/tyrell-theme.css +88 -92
- package/dist/tyrell.css +19 -0
- package/dist/tyrell.js +1 -1
- package/lib/base/ty-component.d.ts +8 -3
- package/lib/base/ty-component.js +13 -5
- package/lib/components/date-picker.js +2 -8
- package/lib/components/file-upload.d.ts +6 -0
- package/lib/components/file-upload.js +19 -4
- package/lib/components/input.js +1 -0
- package/lib/components/modal.d.ts +12 -0
- package/lib/components/modal.js +56 -13
- package/lib/components/popup.js +4 -4
- package/lib/components/radio.js +10 -2
- package/lib/components/resize-observer.d.ts +1 -0
- package/lib/components/resize-observer.js +12 -1
- package/lib/components/select.js +3 -5
- package/lib/components/selected-tags.js +1 -1
- package/lib/components/tabs.d.ts +2 -0
- package/lib/components/tabs.js +15 -0
- package/lib/components/tooltip.js +22 -0
- package/lib/components/wizard.d.ts +2 -0
- package/lib/components/wizard.js +15 -0
- package/lib/styles/button.js +4 -1
- package/lib/styles/date-picker.js +27 -1
- package/lib/styles/modal.d.ts +1 -1
- package/lib/styles/modal.js +18 -0
- package/lib/styles/option.d.ts +1 -1
- package/lib/styles/option.js +15 -0
- package/lib/styles/popup.d.ts +1 -1
- package/lib/styles/popup.js +22 -5
- package/lib/styles/select-base.js +53 -5
- package/lib/styles/select.d.ts +1 -1
- package/lib/styles/select.js +5 -1
- package/lib/utils/number-format.d.ts +6 -1
- package/lib/utils/number-format.js +9 -2
- package/lib/utils/positioning.js +4 -4
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
|
@@ -132,9 +132,14 @@ export declare abstract class TyComponent<T = any> extends HTMLElement {
|
|
|
132
132
|
* two genuinely separate clicks are always in different tasks.
|
|
133
133
|
*/
|
|
134
134
|
protected isDuplicateActivationClick(): boolean;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
*
|
|
135
|
+
private _resetDefaults;
|
|
136
|
+
/**
|
|
137
|
+
* Form reset callback - called when form.reset() is invoked.
|
|
138
|
+
* Restores every formValue-bearing property to its attribute-declared
|
|
139
|
+
* default captured at first connect (native defaultValue/defaultChecked
|
|
140
|
+
* semantics). Non-form state (e.g. a checkbox's submission `value`, a
|
|
141
|
+
* ty-radio's option `value`) is deliberately left alone — the old
|
|
142
|
+
* blanket 'value' reset corrupted radio groups by wiping child values.
|
|
138
143
|
*/
|
|
139
144
|
formResetCallback(): void;
|
|
140
145
|
/**
|
package/lib/base/ty-component.js
CHANGED
|
@@ -5,6 +5,7 @@ export class TyComponent extends HTMLElement {
|
|
|
5
5
|
this._connected = false;
|
|
6
6
|
this._rendered = false;
|
|
7
7
|
this._handlingActivationClick = false;
|
|
8
|
+
this._resetDefaults = null;
|
|
8
9
|
const ctor = this.constructor;
|
|
9
10
|
this.props = new PropertyManager(ctor.properties);
|
|
10
11
|
this._internals = this.attachInternals();
|
|
@@ -79,6 +80,16 @@ export class TyComponent extends HTMLElement {
|
|
|
79
80
|
}
|
|
80
81
|
connectedCallback() {
|
|
81
82
|
this._connected = true;
|
|
83
|
+
if (!this._resetDefaults) {
|
|
84
|
+
this._resetDefaults = new Map();
|
|
85
|
+
const ctor = this.constructor;
|
|
86
|
+
for (const [name, config] of Object.entries(ctor.properties)) {
|
|
87
|
+
if (!config.formValue)
|
|
88
|
+
continue;
|
|
89
|
+
const attr = this.getAttribute(this._toKebabCase(name));
|
|
90
|
+
this._resetDefaults.set(name, attr ?? config.default ?? null);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
82
93
|
const preSetProps = this._capturePreSetProperties();
|
|
83
94
|
if (preSetProps.length > 0) {
|
|
84
95
|
this._handlePropertyChanges(preSetProps);
|
|
@@ -178,11 +189,8 @@ export class TyComponent extends HTMLElement {
|
|
|
178
189
|
return false;
|
|
179
190
|
}
|
|
180
191
|
formResetCallback() {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
if (valueConfig) {
|
|
184
|
-
const defaultValue = valueConfig.default ?? '';
|
|
185
|
-
this.setProperty('value', defaultValue);
|
|
192
|
+
for (const [name, def] of this._resetDefaults ?? []) {
|
|
193
|
+
this.setProperty(name, def);
|
|
186
194
|
}
|
|
187
195
|
this.onFormReset();
|
|
188
196
|
}
|
|
@@ -980,10 +980,7 @@ export class TyDatePicker extends TyComponent {
|
|
|
980
980
|
if (isMobileTouch())
|
|
981
981
|
return;
|
|
982
982
|
this.updateState({ open: true });
|
|
983
|
-
this.dispatchEvent(new CustomEvent('open'
|
|
984
|
-
bubbles: true,
|
|
985
|
-
composed: true
|
|
986
|
-
}));
|
|
983
|
+
this.dispatchEvent(new CustomEvent('open'));
|
|
987
984
|
this.render();
|
|
988
985
|
requestAnimationFrame(() => {
|
|
989
986
|
if (!this.shadowRoot)
|
|
@@ -1022,10 +1019,7 @@ export class TyDatePicker extends TyComponent {
|
|
|
1022
1019
|
if (stubEl)
|
|
1023
1020
|
stubEl.setAttribute('aria-expanded', 'false');
|
|
1024
1021
|
}
|
|
1025
|
-
this.dispatchEvent(new CustomEvent('close'
|
|
1026
|
-
bubbles: true,
|
|
1027
|
-
composed: true
|
|
1028
|
-
}));
|
|
1022
|
+
this.dispatchEvent(new CustomEvent('close'));
|
|
1029
1023
|
this.render();
|
|
1030
1024
|
}
|
|
1031
1025
|
cleanup() {
|
|
@@ -80,6 +80,12 @@ export declare class TyFileUpload extends TyComponent<FileUploadState> implement
|
|
|
80
80
|
protected onPropertiesChanged(_changes: PropertyChange[]): void;
|
|
81
81
|
protected onFormReset(): void;
|
|
82
82
|
protected getFormValue(): FormData | string | null;
|
|
83
|
+
/**
|
|
84
|
+
* Match a file against the `accept` attribute the way native inputs do:
|
|
85
|
+
* comma-separated list of `.ext`, `type/subtype` or `type/*` entries.
|
|
86
|
+
* No/blank accept matches everything.
|
|
87
|
+
*/
|
|
88
|
+
private matchesAccept;
|
|
83
89
|
get files(): File[];
|
|
84
90
|
get multiple(): boolean;
|
|
85
91
|
set multiple(v: boolean);
|
|
@@ -44,15 +44,30 @@ export class TyFileUpload extends TyComponent {
|
|
|
44
44
|
fileInput.value = "";
|
|
45
45
|
}
|
|
46
46
|
getFormValue() {
|
|
47
|
-
if (this._files.length === 0)
|
|
47
|
+
if (this._files.length === 0 || !this.name)
|
|
48
48
|
return null;
|
|
49
49
|
const fd = new FormData();
|
|
50
|
-
const fieldName = this.name || "files";
|
|
51
50
|
for (const file of this._files) {
|
|
52
|
-
fd.append(
|
|
51
|
+
fd.append(this.name, file);
|
|
53
52
|
}
|
|
54
53
|
return fd;
|
|
55
54
|
}
|
|
55
|
+
matchesAccept(file) {
|
|
56
|
+
const accept = this.accept?.trim();
|
|
57
|
+
if (!accept)
|
|
58
|
+
return true;
|
|
59
|
+
return accept.split(",").some((raw) => {
|
|
60
|
+
const entry = raw.trim().toLowerCase();
|
|
61
|
+
if (!entry)
|
|
62
|
+
return false;
|
|
63
|
+
if (entry.startsWith("."))
|
|
64
|
+
return file.name.toLowerCase().endsWith(entry);
|
|
65
|
+
const type = file.type.toLowerCase();
|
|
66
|
+
if (entry.endsWith("/*"))
|
|
67
|
+
return type.startsWith(entry.slice(0, -1));
|
|
68
|
+
return type === entry;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
56
71
|
get files() {
|
|
57
72
|
return [...this._files];
|
|
58
73
|
}
|
|
@@ -178,7 +193,7 @@ export class TyFileUpload extends TyComponent {
|
|
|
178
193
|
e.stopPropagation();
|
|
179
194
|
this._isDragging = false;
|
|
180
195
|
dropZone.classList.remove("drag-active");
|
|
181
|
-
const files = Array.from(e.dataTransfer?.files ?? []);
|
|
196
|
+
const files = Array.from(e.dataTransfer?.files ?? []).filter((f) => this.matchesAccept(f));
|
|
182
197
|
if (files.length > 0)
|
|
183
198
|
this.setFiles(files);
|
|
184
199
|
};
|
package/lib/components/input.js
CHANGED
|
@@ -48,6 +48,18 @@ export declare class TyModal extends HTMLElement {
|
|
|
48
48
|
force?: boolean;
|
|
49
49
|
}) => void;
|
|
50
50
|
static get observedAttributes(): string[];
|
|
51
|
+
get open(): boolean;
|
|
52
|
+
set open(v: boolean);
|
|
53
|
+
get backdrop(): boolean;
|
|
54
|
+
set backdrop(v: boolean);
|
|
55
|
+
get closeOnOutsideClick(): boolean;
|
|
56
|
+
set closeOnOutsideClick(v: boolean);
|
|
57
|
+
get closeOnEscape(): boolean;
|
|
58
|
+
set closeOnEscape(v: boolean);
|
|
59
|
+
get preventOutsideClick(): boolean;
|
|
60
|
+
set preventOutsideClick(v: boolean);
|
|
61
|
+
get preventEscape(): boolean;
|
|
62
|
+
set preventEscape(v: boolean);
|
|
51
63
|
constructor();
|
|
52
64
|
connectedCallback(): void;
|
|
53
65
|
disconnectedCallback(): void;
|
package/lib/components/modal.js
CHANGED
|
@@ -7,6 +7,7 @@ const closeButtonHandlers = new WeakMap();
|
|
|
7
7
|
const hoverEnterHandlers = new WeakMap();
|
|
8
8
|
const hoverLeaveHandlers = new WeakMap();
|
|
9
9
|
const modalIds = new WeakMap();
|
|
10
|
+
const pendingCloseReasons = new WeakMap();
|
|
10
11
|
const CLOSE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>`;
|
|
11
12
|
function parseBoolAttr(el, name) {
|
|
12
13
|
if (!el.hasAttribute(name)) {
|
|
@@ -22,12 +23,16 @@ function getModalAttributes(el) {
|
|
|
22
23
|
return {
|
|
23
24
|
open: parseBoolAttr(el, 'open'),
|
|
24
25
|
backdrop: el.hasAttribute('backdrop') ? parseBoolAttr(el, 'backdrop') : true,
|
|
25
|
-
closeOnOutsideClick: el
|
|
26
|
-
?
|
|
27
|
-
:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
closeOnOutsideClick: parseBoolAttr(el, 'prevent-outside-click')
|
|
27
|
+
? false
|
|
28
|
+
: el.hasAttribute('close-on-outside-click')
|
|
29
|
+
? parseBoolAttr(el, 'close-on-outside-click')
|
|
30
|
+
: true,
|
|
31
|
+
closeOnEscape: parseBoolAttr(el, 'prevent-escape')
|
|
32
|
+
? false
|
|
33
|
+
: el.hasAttribute('close-on-escape')
|
|
34
|
+
? parseBoolAttr(el, 'close-on-escape')
|
|
35
|
+
: true,
|
|
31
36
|
};
|
|
32
37
|
}
|
|
33
38
|
function getModalDialog(shadowRoot) {
|
|
@@ -47,7 +52,6 @@ function isMobile() {
|
|
|
47
52
|
function dispatchModalEvent(el, eventType, detail) {
|
|
48
53
|
const event = new CustomEvent(eventType, {
|
|
49
54
|
detail,
|
|
50
|
-
bubbles: true,
|
|
51
55
|
cancelable: true,
|
|
52
56
|
});
|
|
53
57
|
el.dispatchEvent(event);
|
|
@@ -80,14 +84,13 @@ function closeModal(el, reason = 'programmatic', opts) {
|
|
|
80
84
|
if (!opts?.force) {
|
|
81
85
|
const beforeClose = new CustomEvent('beforeclose', {
|
|
82
86
|
detail: { reason },
|
|
83
|
-
bubbles: true,
|
|
84
|
-
composed: true,
|
|
85
87
|
cancelable: true,
|
|
86
88
|
});
|
|
87
89
|
el.dispatchEvent(beforeClose);
|
|
88
90
|
if (beforeClose.defaultPrevented)
|
|
89
91
|
return;
|
|
90
92
|
}
|
|
93
|
+
pendingCloseReasons.set(el, reason);
|
|
91
94
|
el.removeAttribute('open');
|
|
92
95
|
}
|
|
93
96
|
function openModal(el) {
|
|
@@ -157,6 +160,14 @@ function setupEscapeKey(el, dialog, enabled) {
|
|
|
157
160
|
escapeKeyHandlers.set(el, handler);
|
|
158
161
|
dialog.addEventListener('keydown', handler);
|
|
159
162
|
}
|
|
163
|
+
else {
|
|
164
|
+
const handler = (e) => {
|
|
165
|
+
if (e.key === 'Escape')
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
};
|
|
168
|
+
escapeKeyHandlers.set(el, handler);
|
|
169
|
+
dialog.addEventListener('keydown', handler);
|
|
170
|
+
}
|
|
160
171
|
}
|
|
161
172
|
function setupCloseButton(el, dialog, showButton) {
|
|
162
173
|
const closeButton = dialog.querySelector('.close-button');
|
|
@@ -226,6 +237,11 @@ function render(el) {
|
|
|
226
237
|
}
|
|
227
238
|
setupBackdropClick(el, dialog, attributes.closeOnOutsideClick);
|
|
228
239
|
setupEscapeKey(el, dialog, attributes.closeOnEscape);
|
|
240
|
+
dialog.oncancel = (event) => {
|
|
241
|
+
event.preventDefault();
|
|
242
|
+
if (getModalAttributes(el).closeOnEscape)
|
|
243
|
+
closeModal(el, 'escape');
|
|
244
|
+
};
|
|
229
245
|
setupCloseButton(el, dialog, attributes.closeOnOutsideClick);
|
|
230
246
|
setupCloseButtonHover(el, dialog);
|
|
231
247
|
if (attributes.open) {
|
|
@@ -237,6 +253,7 @@ function render(el) {
|
|
|
237
253
|
else {
|
|
238
254
|
dialog.show();
|
|
239
255
|
}
|
|
256
|
+
syncBackdropZoom();
|
|
240
257
|
dispatchModalEvent(el, 'open', {});
|
|
241
258
|
}
|
|
242
259
|
}
|
|
@@ -244,9 +261,10 @@ function render(el) {
|
|
|
244
261
|
if (dialog.open) {
|
|
245
262
|
unlockScroll(modalId);
|
|
246
263
|
dialog.close();
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
264
|
+
syncBackdropZoom();
|
|
265
|
+
const reason = pendingCloseReasons.get(el) ?? 'programmatic';
|
|
266
|
+
pendingCloseReasons.delete(el);
|
|
267
|
+
dispatchModalEvent(el, 'close', { reason });
|
|
250
268
|
}
|
|
251
269
|
}
|
|
252
270
|
dialog.onclose = (event) => {
|
|
@@ -260,8 +278,16 @@ function render(el) {
|
|
|
260
278
|
returnValue: dialog.returnValue || undefined
|
|
261
279
|
});
|
|
262
280
|
}
|
|
281
|
+
syncBackdropZoom();
|
|
263
282
|
};
|
|
264
283
|
}
|
|
284
|
+
function syncBackdropZoom() {
|
|
285
|
+
const anyOpen = Array.from(document.querySelectorAll('ty-modal[open][backdrop-zoom], ty-dialog[open][backdrop-zoom]')).some((m) => {
|
|
286
|
+
const v = m.getAttribute('backdrop-zoom').toLowerCase().trim();
|
|
287
|
+
return v !== 'false' && v !== '0';
|
|
288
|
+
});
|
|
289
|
+
document.documentElement.classList.toggle('ty-modal-zoom', anyOpen);
|
|
290
|
+
}
|
|
265
291
|
function cleanup(el) {
|
|
266
292
|
const modalId = modalIds.get(el);
|
|
267
293
|
const shadowRoot = el.shadowRoot;
|
|
@@ -270,6 +296,7 @@ function cleanup(el) {
|
|
|
270
296
|
unlockScroll(modalId);
|
|
271
297
|
modalIds.delete(el);
|
|
272
298
|
}
|
|
299
|
+
syncBackdropZoom();
|
|
273
300
|
if (!dialog)
|
|
274
301
|
return;
|
|
275
302
|
const backdropHandler = backdropClickHandlers.get(el);
|
|
@@ -306,7 +333,23 @@ function cleanup(el) {
|
|
|
306
333
|
}
|
|
307
334
|
export class TyModal extends HTMLElement {
|
|
308
335
|
static get observedAttributes() {
|
|
309
|
-
return ['open', 'backdrop', 'close-on-outside-click', 'close-on-escape', 'label'];
|
|
336
|
+
return ['open', 'backdrop', 'close-on-outside-click', 'close-on-escape', 'prevent-outside-click', 'prevent-escape', 'label'];
|
|
337
|
+
}
|
|
338
|
+
get open() { return parseBoolAttr(this, 'open'); }
|
|
339
|
+
set open(v) { v ? this.setAttribute('open', 'true') : this.removeAttribute('open'); }
|
|
340
|
+
get backdrop() { return getModalAttributes(this).backdrop; }
|
|
341
|
+
set backdrop(v) { this.setAttribute('backdrop', String(!!v)); }
|
|
342
|
+
get closeOnOutsideClick() { return getModalAttributes(this).closeOnOutsideClick; }
|
|
343
|
+
set closeOnOutsideClick(v) { this.setAttribute('close-on-outside-click', String(!!v)); }
|
|
344
|
+
get closeOnEscape() { return getModalAttributes(this).closeOnEscape; }
|
|
345
|
+
set closeOnEscape(v) { this.setAttribute('close-on-escape', String(!!v)); }
|
|
346
|
+
get preventOutsideClick() { return parseBoolAttr(this, 'prevent-outside-click'); }
|
|
347
|
+
set preventOutsideClick(v) {
|
|
348
|
+
v ? this.setAttribute('prevent-outside-click', '') : this.removeAttribute('prevent-outside-click');
|
|
349
|
+
}
|
|
350
|
+
get preventEscape() { return parseBoolAttr(this, 'prevent-escape'); }
|
|
351
|
+
set preventEscape(v) {
|
|
352
|
+
v ? this.setAttribute('prevent-escape', '') : this.removeAttribute('prevent-escape');
|
|
310
353
|
}
|
|
311
354
|
constructor() {
|
|
312
355
|
super();
|
package/lib/components/popup.js
CHANGED
|
@@ -58,6 +58,7 @@ function updatePosition(el) {
|
|
|
58
58
|
});
|
|
59
59
|
el.style.setProperty('--popup-x', `${positionData.x}px`);
|
|
60
60
|
el.style.setProperty('--popup-y', `${positionData.y}px`);
|
|
61
|
+
dialog.dataset.side = positionData.placement.split('-')[0];
|
|
61
62
|
dialog.classList.add('position-calculated');
|
|
62
63
|
}
|
|
63
64
|
function closePopup(el, force = false) {
|
|
@@ -76,8 +77,7 @@ function closePopup(el, force = false) {
|
|
|
76
77
|
setTimeout(() => {
|
|
77
78
|
dialog.classList.remove('position-calculated');
|
|
78
79
|
dialog.close();
|
|
79
|
-
},
|
|
80
|
-
el.dispatchEvent(new CustomEvent('close', { bubbles: true }));
|
|
80
|
+
}, 200);
|
|
81
81
|
}
|
|
82
82
|
function openPopup(el) {
|
|
83
83
|
const popupId = getPopupId(el);
|
|
@@ -95,7 +95,7 @@ function openPopup(el) {
|
|
|
95
95
|
dialog.classList.add('preparing-animation');
|
|
96
96
|
requestAnimationFrame(() => {
|
|
97
97
|
dialog.classList.add('open');
|
|
98
|
-
el.dispatchEvent(new CustomEvent('open'
|
|
98
|
+
el.dispatchEvent(new CustomEvent('open'));
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
101
|
}
|
|
@@ -211,7 +211,7 @@ function render(el) {
|
|
|
211
211
|
unlockScroll(popupId);
|
|
212
212
|
dialog.classList.remove('open');
|
|
213
213
|
dialog.classList.remove('preparing-animation');
|
|
214
|
-
el.dispatchEvent(new CustomEvent('close'
|
|
214
|
+
el.dispatchEvent(new CustomEvent('close'));
|
|
215
215
|
});
|
|
216
216
|
}
|
|
217
217
|
el.closePopup = () => closePopup(el, true);
|
package/lib/components/radio.js
CHANGED
|
@@ -176,8 +176,16 @@ export class TyRadioGroup extends TyComponent {
|
|
|
176
176
|
const radios = this.getRadios();
|
|
177
177
|
for (const radio of radios) {
|
|
178
178
|
radio.checked = radio.value === this.value;
|
|
179
|
-
if (this.disabled)
|
|
180
|
-
radio.disabled
|
|
179
|
+
if (this.disabled) {
|
|
180
|
+
if (!radio.disabled) {
|
|
181
|
+
radio.disabled = true;
|
|
182
|
+
radio.dataset.tyGroupDisabled = "";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else if ("tyGroupDisabled" in radio.dataset) {
|
|
186
|
+
radio.disabled = false;
|
|
187
|
+
delete radio.dataset.tyGroupDisabled;
|
|
188
|
+
}
|
|
181
189
|
radio.size = this.size;
|
|
182
190
|
radio.flavor = this.flavor;
|
|
183
191
|
}
|
|
@@ -13,6 +13,7 @@ export declare class TyResizeObserver extends HTMLElement {
|
|
|
13
13
|
set debounce(value: number);
|
|
14
14
|
constructor();
|
|
15
15
|
connectedCallback(): void;
|
|
16
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
|
16
17
|
disconnectedCallback(): void;
|
|
17
18
|
private setupObserver;
|
|
18
19
|
private handleResize;
|
|
@@ -22,6 +22,15 @@ export class TyResizeObserver extends HTMLElement {
|
|
|
22
22
|
connectedCallback() {
|
|
23
23
|
this.setupObserver();
|
|
24
24
|
}
|
|
25
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
26
|
+
if (name === 'id' && oldValue && oldValue !== newValue) {
|
|
27
|
+
removeSize(oldValue);
|
|
28
|
+
if (newValue && this.isConnected) {
|
|
29
|
+
const { width, height } = this.getBoundingClientRect();
|
|
30
|
+
updateSize(newValue, width, height);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
25
34
|
disconnectedCallback() {
|
|
26
35
|
this.cleanup();
|
|
27
36
|
}
|
|
@@ -60,4 +69,6 @@ export class TyResizeObserver extends HTMLElement {
|
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
|
-
customElements.
|
|
72
|
+
if (!customElements.get('ty-resize-observer')) {
|
|
73
|
+
customElements.define('ty-resize-observer', TyResizeObserver);
|
|
74
|
+
}
|
package/lib/components/select.js
CHANGED
|
@@ -379,6 +379,7 @@ export class TySelect extends TyComponent {
|
|
|
379
379
|
popupHeight: estimatedHeight,
|
|
380
380
|
align: anchored.align,
|
|
381
381
|
side: anchored.side,
|
|
382
|
+
gap: 8,
|
|
382
383
|
});
|
|
383
384
|
const positionBelow = pos.below;
|
|
384
385
|
const x = pos.x - wrapPadding;
|
|
@@ -436,8 +437,8 @@ export class TySelect extends TyComponent {
|
|
|
436
437
|
this._scrollLockId = lockId;
|
|
437
438
|
lockScroll(lockId);
|
|
438
439
|
dialog.showModal();
|
|
439
|
-
dialog.classList.add("open");
|
|
440
440
|
this.calculatePosition();
|
|
441
|
+
dialog.classList.add("open");
|
|
441
442
|
this._state.open = true;
|
|
442
443
|
const chevron = shadow.querySelector(".dropdown-chevron");
|
|
443
444
|
if (chevron)
|
|
@@ -516,6 +517,7 @@ export class TySelect extends TyComponent {
|
|
|
516
517
|
this._scrollLockId = lockId;
|
|
517
518
|
lockScroll(lockId);
|
|
518
519
|
dialog.showModal();
|
|
520
|
+
dialog.getBoundingClientRect();
|
|
519
521
|
dialog.classList.add("open");
|
|
520
522
|
this._state.open = true;
|
|
521
523
|
const tags = this.getTagElements().map((el) => this.getTagData(el));
|
|
@@ -845,8 +847,6 @@ export class TySelect extends TyComponent {
|
|
|
845
847
|
fireOpenEvent() {
|
|
846
848
|
this.dispatchEvent(new CustomEvent("open", {
|
|
847
849
|
detail: { mode: this._state.mode, element: this },
|
|
848
|
-
bubbles: true,
|
|
849
|
-
composed: true,
|
|
850
850
|
}));
|
|
851
851
|
if (this._externalSearch) {
|
|
852
852
|
this.fireSearchEvent("");
|
|
@@ -855,8 +855,6 @@ export class TySelect extends TyComponent {
|
|
|
855
855
|
fireCloseEvent() {
|
|
856
856
|
this.dispatchEvent(new CustomEvent("close", {
|
|
857
857
|
detail: { mode: this._state.mode, element: this },
|
|
858
|
-
bubbles: true,
|
|
859
|
-
composed: true,
|
|
860
858
|
}));
|
|
861
859
|
}
|
|
862
860
|
dispatchChangeEvent(detail) {
|
|
@@ -105,7 +105,7 @@ export class TySelectedOptions extends HTMLElement {
|
|
|
105
105
|
return ctx;
|
|
106
106
|
}
|
|
107
107
|
templateSource(template) {
|
|
108
|
-
if (template.content.
|
|
108
|
+
if (template.content.hasChildNodes())
|
|
109
109
|
return template.content;
|
|
110
110
|
const frag = document.createDocumentFragment();
|
|
111
111
|
template.childNodes.forEach(n => frag.appendChild(n.cloneNode(true)));
|
package/lib/components/tabs.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface TabChangeDetail {
|
|
|
27
27
|
export declare class TyTabs extends HTMLElement {
|
|
28
28
|
static get observedAttributes(): string[];
|
|
29
29
|
constructor();
|
|
30
|
+
private _childObserver;
|
|
31
|
+
private _childRenderQueued;
|
|
30
32
|
connectedCallback(): void;
|
|
31
33
|
disconnectedCallback(): void;
|
|
32
34
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
package/lib/components/tabs.js
CHANGED
|
@@ -515,12 +515,27 @@ export class TyTabs extends HTMLElement {
|
|
|
515
515
|
}
|
|
516
516
|
constructor() {
|
|
517
517
|
super();
|
|
518
|
+
this._childObserver = null;
|
|
519
|
+
this._childRenderQueued = false;
|
|
518
520
|
this.attachShadow({ mode: 'open' });
|
|
519
521
|
}
|
|
520
522
|
connectedCallback() {
|
|
521
523
|
render(this);
|
|
524
|
+
this._childObserver = new MutationObserver(() => {
|
|
525
|
+
if (this._childRenderQueued)
|
|
526
|
+
return;
|
|
527
|
+
this._childRenderQueued = true;
|
|
528
|
+
queueMicrotask(() => {
|
|
529
|
+
this._childRenderQueued = false;
|
|
530
|
+
if (this.isConnected)
|
|
531
|
+
render(this);
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
this._childObserver.observe(this, { childList: true });
|
|
522
535
|
}
|
|
523
536
|
disconnectedCallback() {
|
|
537
|
+
this._childObserver?.disconnect();
|
|
538
|
+
this._childObserver = null;
|
|
524
539
|
cleanup(this);
|
|
525
540
|
}
|
|
526
541
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -57,7 +57,28 @@ function removeDescribedBy(anchor, id) {
|
|
|
57
57
|
anchor.removeAttribute('aria-describedby');
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
+
function ensureTooltipMotionStyles() {
|
|
61
|
+
if (document.getElementById('ty-tooltip-motion'))
|
|
62
|
+
return;
|
|
63
|
+
const style = document.createElement('style');
|
|
64
|
+
style.id = 'ty-tooltip-motion';
|
|
65
|
+
style.textContent = `
|
|
66
|
+
.ty-tooltip-popover:popover-open {
|
|
67
|
+
transition:
|
|
68
|
+
opacity var(--ty-tooltip-duration, 120ms) ease-out,
|
|
69
|
+
translate var(--ty-tooltip-duration, 120ms) ease-out;
|
|
70
|
+
}
|
|
71
|
+
@starting-style {
|
|
72
|
+
.ty-tooltip-popover:popover-open { opacity: 0; translate: 0 4px; }
|
|
73
|
+
.ty-tooltip-popover:popover-open[data-side="bottom"] { translate: 0 -4px; }
|
|
74
|
+
.ty-tooltip-popover:popover-open[data-side="left"] { translate: 4px 0; }
|
|
75
|
+
.ty-tooltip-popover:popover-open[data-side="right"] { translate: -4px 0; }
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
document.head.appendChild(style);
|
|
79
|
+
}
|
|
60
80
|
function getOrCreatePopover(el) {
|
|
81
|
+
ensureTooltipMotionStyles();
|
|
61
82
|
let popover = popoverElements.get(el);
|
|
62
83
|
if (!popover) {
|
|
63
84
|
popover = document.createElement('div');
|
|
@@ -143,6 +164,7 @@ function updatePosition(el) {
|
|
|
143
164
|
});
|
|
144
165
|
popover.style.left = `${position.x}px`;
|
|
145
166
|
popover.style.top = `${position.y}px`;
|
|
167
|
+
popover.dataset.side = position.placement.split('-')[0];
|
|
146
168
|
}
|
|
147
169
|
function cleanupAutoUpdate(el) {
|
|
148
170
|
const cleanup = autoUpdateCleanup.get(el);
|
|
@@ -30,6 +30,8 @@ export interface WizardStepChangeDetail {
|
|
|
30
30
|
export declare class TyWizard extends HTMLElement {
|
|
31
31
|
static get observedAttributes(): string[];
|
|
32
32
|
constructor();
|
|
33
|
+
private _childObserver;
|
|
34
|
+
private _childRenderQueued;
|
|
33
35
|
connectedCallback(): void;
|
|
34
36
|
disconnectedCallback(): void;
|
|
35
37
|
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
|
package/lib/components/wizard.js
CHANGED
|
@@ -399,12 +399,27 @@ export class TyWizard extends HTMLElement {
|
|
|
399
399
|
}
|
|
400
400
|
constructor() {
|
|
401
401
|
super();
|
|
402
|
+
this._childObserver = null;
|
|
403
|
+
this._childRenderQueued = false;
|
|
402
404
|
this.attachShadow({ mode: 'open' });
|
|
403
405
|
}
|
|
404
406
|
connectedCallback() {
|
|
405
407
|
render(this);
|
|
408
|
+
this._childObserver = new MutationObserver(() => {
|
|
409
|
+
if (this._childRenderQueued)
|
|
410
|
+
return;
|
|
411
|
+
this._childRenderQueued = true;
|
|
412
|
+
queueMicrotask(() => {
|
|
413
|
+
this._childRenderQueued = false;
|
|
414
|
+
if (this.isConnected)
|
|
415
|
+
render(this);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
this._childObserver.observe(this, { childList: true });
|
|
406
419
|
}
|
|
407
420
|
disconnectedCallback() {
|
|
421
|
+
this._childObserver?.disconnect();
|
|
422
|
+
this._childObserver = null;
|
|
408
423
|
cleanup(this);
|
|
409
424
|
}
|
|
410
425
|
attributeChangedCallback(name, oldValue, newValue) {
|
package/lib/styles/button.js
CHANGED
|
@@ -8,7 +8,7 @@ const solidFlavor = (f, fb) => {
|
|
|
8
8
|
const solid = (suffix) => tokenRef('--ty-solid', f, suffix, fb);
|
|
9
9
|
const borderL = f === 'neutral'
|
|
10
10
|
? ' --_border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));'
|
|
11
|
-
: ' --_border-l: var(--ty-button-border-l, -0.15);';
|
|
11
|
+
: ' --_border-l: var(--_muted-border-l, var(--ty-button-border-l, -0.15));';
|
|
12
12
|
const plusNudge = f === 'neutral' ? nudgedMid : nudged;
|
|
13
13
|
return `
|
|
14
14
|
button.solid.${f} { --_ring: ${tokenRef('--ty-color', f, '', fb)}; --_solid-bg: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('')})); --_solid-rest: var(--ty-button-bg, var(--_muted-solid-bg, ${solid('')}));${borderL} color: var(--ty-button-color, var(--_muted-solid-fg, ${solid('-fg')})); }
|
|
@@ -288,6 +288,7 @@ button.muted {
|
|
|
288
288
|
--_muted-solid-fg: var(--ty-solid-neutral-fg);
|
|
289
289
|
--_muted-outlined-line: var(--ty-color-neutral);
|
|
290
290
|
--_muted-ghost-fg: var(--ty-color-neutral);
|
|
291
|
+
--_muted-border-l: var(--ty-button-border-l-neutral, var(--ty-button-border-l, -0.15));
|
|
291
292
|
}
|
|
292
293
|
button.muted.tone-plus {
|
|
293
294
|
--_muted-solid-bg: var(--ty-solid-neutral-strong);
|
|
@@ -310,6 +311,7 @@ button.muted.tone-minus {
|
|
|
310
311
|
--_muted-solid-fg: unset;
|
|
311
312
|
--_muted-outlined-line: unset;
|
|
312
313
|
--_muted-ghost-fg: unset;
|
|
314
|
+
--_muted-border-l: unset;
|
|
313
315
|
}
|
|
314
316
|
}
|
|
315
317
|
button.muted:active:not(:disabled),
|
|
@@ -318,6 +320,7 @@ button.muted:focus-visible:not(:disabled) {
|
|
|
318
320
|
--_muted-solid-fg: unset;
|
|
319
321
|
--_muted-outlined-line: unset;
|
|
320
322
|
--_muted-ghost-fg: unset;
|
|
323
|
+
--_muted-border-l: unset;
|
|
321
324
|
}
|
|
322
325
|
|
|
323
326
|
button.pill {
|
|
@@ -252,7 +252,15 @@ ${FLAVORS.filter((f) => f !== "neutral").map((f) => datePickerFlavor(f)).join(""
|
|
|
252
252
|
padding: var(--calendar-padding, 8px);
|
|
253
253
|
|
|
254
254
|
opacity: 0;
|
|
255
|
-
|
|
255
|
+
/* Entrance motion uses the individual scale/translate properties so it
|
|
256
|
+
composes with the collision-shift transform below (they apply first). */
|
|
257
|
+
scale: 0.96;
|
|
258
|
+
translate: 0 -6px;
|
|
259
|
+
transform-origin: top center;
|
|
260
|
+
transition:
|
|
261
|
+
opacity var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1)),
|
|
262
|
+
scale var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1)),
|
|
263
|
+
translate var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1));
|
|
256
264
|
|
|
257
265
|
transform: translate(var(--calendar-offset-x, 0px), var(--calendar-offset-y, 0px));
|
|
258
266
|
top: -1000px;
|
|
@@ -269,10 +277,28 @@ ${FLAVORS.filter((f) => f !== "neutral").map((f) => datePickerFlavor(f)).join(""
|
|
|
269
277
|
bottom: var(--calendar-y);
|
|
270
278
|
top: auto;
|
|
271
279
|
flex-direction: column-reverse;
|
|
280
|
+
translate: 0 6px;
|
|
281
|
+
transform-origin: bottom center;
|
|
272
282
|
}
|
|
273
283
|
|
|
274
284
|
.calendar-dialog.open {
|
|
275
285
|
opacity: 1;
|
|
286
|
+
scale: 1;
|
|
287
|
+
translate: 0 0;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* First-frame "from" values — see select-base.ts: transitions can't start on
|
|
291
|
+
an element that was display:none last frame; @starting-style fixes that. */
|
|
292
|
+
@starting-style {
|
|
293
|
+
.calendar-dialog.open {
|
|
294
|
+
opacity: 0;
|
|
295
|
+
scale: 0.96;
|
|
296
|
+
translate: 0 -6px;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.calendar-dialog.open.position-above {
|
|
300
|
+
translate: 0 6px;
|
|
301
|
+
}
|
|
276
302
|
}
|
|
277
303
|
|
|
278
304
|
.calendar-dialog::backdrop {
|