tyrell-components 1.0.0-TC46 → 1.0.0-TC48
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/README.md +1 -1
- package/css/tyrell-theme.css +167 -178
- package/css/tyrell.css +19 -0
- package/dist/tyrell-theme.css +167 -178
- 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.d.ts +8 -0
- package/lib/components/date-picker.js +13 -9
- 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 +8 -0
- package/lib/components/modal.js +29 -6
- package/lib/components/popup.js +6 -10
- 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.d.ts +11 -3
- package/lib/components/select.js +26 -9
- package/lib/components/selected-tags.js +1 -1
- package/lib/components/tabs.d.ts +2 -0
- package/lib/components/tabs.js +18 -1
- package/lib/components/tooltip.js +24 -6
- package/lib/components/wizard.d.ts +2 -0
- package/lib/components/wizard.js +15 -0
- package/lib/styles/button.js +9 -2
- package/lib/styles/calendar-month.js +1 -1
- package/lib/styles/calendar-navigation.d.ts +1 -1
- package/lib/styles/calendar-navigation.js +1 -1
- package/lib/styles/calendar.d.ts +1 -1
- package/lib/styles/calendar.js +1 -1
- package/lib/styles/date-picker.js +30 -4
- package/lib/styles/file-upload.d.ts +1 -1
- package/lib/styles/file-upload.js +1 -1
- package/lib/styles/icon.d.ts +1 -1
- package/lib/styles/icon.js +12 -1
- package/lib/styles/input.js +2 -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 +16 -1
- package/lib/styles/popup.d.ts +1 -1
- package/lib/styles/popup.js +22 -5
- package/lib/styles/radio.js +1 -1
- package/lib/styles/select-base.js +59 -9
- package/lib/styles/select.d.ts +1 -1
- package/lib/styles/select.js +5 -1
- package/lib/styles/tabs.d.ts +1 -1
- package/lib/styles/tabs.js +6 -1
- package/lib/styles/textarea.d.ts +1 -1
- package/lib/styles/textarea.js +2 -1
- package/lib/styles/wizard.d.ts +1 -1
- package/lib/styles/wizard.js +3 -3
- package/lib/utils/number-format.d.ts +6 -1
- package/lib/utils/number-format.js +9 -2
- package/lib/utils/positioning.d.ts +50 -5
- package/lib/utils/positioning.js +52 -8
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findBestPosition,
|
|
1
|
+
import { findBestPosition, preferenceChain } from '../utils/positioning.js';
|
|
2
2
|
import { ensureStyles } from '../utils/styles.js';
|
|
3
3
|
import { tooltipStyles } from '../styles/tooltip.js';
|
|
4
4
|
const autoUpdateCleanup = new WeakMap();
|
|
@@ -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');
|
|
@@ -134,11 +155,7 @@ function updatePosition(el) {
|
|
|
134
155
|
console.warn('[ty-tooltip] updatePosition: missing anchor or popover');
|
|
135
156
|
return;
|
|
136
157
|
}
|
|
137
|
-
const preferences = placement
|
|
138
|
-
placement === 'bottom' ? ['bottom', 'top', 'left', 'right'] :
|
|
139
|
-
placement === 'left' ? ['left', 'right', 'top', 'bottom'] :
|
|
140
|
-
placement === 'right' ? ['right', 'left', 'top', 'bottom'] :
|
|
141
|
-
placementPreferences.tooltip;
|
|
158
|
+
const preferences = preferenceChain(placement);
|
|
142
159
|
const position = findBestPosition({
|
|
143
160
|
targetEl: anchor,
|
|
144
161
|
floatingEl: popover,
|
|
@@ -147,6 +164,7 @@ function updatePosition(el) {
|
|
|
147
164
|
});
|
|
148
165
|
popover.style.left = `${position.x}px`;
|
|
149
166
|
popover.style.top = `${position.y}px`;
|
|
167
|
+
popover.dataset.side = position.placement.split('-')[0];
|
|
150
168
|
}
|
|
151
169
|
function cleanupAutoUpdate(el) {
|
|
152
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')})); }
|
|
@@ -82,7 +82,7 @@ export const buttonCustomFlavorCss = (base) => solidFlavor(base, 'neutral') + ou
|
|
|
82
82
|
export const buttonStyles = `
|
|
83
83
|
:host {
|
|
84
84
|
display: inline-block;
|
|
85
|
-
font-family:
|
|
85
|
+
font-family: inherit;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
:host([wide]) {
|
|
@@ -91,6 +91,10 @@ export const buttonStyles = `
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
button {
|
|
94
|
+
/* Native form controls do NOT inherit font from the shadow host — the UA
|
|
95
|
+
stylesheet sets one on them, which beats inheritance. Must be explicit
|
|
96
|
+
here or the button silently falls back to the system font. */
|
|
97
|
+
font-family: inherit;
|
|
94
98
|
position: relative;
|
|
95
99
|
display: flex;
|
|
96
100
|
align-items: center;
|
|
@@ -284,6 +288,7 @@ button.muted {
|
|
|
284
288
|
--_muted-solid-fg: var(--ty-solid-neutral-fg);
|
|
285
289
|
--_muted-outlined-line: var(--ty-color-neutral);
|
|
286
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));
|
|
287
292
|
}
|
|
288
293
|
button.muted.tone-plus {
|
|
289
294
|
--_muted-solid-bg: var(--ty-solid-neutral-strong);
|
|
@@ -306,6 +311,7 @@ button.muted.tone-minus {
|
|
|
306
311
|
--_muted-solid-fg: unset;
|
|
307
312
|
--_muted-outlined-line: unset;
|
|
308
313
|
--_muted-ghost-fg: unset;
|
|
314
|
+
--_muted-border-l: unset;
|
|
309
315
|
}
|
|
310
316
|
}
|
|
311
317
|
button.muted:active:not(:disabled),
|
|
@@ -314,6 +320,7 @@ button.muted:focus-visible:not(:disabled) {
|
|
|
314
320
|
--_muted-solid-fg: unset;
|
|
315
321
|
--_muted-outlined-line: unset;
|
|
316
322
|
--_muted-ghost-fg: unset;
|
|
323
|
+
--_muted-border-l: unset;
|
|
317
324
|
}
|
|
318
325
|
|
|
319
326
|
button.pill {
|
|
@@ -42,7 +42,7 @@ export const calendarMonthStyles = `
|
|
|
42
42
|
gap: 0;
|
|
43
43
|
padding: 0.75rem;
|
|
44
44
|
border-radius: var(--ty-radius-md);
|
|
45
|
-
font-family:
|
|
45
|
+
font-family: inherit;
|
|
46
46
|
user-select: none;
|
|
47
47
|
width: var(--calendar-width, fit-content);
|
|
48
48
|
min-width: 280px;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Calendar Navigation Styles */
|
|
2
|
-
export declare const calendarNavigationStyles = "\n/* Theming tokens applied as var(--ty-calendar-nav-*, <default>) at point of\n use (NOT on :host) so consumers can override them from outside \u2014 this\n component is nested inside ty-calendar's shadow, so a :host default would\n block inherited overrides. --ty-calendar-accent still cascades in. */\n\n:host([data-size=\"md\"]) {\n --nav-btn-size: 2rem;\n --nav-btn-icon-size: 1.25rem;\n --nav-font-size: 1rem;\n --nav-padding: 0.5rem 0.75rem;\n --nav-default-width: 280px;\n}\n\n:host([data-size=\"sm\"]) {\n --nav-btn-size: 1.5rem;\n --nav-btn-icon-size: 1rem;\n --nav-font-size: 0.875rem;\n --nav-padding: 0.375rem 0.5rem;\n --nav-default-width: 240px;\n}\n\n:host([data-size=\"lg\"]) {\n --nav-btn-size: 2.5rem;\n --nav-btn-icon-size: 1.5rem;\n --nav-font-size: 1.125rem;\n --nav-padding: 0.625rem 1rem;\n --nav-default-width: 360px;\n}\n\n.calendar-navigation-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--nav-padding);\n font-family:
|
|
2
|
+
export declare const calendarNavigationStyles = "\n/* Theming tokens applied as var(--ty-calendar-nav-*, <default>) at point of\n use (NOT on :host) so consumers can override them from outside \u2014 this\n component is nested inside ty-calendar's shadow, so a :host default would\n block inherited overrides. --ty-calendar-accent still cascades in. */\n\n:host([data-size=\"md\"]) {\n --nav-btn-size: 2rem;\n --nav-btn-icon-size: 1.25rem;\n --nav-font-size: 1rem;\n --nav-padding: 0.5rem 0.75rem;\n --nav-default-width: 280px;\n}\n\n:host([data-size=\"sm\"]) {\n --nav-btn-size: 1.5rem;\n --nav-btn-icon-size: 1rem;\n --nav-font-size: 0.875rem;\n --nav-padding: 0.375rem 0.5rem;\n --nav-default-width: 240px;\n}\n\n:host([data-size=\"lg\"]) {\n --nav-btn-size: 2.5rem;\n --nav-btn-icon-size: 1.5rem;\n --nav-font-size: 1.125rem;\n --nav-padding: 0.625rem 1rem;\n --nav-default-width: 360px;\n}\n\n.calendar-navigation-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--nav-padding);\n font-family: inherit;\n user-select: none;\n width: var(--nav-width, var(--nav-default-width));\n box-sizing: border-box;\n}\n\n/* Navigation groups: left, center, right */\n.nav-group {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n}\n\n.nav-group-left,\n.nav-group-right {\n flex: 0 0 auto;\n}\n\n.nav-group-center {\n flex: 1;\n justify-content: center;\n}\n\n.nav-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--nav-btn-size);\n height: var(--nav-btn-size);\n padding: 0;\n border: none;\n border-radius: var(--ty-radius-base);\n background-color: transparent;\n color: var(--ty-calendar-nav-color, var(--ty-color-neutral));\n cursor: pointer;\n transition: var(--ty-local-transition, all 0.15s ease);\n outline: none;\n}\n\n.nav-btn:hover {\n background-color: var(--ty-calendar-nav-hover-bg, var(--ty-bg-neutral-soft));\n color: var(--ty-calendar-nav-hover-color, var(--ty-color-neutral-strong));\n}\n\n.nav-btn:active {\n background-color: var(--ty-calendar-nav-active-bg, var(--ty-bg-neutral));\n transform: scale(0.95);\n}\n\n.nav-btn:disabled {\n opacity: 0.35;\n cursor: default;\n pointer-events: none;\n}\n\n.nav-btn:focus-visible {\n outline: 2px solid var(--ty-calendar-nav-focus-outline, var(--ty-calendar-accent, var(--ty-color-primary)));\n outline-offset: 2px;\n}\n\n.nav-btn svg {\n width: var(--nav-btn-icon-size);\n height: var(--nav-btn-icon-size);\n display: block;\n}\n\n.month-year-display {\n font-size: var(--nav-font-size);\n font-weight: 600;\n color: var(--ty-calendar-nav-title-color, var(--ty-color-neutral-strong));\n text-align: center;\n white-space: nowrap;\n letter-spacing: -0.01em;\n}\n";
|
|
@@ -33,7 +33,7 @@ export const calendarNavigationStyles = `
|
|
|
33
33
|
align-items: center;
|
|
34
34
|
justify-content: space-between;
|
|
35
35
|
padding: var(--nav-padding);
|
|
36
|
-
font-family:
|
|
36
|
+
font-family: inherit;
|
|
37
37
|
user-select: none;
|
|
38
38
|
width: var(--nav-width, var(--nav-default-width));
|
|
39
39
|
box-sizing: border-box;
|
package/lib/styles/calendar.d.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* Calendar Orchestration Styles
|
|
3
3
|
* Simple container for navigation + month display
|
|
4
4
|
*/
|
|
5
|
-
export declare const calendarStyles = "\n.calendar-container {\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n box-sizing: border-box;\n font-family:
|
|
5
|
+
export declare const calendarStyles = "\n.calendar-container {\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n box-sizing: border-box;\n font-family: inherit;\n}\n\n@media (max-width: 320px) {\n .calendar-container {\n gap: 0.375rem;\n }\n}\n";
|
package/lib/styles/calendar.js
CHANGED
|
@@ -31,7 +31,7 @@ export const datePickerStyles = `
|
|
|
31
31
|
display: block;
|
|
32
32
|
width: auto;
|
|
33
33
|
min-width: 200px;
|
|
34
|
-
font-family:
|
|
34
|
+
font-family: inherit;
|
|
35
35
|
|
|
36
36
|
/* Theming tokens — date-picker stub. Thin shim over --ty-input-*: override
|
|
37
37
|
these to retheme just the date-picker trigger, not other inputs. */
|
|
@@ -123,7 +123,7 @@ export const datePickerStyles = `
|
|
|
123
123
|
color: var(--ty-date-picker-color);
|
|
124
124
|
border: 1px solid var(--date-picker-accent, var(--ty-date-picker-border));
|
|
125
125
|
border-radius: var(--ty-date-picker-radius);
|
|
126
|
-
font-family:
|
|
126
|
+
font-family: inherit;
|
|
127
127
|
font-size: var(--ty-font-sm);
|
|
128
128
|
font-weight: var(--ty-font-normal);
|
|
129
129
|
line-height: var(--ty-line-height-tight);
|
|
@@ -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 {
|
|
@@ -341,7 +367,7 @@ ${FLAVORS.filter((f) => f !== "neutral").map((f) => datePickerFlavor(f)).join(""
|
|
|
341
367
|
border-radius: var(--ty-radius-sm);
|
|
342
368
|
background: var(--ty-input-bg);
|
|
343
369
|
color: var(--ty-calendar-time-input-color);
|
|
344
|
-
font-family:
|
|
370
|
+
font-family: inherit;
|
|
345
371
|
font-size: var(--ty-font-sm);
|
|
346
372
|
font-variant-numeric: tabular-nums;
|
|
347
373
|
text-align: center;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const fileUploadStyles = "\n:host {\n display: block;\n font-family:
|
|
1
|
+
export declare const fileUploadStyles = "\n:host {\n display: block;\n font-family: inherit;\n width: 100%;\n}\n\n.file-upload-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n box-sizing: border-box;\n}\n\n.upload-label {\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n letter-spacing: var(--ty-tracking-sm);\n font-weight: var(--ty-font-medium);\n color: var(--ty-label-color);\n margin-bottom: 6px;\n padding-left: 4px;\n display: flex;\n align-items: center;\n}\n\n.required-icon {\n color: var(--ty-color-danger);\n margin-left: 4px;\n font-size: 0.75rem;\n line-height: 1;\n}\n\n.drop-zone {\n border: 2px dashed var(--ty-border);\n border-radius: var(--ty-radius-base);\n padding: 2rem 1.5rem;\n text-align: center;\n cursor: pointer;\n transition: var(--ty-local-transition, border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease);\n outline: none;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n min-height: 140px;\n user-select: none;\n}\n\n.drop-zone:hover:not(.disabled) {\n border-color: var(--ty-input-border-hover);\n background: var(--ty-surface-content);\n}\n\n.drop-zone.focused {\n border-color: var(--ty-input-border-focus);\n box-shadow: 0 0 0 3px var(--ty-input-shadow-focus);\n}\n\n.drop-zone.drag-active {\n border-color: var(--ty-color-primary);\n border-style: solid;\n background: var(--ty-bg-primary-);\n}\n\n.drop-zone.drag-active .upload-icon {\n color: var(--ty-color-primary);\n transform: translateY(-2px);\n}\n\n.drop-zone.has-files {\n min-height: 80px;\n padding: 1.25rem 1.5rem;\n}\n\n.drop-zone.disabled {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.drop-zone.error {\n border-color: var(--ty-color-danger);\n}\n\n.drop-zone.error:not(.disabled) {\n background: var(--ty-bg-danger-);\n}\n\n.upload-icon {\n color: var(--ty-text-faint);\n width: 2.5rem;\n height: 2.5rem;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: var(--ty-local-transition, color 0.15s ease, transform 0.15s ease);\n pointer-events: none;\n}\n\n.upload-icon svg {\n width: 100%;\n height: 100%;\n}\n\n.upload-hint {\n color: var(--ty-text-soft);\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n pointer-events: none;\n}\n\n.browse-link {\n color: var(--ty-color-primary);\n font-weight: var(--ty-font-medium);\n}\n\n.upload-sub-hint {\n color: var(--ty-text-faint);\n font-size: var(--ty-font-xs);\n line-height: var(--ty-leading-xs);\n pointer-events: none;\n}\n\n.file-list {\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n margin-top: 0.5rem;\n}\n\n.file-item {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 0.75rem;\n border-radius: var(--ty-radius-base);\n background: var(--ty-surface-content);\n border: 1px solid var(--ty-border-soft);\n}\n\n.file-icon {\n color: var(--ty-text-faint);\n flex-shrink: 0;\n width: 1rem;\n height: 1rem;\n display: flex;\n align-items: center;\n}\n\n.file-icon svg {\n width: 100%;\n height: 100%;\n}\n\n.file-name {\n flex: 1;\n font-size: var(--ty-font-sm);\n color: var(--ty-text);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n\n.file-size {\n font-size: var(--ty-font-xs);\n color: var(--ty-text-faint);\n flex-shrink: 0;\n font-variant-numeric: tabular-nums;\n}\n\n.file-remove {\n flex-shrink: 0;\n cursor: pointer;\n color: var(--ty-text-faint);\n width: 1.25rem;\n height: 1.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition: var(--ty-local-transition, color 0.15s ease, background-color 0.15s ease);\n border: none;\n background: none;\n padding: 0;\n outline: none;\n}\n\n.file-remove:hover {\n color: var(--ty-color-danger);\n background: var(--ty-bg-danger-);\n}\n\n.file-remove:focus-visible {\n box-shadow: 0 0 0 2px var(--ty-color-danger);\n}\n\n.file-remove svg {\n width: 12px;\n height: 12px;\n}\n\n.error-message {\n font-size: var(--ty-font-xs);\n line-height: var(--ty-leading-xs);\n color: var(--ty-color-danger);\n margin-top: 4px;\n padding-left: 4px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .drop-zone,\n .upload-icon,\n .file-remove {\n transition: none;\n }\n}\n\n@media (prefers-contrast: high) {\n .drop-zone {\n border-width: 3px;\n }\n}\n";
|
package/lib/styles/icon.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Icon Component Styles */
|
|
2
|
-
export declare const iconStyles = "\n:host {\n /* Display & Layout - CRITICAL: No containment to prevent paint deferral */\n
|
|
2
|
+
export declare const iconStyles = "\n:host {\n /* Display & Layout - CRITICAL: No containment to prevent paint deferral */\n /* Block-level flex, NOT inline-flex: as an inline box the icon generates a\n line box in its parent, so a shrink-wrapping wrapper (badge, .rounded-full\n chip) inherits the parent's line-height strut and renders taller than wide\n \u2014 a 12px icon in a 24px-line-height badge becomes a 22x34 oval. To place an\n icon inline with text, make the *text wrapper* inline-flex instead. */\n display: flex;\n align-items: center;\n justify-content: center;\n line-height: 0;\n \n width: var(--ty-icon-size, 1em);\n height: var(--ty-icon-size, 1em);\n \n aspect-ratio: 1 / 1;\n \n \n color: inherit;\n transition: color var(--ty-transition-fast);\n \n flex-shrink: 0;\n \n /* Visibility - Force immediate rendering */\n visibility: visible !important;\n opacity: 1;\n \n /* ANTI-FLICKER: Force browser to paint immediately */\n will-change: contents;\n \n /* Prevent any sub-pixel rendering issues */\n transform: translateZ(0);\n -webkit-font-smoothing: antialiased;\n}\n\n/* The SVG element inside shadow DOM (registry / fallback path) */\n:host svg {\n width: 100%;\n height: 100%;\n display: block;\n\n /* Force immediate SVG rendering */\n will-change: auto;\n transform: translateZ(0);\n}\n\n/* Slotted SVG (server-side path: <ty-icon><svg>...</svg></ty-icon>).\n ::slotted() targets light-DOM children so they fill the host the same way\n shadow-DOM SVG does. Color is inherited from :host via currentColor. */\n::slotted(svg),\n::slotted(img) {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n/* In a named slot the icon sits alongside text (button start/end), so inline\n layout is what the host expects there. */\n:host([slot]) {\n display: inline-flex;\n}\n\n:host(.icon-xs) {\n width: 0.75em;\n height: 0.75em;\n}\n\n:host(.icon-sm) {\n width: 0.875em;\n height: 0.875em;\n}\n\n:host(.icon-md) {\n width: 1em;\n height: 1em;\n}\n\n:host(.icon-lg) {\n width: 1.25em;\n height: 1.25em;\n}\n\n:host(.icon-xl) {\n width: 1.5em;\n height: 1.5em;\n}\n\n:host(.icon-2xl) {\n width: 2em;\n height: 2em;\n}\n\n/* Fixed pixel sizes */\n:host(.icon-12) {\n width: 12px;\n height: 12px;\n}\n\n:host(.icon-14) {\n width: 14px;\n height: 14px;\n}\n\n:host(.icon-16) {\n width: 16px;\n height: 16px;\n}\n\n:host(.icon-18) {\n width: 18px;\n height: 18px;\n}\n\n:host(.icon-20) {\n width: 20px;\n height: 20px;\n}\n\n:host(.icon-24) {\n width: 24px;\n height: 24px;\n}\n\n:host(.icon-32) {\n width: 32px;\n height: 32px;\n}\n\n:host(.icon-48) {\n width: 48px;\n height: 48px;\n}\n\n:host(.icon-64) {\n width: 64px;\n height: 64px;\n}\n\n:host(.icon-80) {\n width: 80px;\n height: 80px;\n}\n\n:host(.icon-96) {\n width: 96px;\n height: 96px;\n}\n\n:host(.icon-spin) {\n animation: icon-spin 1s linear infinite;\n}\n\n:host(.icon-spin.icon-tempo-slow) {\n animation: icon-spin 2s linear infinite;\n}\n\n:host(.icon-spin.icon-tempo-fast) {\n animation: icon-spin 0.5s linear infinite;\n}\n\n@keyframes icon-spin {\n from {\n transform: rotate(0deg);\n }\n\n to {\n transform: rotate(360deg);\n }\n}\n\n:host(.icon-pulse) {\n animation: icon-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n:host(.icon-pulse.icon-tempo-slow) {\n animation: icon-pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n:host(.icon-pulse.icon-tempo-fast) {\n animation: icon-pulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n@keyframes icon-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n\n 50% {\n opacity: 0.5;\n }\n}\n\n/* Accessibility: ensure icons are not announced by screen readers by default */\n:host {\n -webkit-user-select: none;\n user-select: none;\n}\n\n:host(:not(.icon-clickable)) {\n pointer-events: none;\n}\n\n:host(.icon-clickable) {\n cursor: pointer;\n pointer-events: auto;\n transition: color var(--ty-transition-fast), transform var(--ty-transition-fast);\n}\n\n:host(.icon-clickable:hover) {\n transform: scale(1.1);\n}\n\n:host(.icon-clickable:active) {\n transform: scale(0.95);\n}\n\n:host(.icon-disabled) {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n:host(.icon-block) {\n display: block;\n}\n";
|
package/lib/styles/icon.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export const iconStyles = `
|
|
2
2
|
:host {
|
|
3
3
|
/* Display & Layout - CRITICAL: No containment to prevent paint deferral */
|
|
4
|
-
|
|
4
|
+
/* Block-level flex, NOT inline-flex: as an inline box the icon generates a
|
|
5
|
+
line box in its parent, so a shrink-wrapping wrapper (badge, .rounded-full
|
|
6
|
+
chip) inherits the parent's line-height strut and renders taller than wide
|
|
7
|
+
— a 12px icon in a 24px-line-height badge becomes a 22x34 oval. To place an
|
|
8
|
+
icon inline with text, make the *text wrapper* inline-flex instead. */
|
|
9
|
+
display: flex;
|
|
5
10
|
align-items: center;
|
|
6
11
|
justify-content: center;
|
|
7
12
|
line-height: 0;
|
|
@@ -50,6 +55,12 @@ export const iconStyles = `
|
|
|
50
55
|
display: block;
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
/* In a named slot the icon sits alongside text (button start/end), so inline
|
|
59
|
+
layout is what the host expects there. */
|
|
60
|
+
:host([slot]) {
|
|
61
|
+
display: inline-flex;
|
|
62
|
+
}
|
|
63
|
+
|
|
53
64
|
:host(.icon-xs) {
|
|
54
65
|
width: 0.75em;
|
|
55
66
|
height: 0.75em;
|
package/lib/styles/input.js
CHANGED
|
@@ -29,7 +29,7 @@ export const inputCustomFlavorCss = (base) => inputFlavor(base, "neutral");
|
|
|
29
29
|
export const inputStyles = `
|
|
30
30
|
:host {
|
|
31
31
|
display: block;
|
|
32
|
-
font-family:
|
|
32
|
+
font-family: inherit;
|
|
33
33
|
width: 100%;
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -178,6 +178,7 @@ export const inputStyles = `
|
|
|
178
178
|
|
|
179
179
|
input {
|
|
180
180
|
/* Linear-paired typography */
|
|
181
|
+
font-family: inherit;
|
|
181
182
|
flex: 1;
|
|
182
183
|
min-width: 0;
|
|
183
184
|
box-sizing: border-box;
|
package/lib/styles/modal.d.ts
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* CSS for the modal component wrapper.
|
|
5
5
|
* The modal is a pure wrapper - all content styling is user-defined.
|
|
6
6
|
*/
|
|
7
|
-
export declare const modalStyles = "\n:host {\n display: contents;\n /* Don't interfere with layout */\n}\n\n/* Part of the modal wrapper functionality, not user content styling */\n.close-button {\n position: absolute;\n top: -8px;\n right: -8px;\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n transition: var(--ty-transition-colors);\n z-index: 1;\n color: #d8d8d8;\n\n /* Remove all default browser styling that could cause outlines/borders */\n outline: none;\n box-shadow: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n\n /* Remove mobile tap highlights and focus rings */\n -webkit-tap-highlight-color: transparent;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n\n.close-button:hover {\n color: white;\n}\n\n/* Explicitly remove focus and active states for all interaction modes */\n.close-button:focus,\n.close-button:focus-visible,\n.close-button:focus-within,\n.close-button:active {\n outline: none !important;\n box-shadow: none !important;\n border: none !important;\n background: transparent !important;\n}\n\n/* Override hover color even in focus/active states */\n.close-button:hover,\n.close-button:focus:hover,\n.close-button:active:hover {\n color: white;\n}\n\n.close-button.hide {\n visibility: hidden;\n}\n\n/* Pure wrapper: only essential modal functionality, no styling of user content */\n.ty-modal-dialog {\n /* Reset default dialog styles */\n padding: 18px;\n border: none;\n margin: 0;\n background: transparent;\n\n /* Centered */\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n\n z-index: 1000;\n overflow: hidden;\n\n /* No sizing - let user content determine size */\n max-width: 85vw;\n max-height: 90vh;\n\n outline: none;\n}\n\n/* Backdrop is part of modal functionality, not user content */\n.ty-modal-dialog::backdrop {\n background: var(--ty-modal-backdrop, rgba(0, 0, 0, 0.5));\n backdrop-filter: var(--ty-modal-backdrop-blur, blur(2px));\n animation: ty-modal-backdrop-enter var(--ty-modal-duration, 200ms) ease-out;\n}\n\n/* When backdrop is disabled */\n.ty-modal-dialog:not([data-backdrop])::backdrop {\n background: transparent;\n backdrop-filter: none;\n}\n\n/* Minimal container - no styling imposed on user content */\n.modal-content {\n color: var(--ty-text);\n}\n\n@keyframes ty-modal-backdrop-enter {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n}\n";
|
|
7
|
+
export declare const modalStyles = "\n:host {\n display: contents;\n /* Don't interfere with layout */\n}\n\n/* Part of the modal wrapper functionality, not user content styling */\n.close-button {\n position: absolute;\n top: -8px;\n right: -8px;\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n transition: var(--ty-transition-colors);\n z-index: 1;\n color: #d8d8d8;\n\n /* Remove all default browser styling that could cause outlines/borders */\n outline: none;\n box-shadow: none;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n\n /* Remove mobile tap highlights and focus rings */\n -webkit-tap-highlight-color: transparent;\n -webkit-touch-callout: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n\n.close-button:hover {\n color: white;\n}\n\n/* Explicitly remove focus and active states for all interaction modes */\n.close-button:focus,\n.close-button:focus-visible,\n.close-button:focus-within,\n.close-button:active {\n outline: none !important;\n box-shadow: none !important;\n border: none !important;\n background: transparent !important;\n}\n\n/* Override hover color even in focus/active states */\n.close-button:hover,\n.close-button:focus:hover,\n.close-button:active:hover {\n color: white;\n}\n\n.close-button.hide {\n visibility: hidden;\n}\n\n/* Pure wrapper: only essential modal functionality, no styling of user content */\n.ty-modal-dialog {\n /* Reset default dialog styles */\n padding: 18px;\n border: none;\n margin: 0;\n background: transparent;\n\n /* Centered */\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n\n z-index: 1000;\n overflow: hidden;\n\n /* No sizing - let user content determine size */\n max-width: 85vw;\n max-height: 90vh;\n\n outline: none;\n}\n\n/* Panel zoom-in synced with the backdrop fade (shadcn-style zoom-in-95).\n Enter-only: animating exit on a native <dialog> means delaying close(). */\n.ty-modal-dialog[open] {\n animation: ty-modal-enter var(--ty-modal-duration, 200ms) ease-out;\n}\n\n@keyframes ty-modal-enter {\n from {\n opacity: 0;\n transform: translate(-50%, -50%) scale(0.93);\n }\n\n to {\n opacity: 1;\n transform: translate(-50%, -50%) scale(1);\n }\n}\n\n/* Backdrop is part of modal functionality, not user content */\n.ty-modal-dialog::backdrop {\n background: var(--ty-modal-backdrop, rgba(0, 0, 0, 0.5));\n backdrop-filter: var(--ty-modal-backdrop-blur, blur(2px));\n animation: ty-modal-backdrop-enter var(--ty-modal-duration, 200ms) ease-out;\n}\n\n/* When backdrop is disabled */\n.ty-modal-dialog:not([data-backdrop])::backdrop {\n background: transparent;\n backdrop-filter: none;\n}\n\n/* Minimal container - no styling imposed on user content */\n.modal-content {\n color: var(--ty-text);\n}\n\n@keyframes ty-modal-backdrop-enter {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n}\n";
|
package/lib/styles/modal.js
CHANGED
|
@@ -84,6 +84,24 @@ export const modalStyles = `
|
|
|
84
84
|
outline: none;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/* Panel zoom-in synced with the backdrop fade (shadcn-style zoom-in-95).
|
|
88
|
+
Enter-only: animating exit on a native <dialog> means delaying close(). */
|
|
89
|
+
.ty-modal-dialog[open] {
|
|
90
|
+
animation: ty-modal-enter var(--ty-modal-duration, 200ms) ease-out;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@keyframes ty-modal-enter {
|
|
94
|
+
from {
|
|
95
|
+
opacity: 0;
|
|
96
|
+
transform: translate(-50%, -50%) scale(0.93);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
to {
|
|
100
|
+
opacity: 1;
|
|
101
|
+
transform: translate(-50%, -50%) scale(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
87
105
|
/* Backdrop is part of modal functionality, not user content */
|
|
88
106
|
.ty-modal-dialog::backdrop {
|
|
89
107
|
background: var(--ty-modal-backdrop, rgba(0, 0, 0, 0.5));
|
package/lib/styles/option.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Option Component Styles */
|
|
2
|
-
export declare const optionStyles = "\n.option-content {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--ty-spacing-2);\n box-sizing: border-box;\n width: 100%;\n\n cursor: pointer;\n user-select: none;\n\n transition: var(--ty-transition-all);\n\n /* Linear-paired typography */\n color: var(--ty-text);\n font-family:
|
|
2
|
+
export declare const optionStyles = "\n.option-content {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--ty-spacing-2);\n box-sizing: border-box;\n width: 100%;\n\n cursor: pointer;\n user-select: none;\n\n transition: var(--ty-transition-all);\n\n /* Linear-paired typography */\n color: var(--ty-text);\n font-family: inherit;\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n letter-spacing: var(--ty-tracking-sm);\n padding: var(--ty-spacing-2) var(--ty-spacing-3);\n border-radius: var(--ty-radius-md);\n}\n\n/* Cloned copy (projected into the select's trigger field): host defaults to\n inline like any unstyled custom element, which ignores width/overflow \u2014\n force a block box so the parent's flex-basis:0 truncation (select.ts\n .select-stub.has-clone slot[name=\"selected\"]) actually clips it, and\n collapse the content to a single ellipsized line instead of wrapping. */\n:host([cloned]) {\n display: block;\n min-width: 0;\n overflow: hidden;\n}\n:host([cloned]) .option-content {\n display: block;\n padding: 0px;\n background: transparent;\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.option-content:hover {\n background: var(--ty-bg-neutral-soft);\n}\n\n.option-content[highlighted] {\n background: var(--ty-bg-neutral-soft);\n color: var(--ty-text-strong);\n font-weight: var(--ty-font-semibold);\n}\n\n/* Selected: quiet \u2014 bolder text color only, no background slab, no weight change */\n.option-content[selected] {\n background: transparent;\n color: var(--ty-text-bold);\n}\n\n/* Lucide check \u2014 right-aligned, visible only on selected options */\n.option-check {\n display: none;\n flex-shrink: 0;\n width: 14px;\n height: 14px;\n margin-left: auto;\n color: var(--ty-color-primary);\n}\n\n.option-content[selected] .option-check {\n display: block;\n}\n\n/* Never show the check on the copy cloned into the select's trigger field */\n:host([cloned]) .option-check {\n display: none;\n}\n\n.option-content[selected]:hover {\n background: var(--ty-bg-neutral-soft);\n}\n\n.option-content[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.option-content[hidden] {}\n\n.option-content * {\n pointer-events: none;\n}\n";
|
package/lib/styles/option.js
CHANGED
|
@@ -14,7 +14,7 @@ export const optionStyles = `
|
|
|
14
14
|
|
|
15
15
|
/* Linear-paired typography */
|
|
16
16
|
color: var(--ty-text);
|
|
17
|
-
font-family:
|
|
17
|
+
font-family: inherit;
|
|
18
18
|
font-size: var(--ty-font-sm);
|
|
19
19
|
line-height: var(--ty-leading-sm);
|
|
20
20
|
letter-spacing: var(--ty-tracking-sm);
|
|
@@ -22,9 +22,24 @@ export const optionStyles = `
|
|
|
22
22
|
border-radius: var(--ty-radius-md);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/* Cloned copy (projected into the select's trigger field): host defaults to
|
|
26
|
+
inline like any unstyled custom element, which ignores width/overflow —
|
|
27
|
+
force a block box so the parent's flex-basis:0 truncation (select.ts
|
|
28
|
+
.select-stub.has-clone slot[name="selected"]) actually clips it, and
|
|
29
|
+
collapse the content to a single ellipsized line instead of wrapping. */
|
|
30
|
+
:host([cloned]) {
|
|
31
|
+
display: block;
|
|
32
|
+
min-width: 0;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
}
|
|
25
35
|
:host([cloned]) .option-content {
|
|
36
|
+
display: block;
|
|
26
37
|
padding: 0px;
|
|
27
38
|
background: transparent;
|
|
39
|
+
min-width: 0;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
text-overflow: ellipsis;
|
|
28
43
|
}
|
|
29
44
|
|
|
30
45
|
.option-content:hover {
|
package/lib/styles/popup.d.ts
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* Popup component with dropdown-like behavior and tooltip positioning.
|
|
5
5
|
* Uses dialog element for top layer rendering like dropdown and date-picker.
|
|
6
6
|
*/
|
|
7
|
-
export declare const popupStyles = "\n.popup-dialog {\n position: fixed;\n top: 0;\n left: 0;\n\n /* Reset dialog defaults */\n margin: 0;\n border: none;\n background: transparent;\n max-width: none;\n max-height: none;\n\n /* Shadow infrastructure - provide space for user shadows */\n padding: var(--popup-padding, 16px);\n\n /* Hidden by default - even when [open] (for positioning phase) */\n visibility: hidden;\n opacity: 0;\n /* NO TRANSFORM here - apply only when animating to avoid measurement errors! */\n\n transition:\n opacity
|
|
7
|
+
export declare const popupStyles = "\n.popup-dialog {\n position: fixed;\n top: 0;\n left: 0;\n\n /* Reset dialog defaults */\n margin: 0;\n border: none;\n background: transparent;\n max-width: none;\n max-height: none;\n\n /* Shadow infrastructure - provide space for user shadows */\n padding: var(--popup-padding, 16px);\n\n /* Hidden by default - even when [open] (for positioning phase) */\n visibility: hidden;\n opacity: 0;\n /* NO TRANSFORM here - apply only when animating to avoid measurement errors! */\n\n transition:\n opacity var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1)),\n visibility var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1)),\n transform var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1));\n\n /* Prevent interactions when hidden */\n pointer-events: none;\n}\n\n/* Apply position variables only when position is calculated (like dropdown) */\n.popup-dialog.position-calculated {\n top: var(--popup-y, 0px);\n left: var(--popup-x, 0px);\n}\n\n/* Entrance prep (right before .open): scale + a short slide from the anchor\n side. data-side is the RESOLVED side (post-flip), set by updatePosition. */\n.popup-dialog.preparing-animation {\n transform: scale(0.96);\n}\n\n.popup-dialog[data-side=\"bottom\"].preparing-animation:not(.open) {\n transform: translateY(-6px) scale(0.96);\n}\n\n.popup-dialog[data-side=\"top\"].preparing-animation:not(.open) {\n transform: translateY(6px) scale(0.96);\n}\n\n.popup-dialog[data-side=\"left\"].preparing-animation:not(.open) {\n transform: translateX(6px) scale(0.96);\n}\n\n.popup-dialog[data-side=\"right\"].preparing-animation:not(.open) {\n transform: translateX(-6px) scale(0.96);\n}\n\n.popup-dialog.open {\n visibility: visible;\n opacity: 1;\n transform: scale(1);\n pointer-events: auto;\n}\n\n/* Make dialog backdrop transparent like dropdown/date-picker */\n.popup-dialog::backdrop {\n background: transparent;\n}\n\n/* Neutral structural container \u2014 users control all visuals via slotted content */\n.popup-container {\n display: contents;\n}\n\n/* Slotted content defines its own styling */\n#popup-content {\n display: contents;\n}\n\n/* Users have complete control over the popup's appearance; only essential\n positioning is imposed here. */\n#popup-content ::slotted(*) {\n position: relative;\n\n /* Prevent content from being too wide - still helpful */\n max-width: min(400px, 90vw);\n max-height: min(500px, 80vh);\n overflow: auto;\n}\n";
|
package/lib/styles/popup.js
CHANGED
|
@@ -20,9 +20,9 @@ export const popupStyles = `
|
|
|
20
20
|
/* NO TRANSFORM here - apply only when animating to avoid measurement errors! */
|
|
21
21
|
|
|
22
22
|
transition:
|
|
23
|
-
opacity
|
|
24
|
-
visibility
|
|
25
|
-
transform
|
|
23
|
+
opacity var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1)),
|
|
24
|
+
visibility var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1)),
|
|
25
|
+
transform var(--ty-popup-duration, 180ms) var(--ty-popup-ease, cubic-bezier(0.34, 1.56, 0.64, 1));
|
|
26
26
|
|
|
27
27
|
/* Prevent interactions when hidden */
|
|
28
28
|
pointer-events: none;
|
|
@@ -34,9 +34,26 @@ export const popupStyles = `
|
|
|
34
34
|
left: var(--popup-x, 0px);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/*
|
|
37
|
+
/* Entrance prep (right before .open): scale + a short slide from the anchor
|
|
38
|
+
side. data-side is the RESOLVED side (post-flip), set by updatePosition. */
|
|
38
39
|
.popup-dialog.preparing-animation {
|
|
39
|
-
transform: scale(0.
|
|
40
|
+
transform: scale(0.96);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.popup-dialog[data-side="bottom"].preparing-animation:not(.open) {
|
|
44
|
+
transform: translateY(-6px) scale(0.96);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.popup-dialog[data-side="top"].preparing-animation:not(.open) {
|
|
48
|
+
transform: translateY(6px) scale(0.96);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.popup-dialog[data-side="left"].preparing-animation:not(.open) {
|
|
52
|
+
transform: translateX(6px) scale(0.96);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.popup-dialog[data-side="right"].preparing-animation:not(.open) {
|
|
56
|
+
transform: translateX(-6px) scale(0.96);
|
|
40
57
|
}
|
|
41
58
|
|
|
42
59
|
.popup-dialog.open {
|