tyrell-components 1.0.0-TC46 → 1.0.0-TC47
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 +93 -100
- package/dist/tyrell-theme.css +93 -100
- package/dist/tyrell.js +1 -1
- package/lib/components/date-picker.d.ts +8 -0
- package/lib/components/date-picker.js +11 -1
- package/lib/components/popup.js +2 -6
- package/lib/components/select.d.ts +11 -3
- package/lib/components/select.js +23 -4
- package/lib/components/tabs.js +3 -1
- package/lib/components/tooltip.js +2 -6
- package/lib/styles/button.js +5 -1
- 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 +3 -3
- 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/option.d.ts +1 -1
- package/lib/styles/option.js +1 -1
- package/lib/styles/radio.js +1 -1
- package/lib/styles/select-base.js +6 -4
- 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/positioning.d.ts +50 -5
- package/lib/utils/positioning.js +49 -5
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* (modal <dialog>), optional time input, form participation via
|
|
4
4
|
* ElementInternals. Value is UTC; display is local.
|
|
5
5
|
*/
|
|
6
|
+
import { type Placement } from '../utils/positioning.js';
|
|
6
7
|
import { TyComponent } from '../base/ty-component.js';
|
|
7
8
|
import type { Flavor } from '../types/common.js';
|
|
8
9
|
import type { PropertyChange } from '../utils/property-manager.js';
|
|
@@ -41,6 +42,11 @@ export interface DatePickerChangeDetail {
|
|
|
41
42
|
}
|
|
42
43
|
export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
43
44
|
protected static properties: {
|
|
45
|
+
placement: {
|
|
46
|
+
type: "string";
|
|
47
|
+
visual: boolean;
|
|
48
|
+
default: string;
|
|
49
|
+
};
|
|
44
50
|
size: {
|
|
45
51
|
type: "string";
|
|
46
52
|
visual: boolean;
|
|
@@ -169,6 +175,8 @@ export declare class TyDatePicker extends TyComponent<DatePickerState> {
|
|
|
169
175
|
private emitChangeEvent;
|
|
170
176
|
get size(): DatePickerSize;
|
|
171
177
|
set size(v: DatePickerSize);
|
|
178
|
+
get placement(): Placement | '';
|
|
179
|
+
set placement(v: Placement | '');
|
|
172
180
|
get flavor(): DatePickerFlavor;
|
|
173
181
|
set flavor(v: DatePickerFlavor);
|
|
174
182
|
get label(): string;
|
|
@@ -2,7 +2,7 @@ import { ensureStyles } from '../utils/styles.js';
|
|
|
2
2
|
import { syncCustomFlavorSheet } from '../utils/flavor-sheet.js';
|
|
3
3
|
import { datePickerStyles, datePickerCustomFlavorCss } from '../styles/date-picker.js';
|
|
4
4
|
import { lockScroll, unlockScroll } from '../utils/scroll-lock.js';
|
|
5
|
-
import { computeAnchoredPosition } from '../utils/positioning.js';
|
|
5
|
+
import { computeAnchoredPosition, placementToAnchored } from '../utils/positioning.js';
|
|
6
6
|
import { getEffectiveLocale, observeLocaleChanges } from '../utils/locale.js';
|
|
7
7
|
import { isMobileTouch } from '../utils/mobile.js';
|
|
8
8
|
import { TyComponent } from '../base/ty-component.js';
|
|
@@ -506,6 +506,8 @@ export class TyDatePicker extends TyComponent {
|
|
|
506
506
|
}
|
|
507
507
|
get size() { return this.getProperty('size'); }
|
|
508
508
|
set size(v) { this.setProperty('size', v); }
|
|
509
|
+
get placement() { return this.getProperty('placement'); }
|
|
510
|
+
set placement(v) { this.setProperty('placement', v); }
|
|
509
511
|
get flavor() { return this.getProperty('flavor'); }
|
|
510
512
|
set flavor(v) { this.setProperty('flavor', v); }
|
|
511
513
|
get label() { return this.getProperty('label'); }
|
|
@@ -755,11 +757,14 @@ export class TyDatePicker extends TyComponent {
|
|
|
755
757
|
const estimatedHeight = dialogRect.height || 400;
|
|
756
758
|
const popupWidth = dialogRect.width || 320;
|
|
757
759
|
const wrapPadding = 8;
|
|
760
|
+
const anchored = placementToAnchored(this.getProperty('placement'));
|
|
758
761
|
const pos = computeAnchoredPosition({
|
|
759
762
|
anchorRect: stubRect,
|
|
760
763
|
popupWidth,
|
|
761
764
|
popupHeight: estimatedHeight,
|
|
762
765
|
gap: 0,
|
|
766
|
+
align: anchored.align,
|
|
767
|
+
side: anchored.side,
|
|
763
768
|
});
|
|
764
769
|
const positionBelow = pos.below;
|
|
765
770
|
const x = pos.x - wrapPadding;
|
|
@@ -1047,6 +1052,11 @@ export class TyDatePicker extends TyComponent {
|
|
|
1047
1052
|
}
|
|
1048
1053
|
}
|
|
1049
1054
|
TyDatePicker.properties = {
|
|
1055
|
+
placement: {
|
|
1056
|
+
type: 'string',
|
|
1057
|
+
visual: true,
|
|
1058
|
+
default: '',
|
|
1059
|
+
},
|
|
1050
1060
|
size: {
|
|
1051
1061
|
type: 'string',
|
|
1052
1062
|
visual: true,
|
package/lib/components/popup.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findBestPosition } from '../utils/positioning.js';
|
|
1
|
+
import { findBestPosition, preferenceChain } from '../utils/positioning.js';
|
|
2
2
|
import { lockScroll, unlockScroll } from '../utils/scroll-lock.js';
|
|
3
3
|
import { ensureStyles } from '../utils/styles.js';
|
|
4
4
|
import { popupStyles } from '../styles/popup.js';
|
|
@@ -47,11 +47,7 @@ function updatePosition(el) {
|
|
|
47
47
|
const dialog = shadowRoot ? getPopupDialog(shadowRoot) : null;
|
|
48
48
|
if (!anchor || !dialog)
|
|
49
49
|
return;
|
|
50
|
-
const preferences = placement
|
|
51
|
-
placement === 'bottom' ? ['bottom', 'top', 'left', 'right'] :
|
|
52
|
-
placement === 'left' ? ['left', 'right', 'top', 'bottom'] :
|
|
53
|
-
placement === 'right' ? ['right', 'left', 'top', 'bottom'] :
|
|
54
|
-
['bottom', 'top', 'right', 'left'];
|
|
50
|
+
const preferences = preferenceChain(placement);
|
|
55
51
|
const positionData = findBestPosition({
|
|
56
52
|
targetEl: anchor,
|
|
57
53
|
floatingEl: dialog,
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Skins: default form field, `compact` content-hugging trigger, slot="trigger" custom.
|
|
6
6
|
*/
|
|
7
7
|
import type { Flavor, Size } from "../types/common.js";
|
|
8
|
+
import { type Placement } from "../utils/positioning.js";
|
|
8
9
|
import { TyComponent } from "../base/ty-component.js";
|
|
9
10
|
import type { PropertyChange } from "../utils/property-manager.js";
|
|
10
11
|
interface TagData {
|
|
@@ -116,7 +117,12 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
116
117
|
visual: boolean;
|
|
117
118
|
default: string;
|
|
118
119
|
validate: (v: any) => boolean;
|
|
119
|
-
coerce: (v: any) => "start" | "end";
|
|
120
|
+
coerce: (v: any) => "start" | "end" | "center";
|
|
121
|
+
};
|
|
122
|
+
placement: {
|
|
123
|
+
type: "string";
|
|
124
|
+
visual: boolean;
|
|
125
|
+
default: string;
|
|
120
126
|
};
|
|
121
127
|
size: {
|
|
122
128
|
type: "string";
|
|
@@ -402,8 +408,10 @@ export declare class TySelect extends TyComponent<SelectState> {
|
|
|
402
408
|
set debounce(value: number | string);
|
|
403
409
|
get size(): Size;
|
|
404
410
|
set size(value: Size);
|
|
405
|
-
get align(): "start" | "end";
|
|
406
|
-
set align(value: "start" | "end");
|
|
411
|
+
get align(): "start" | "center" | "end";
|
|
412
|
+
set align(value: "start" | "center" | "end");
|
|
413
|
+
get placement(): Placement | "";
|
|
414
|
+
set placement(value: Placement | "");
|
|
407
415
|
get flavor(): Flavor;
|
|
408
416
|
set flavor(value: Flavor);
|
|
409
417
|
get form(): HTMLFormElement | null;
|
package/lib/components/select.js
CHANGED
|
@@ -4,7 +4,7 @@ import { selectBaseStyles, selectCustomFlavorCss } from "../styles/select-base.j
|
|
|
4
4
|
import { selectStyles } from "../styles/select.js";
|
|
5
5
|
import { getLoaderSvg } from "../utils/loader-registry.js";
|
|
6
6
|
import { lockScroll, unlockScroll } from "../utils/scroll-lock.js";
|
|
7
|
-
import { computeAnchoredPosition } from "../utils/positioning.js";
|
|
7
|
+
import { computeAnchoredPosition, placementToAnchored } from "../utils/positioning.js";
|
|
8
8
|
import { isMobileTouch } from "../utils/mobile.js";
|
|
9
9
|
import { TyComponent } from "../base/ty-component.js";
|
|
10
10
|
import { CustomScrollbar, isCustomScrollbarEnabled, } from "../utils/custom-scrollbar.js";
|
|
@@ -366,11 +366,19 @@ export class TySelect extends TyComponent {
|
|
|
366
366
|
const popupWidth = !isNaN(cssWidth)
|
|
367
367
|
? cssWidth
|
|
368
368
|
: Math.max(stubRect.width, 320);
|
|
369
|
+
const requested = this.getProperty("placement");
|
|
370
|
+
const anchored = requested
|
|
371
|
+
? placementToAnchored(requested)
|
|
372
|
+
: {
|
|
373
|
+
side: "auto",
|
|
374
|
+
align: this.getProperty("align"),
|
|
375
|
+
};
|
|
369
376
|
const pos = computeAnchoredPosition({
|
|
370
377
|
anchorRect: stubRect,
|
|
371
378
|
popupWidth,
|
|
372
379
|
popupHeight: estimatedHeight,
|
|
373
|
-
align:
|
|
380
|
+
align: anchored.align,
|
|
381
|
+
side: anchored.side,
|
|
374
382
|
});
|
|
375
383
|
const positionBelow = pos.below;
|
|
376
384
|
const x = pos.x - wrapPadding;
|
|
@@ -1377,6 +1385,12 @@ export class TySelect extends TyComponent {
|
|
|
1377
1385
|
set align(value) {
|
|
1378
1386
|
this.setProperty("align", value);
|
|
1379
1387
|
}
|
|
1388
|
+
get placement() {
|
|
1389
|
+
return this.getProperty("placement");
|
|
1390
|
+
}
|
|
1391
|
+
set placement(value) {
|
|
1392
|
+
this.setProperty("placement", value);
|
|
1393
|
+
}
|
|
1380
1394
|
get flavor() {
|
|
1381
1395
|
return this.getProperty("flavor");
|
|
1382
1396
|
}
|
|
@@ -1484,8 +1498,13 @@ TySelect.properties = {
|
|
|
1484
1498
|
type: "string",
|
|
1485
1499
|
visual: true,
|
|
1486
1500
|
default: "start",
|
|
1487
|
-
validate: (v) => ["start", "end"].includes(v),
|
|
1488
|
-
coerce: (v) =>
|
|
1501
|
+
validate: (v) => ["start", "center", "end"].includes(v),
|
|
1502
|
+
coerce: (v) => v === "end" ? "end" : v === "center" ? "center" : "start",
|
|
1503
|
+
},
|
|
1504
|
+
placement: {
|
|
1505
|
+
type: "string",
|
|
1506
|
+
visual: true,
|
|
1507
|
+
default: "",
|
|
1489
1508
|
},
|
|
1490
1509
|
size: {
|
|
1491
1510
|
type: "string",
|
package/lib/components/tabs.js
CHANGED
|
@@ -231,7 +231,7 @@ function setupResizeObserver(el) {
|
|
|
231
231
|
const activeId = getActiveTabId(el, tabs);
|
|
232
232
|
const activeIndex = activeId ? findTabIndex(tabs, activeId) : 0;
|
|
233
233
|
if (width.includes('%')) {
|
|
234
|
-
el.style.setProperty('--tabs-width', `${entry.contentRect.width}px`);
|
|
234
|
+
el.style.setProperty('--tabs-page-width', `${entry.contentRect.width}px`);
|
|
235
235
|
}
|
|
236
236
|
if (activeIndex !== undefined) {
|
|
237
237
|
updateTransform(el, activeIndex);
|
|
@@ -370,6 +370,8 @@ function updateOverflow(el) {
|
|
|
370
370
|
if (buttons.length === 0)
|
|
371
371
|
return;
|
|
372
372
|
const available = container.clientWidth;
|
|
373
|
+
if (available <= 0)
|
|
374
|
+
return;
|
|
373
375
|
let total = 0;
|
|
374
376
|
let cutoff = buttons.length;
|
|
375
377
|
for (let i = 0; i < buttons.length; i++) {
|
|
@@ -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();
|
|
@@ -134,11 +134,7 @@ function updatePosition(el) {
|
|
|
134
134
|
console.warn('[ty-tooltip] updatePosition: missing anchor or popover');
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
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;
|
|
137
|
+
const preferences = preferenceChain(placement);
|
|
142
138
|
const position = findBestPosition({
|
|
143
139
|
targetEl: anchor,
|
|
144
140
|
floatingEl: popover,
|
package/lib/styles/button.js
CHANGED
|
@@ -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;
|
|
@@ -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);
|
|
@@ -341,7 +341,7 @@ ${FLAVORS.filter((f) => f !== "neutral").map((f) => datePickerFlavor(f)).join(""
|
|
|
341
341
|
border-radius: var(--ty-radius-sm);
|
|
342
342
|
background: var(--ty-input-bg);
|
|
343
343
|
color: var(--ty-calendar-time-input-color);
|
|
344
|
-
font-family:
|
|
344
|
+
font-family: inherit;
|
|
345
345
|
font-size: var(--ty-font-sm);
|
|
346
346
|
font-variant-numeric: tabular-nums;
|
|
347
347
|
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/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:host([cloned]) .option-content {\n padding: 0px;\n background: transparent;\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);
|
package/lib/styles/radio.js
CHANGED
|
@@ -31,7 +31,7 @@ export const selectBaseStyles = `
|
|
|
31
31
|
:host {
|
|
32
32
|
display: block;
|
|
33
33
|
width: 100%;
|
|
34
|
-
font-family:
|
|
34
|
+
font-family: inherit;
|
|
35
35
|
--mobile-border-color: var(--ty-border, #5858587d);
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -115,6 +115,7 @@ export const selectBaseStyles = `
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
.dropdown-search-input {
|
|
118
|
+
font-family: inherit;
|
|
118
119
|
width: 100%;
|
|
119
120
|
min-width: 0;
|
|
120
121
|
box-sizing: border-box;
|
|
@@ -122,7 +123,7 @@ export const selectBaseStyles = `
|
|
|
122
123
|
color: var(--input-color, var(--ty-input-color));
|
|
123
124
|
border: 1px solid var(--input-border, var(--ty-input-border));
|
|
124
125
|
border-radius: var(--ty-radius-md);
|
|
125
|
-
font-family:
|
|
126
|
+
font-family: inherit;
|
|
126
127
|
font-size: var(--ty-font-sm);
|
|
127
128
|
line-height: var(--ty-leading-sm);
|
|
128
129
|
letter-spacing: var(--ty-tracking-sm);
|
|
@@ -184,7 +185,7 @@ export const selectBaseStyles = `
|
|
|
184
185
|
color: var(--input-color, var(--ty-input-color));
|
|
185
186
|
border: 1px solid var(--select-accent, var(--input-border, var(--ty-input-border)));
|
|
186
187
|
border-radius: var(--ty-radius-md);
|
|
187
|
-
font-family:
|
|
188
|
+
font-family: inherit;
|
|
188
189
|
font-size: var(--ty-font-sm);
|
|
189
190
|
line-height: var(--ty-leading-sm);
|
|
190
191
|
letter-spacing: var(--ty-tracking-sm);
|
|
@@ -623,6 +624,7 @@ export const selectBaseStyles = `
|
|
|
623
624
|
}
|
|
624
625
|
|
|
625
626
|
.dropdown-mode-mobile .mobile-search-input {
|
|
627
|
+
font-family: inherit;
|
|
626
628
|
flex: 1;
|
|
627
629
|
min-width: 0;
|
|
628
630
|
box-sizing: border-box;
|
|
@@ -631,7 +633,7 @@ export const selectBaseStyles = `
|
|
|
631
633
|
border: 2px solid;
|
|
632
634
|
border-color: var(--mobile-border-color);
|
|
633
635
|
border-radius: var(--ty-radius-md);
|
|
634
|
-
font-family:
|
|
636
|
+
font-family: inherit;
|
|
635
637
|
font-size: var(--ty-font-sm);
|
|
636
638
|
line-height: var(--ty-leading-sm);
|
|
637
639
|
letter-spacing: var(--ty-tracking-sm);
|
package/lib/styles/tabs.d.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* Tabs Component Styles — ty-tabs container: top/bottom placement, animated
|
|
3
3
|
* active marker, carousel viewport. Customizable via CSS Parts (::part).
|
|
4
4
|
*/
|
|
5
|
-
export declare const tabsStyles = "\n:host {\n display: block;\n width: var(--tabs-width, 100%);\n height: var(--tabs-height, 400px);\n box-sizing: border-box;\n}\n\n.tabs-container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n}\n\n.tabs-container[data-placement=\"bottom\"] {\n flex-direction: column-reverse;\n}\n\n/* Tab buttons container \u2014 exposed as a CSS Part for full styling control */\n.tab-buttons {\n display: flex;\n gap: 0;\n flex-shrink: 0;\n position: relative;\n /* For absolute positioned marker */\n \n /* Default minimal styling - customize via ::part(buttons-container) */\n border-bottom: 1px solid var(--ty-border);\n background: transparent;\n}\n\n.tabs-container[data-placement=\"bottom\"] .tab-buttons {\n border-bottom: none;\n border-top: 1px solid var(--ty-border);\n}\n\n/* Marker wrapper \u2014 exposed as a CSS Part for custom marker styling */\n.marker-wrapper {\n position: absolute;\n z-index: 0;\n /* Behind buttons */\n pointer-events: none;\n /* Don't block clicks */\n transition: left var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n width var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n height var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n top var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);\n}\n\n.default-marker {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 2px;\n width: 100%;\n background: var(--ty-color-primary);\n pointer-events: none;\n}\n\n/* Hide default marker when custom marker is slotted */\n.marker-wrapper:has(::slotted([slot=\"marker\"])) .default-marker {\n display: none;\n}\n\n::slotted([slot=\"marker\"]) {\n display: block;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n}\n\n.tab-button {\n min-width: 120px;\n padding: 6px 12px;\n border: none;\n background: transparent;\n cursor: pointer;\n font: inherit;\n color: var(--ty-text-soft);\n transition: var(--ty-local-transition, color 200ms, background-color 200ms);\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-weight: var(--ty-font-bold);\n font-size: var(--ty-font-sm);\n position: relative;\n /* Establish stacking context */\n z-index: 10;\n /* Above marker */\n}\n\n.tab-button[aria-selected=true] {\n color: var(--ty-text-strong);\n}\n\n.tab-button[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.tab-button:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n.tab-button.overflow-hidden {\n display: none;\n}\n\n/* Overflow \"more\" trigger + menu */\n.tab-overflow-trigger {\n flex-shrink: 0;\n width: 40px;\n min-width: 40px;\n padding: 6px;\n border: none;\n background: transparent;\n cursor: pointer;\n color: var(--ty-text-soft);\n font-size: 18px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 10;\n}\n\n.tab-overflow-trigger:hover {\n color: var(--ty-text-strong);\n}\n\n.tab-overflow-trigger:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n.tab-overflow-menu {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n max-height: 300px;\n overflow-y: auto;\n padding: var(--ty-spacing-2, 4px);\n background: var(--ty-surface-floating);\n border-radius: var(--ty-radius-md);\n box-shadow: var(--ty-shadow-md, 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1));\n}\n\n.tab-overflow-item {\n padding: var(--ty-spacing-2, 6px) var(--ty-spacing-3, 12px);\n border: none;\n background: transparent;\n text-align: left;\n cursor: pointer;\n border-radius: var(--ty-radius-md);\n color: var(--ty-text);\n font: inherit;\n font-size: var(--ty-font-sm);\n white-space: nowrap;\n}\n\n.tab-overflow-item:hover {\n background: var(--ty-bg-neutral-soft);\n color: var(--ty-text-strong);\n}\n\n.tab-overflow-item[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Panels viewport \u2014 exposed as a CSS Part */\n.panels-viewport {\n position: relative;\n flex: 1;\n overflow: hidden;\n /* Critical: hides off-screen panels */\n min-height: 0;\n /* Allows flex child to shrink */\n}\n\n/* Panels wrapper slides horizontally */\n.panels-wrapper {\n display: flex;\n height: 100%;\n transition: transform var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panels-wrapper {\n transition-duration: 0ms !important;\n }\n\n .marker-wrapper {\n transition-duration: 0ms !important;\n }\n}\n\n::slotted(ty-tab) {\n width: var(--tabs-width, 100%);\n height: 100%;\n flex-shrink: 0;\n}\n";
|
|
5
|
+
export declare const tabsStyles = "\n:host {\n display: block;\n width: var(--tabs-width, 100%);\n height: var(--tabs-height, 400px);\n box-sizing: border-box;\n}\n\n.tabs-container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n}\n\n.tabs-container[data-placement=\"bottom\"] {\n flex-direction: column-reverse;\n}\n\n/* Tab buttons container \u2014 exposed as a CSS Part for full styling control */\n.tab-buttons {\n display: flex;\n gap: 0;\n flex-shrink: 0;\n position: relative;\n /* For absolute positioned marker */\n \n /* Default minimal styling - customize via ::part(buttons-container) */\n border-bottom: 1px solid var(--ty-border);\n background: transparent;\n}\n\n.tabs-container[data-placement=\"bottom\"] .tab-buttons {\n border-bottom: none;\n border-top: 1px solid var(--ty-border);\n}\n\n/* Marker wrapper \u2014 exposed as a CSS Part for custom marker styling */\n.marker-wrapper {\n position: absolute;\n z-index: 0;\n /* Behind buttons */\n pointer-events: none;\n /* Don't block clicks */\n transition: left var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n width var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n height var(--transition-duration, 300ms) var(--transition-easing, ease-in-out),\n top var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);\n}\n\n.default-marker {\n position: absolute;\n bottom: 0;\n left: 0;\n height: 2px;\n width: 100%;\n background: var(--ty-color-primary);\n pointer-events: none;\n}\n\n/* Hide default marker when custom marker is slotted */\n.marker-wrapper:has(::slotted([slot=\"marker\"])) .default-marker {\n display: none;\n}\n\n::slotted([slot=\"marker\"]) {\n display: block;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n}\n\n.tab-button {\n min-width: 120px;\n padding: 6px 12px;\n border: none;\n background: transparent;\n cursor: pointer;\n font: inherit;\n color: var(--ty-text-soft);\n transition: var(--ty-local-transition, color 200ms, background-color 200ms);\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-weight: var(--ty-font-bold);\n font-size: var(--ty-font-sm);\n position: relative;\n /* Establish stacking context */\n z-index: 10;\n /* Above marker */\n}\n\n.tab-button[aria-selected=true] {\n color: var(--ty-text-strong);\n}\n\n.tab-button[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.tab-button:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n.tab-button.overflow-hidden {\n display: none;\n}\n\n/* Overflow \"more\" trigger + menu */\n.tab-overflow-trigger {\n flex-shrink: 0;\n width: 40px;\n min-width: 40px;\n padding: 6px;\n border: none;\n background: transparent;\n cursor: pointer;\n color: var(--ty-text-soft);\n font-size: 18px;\n line-height: 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 10;\n}\n\n.tab-overflow-trigger:hover {\n color: var(--ty-text-strong);\n}\n\n.tab-overflow-trigger:focus-visible {\n outline: 2px solid var(--ty-color-primary);\n outline-offset: -2px;\n}\n\n.tab-overflow-menu {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n max-height: 300px;\n overflow-y: auto;\n padding: var(--ty-spacing-2, 4px);\n background: var(--ty-surface-floating);\n border-radius: var(--ty-radius-md);\n box-shadow: var(--ty-shadow-md, 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1));\n}\n\n.tab-overflow-item {\n padding: var(--ty-spacing-2, 6px) var(--ty-spacing-3, 12px);\n border: none;\n background: transparent;\n text-align: left;\n cursor: pointer;\n border-radius: var(--ty-radius-md);\n color: var(--ty-text);\n font: inherit;\n font-size: var(--ty-font-sm);\n white-space: nowrap;\n}\n\n.tab-overflow-item:hover {\n background: var(--ty-bg-neutral-soft);\n color: var(--ty-text-strong);\n}\n\n.tab-overflow-item[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* Panels viewport \u2014 exposed as a CSS Part */\n.panels-viewport {\n position: relative;\n flex: 1;\n overflow: hidden;\n /* Critical: hides off-screen panels */\n min-height: 0;\n /* Allows flex child to shrink */\n}\n\n/* Panels wrapper slides horizontally */\n.panels-wrapper {\n display: flex;\n height: 100%;\n transition: transform var(--transition-duration, 300ms) var(--transition-easing, ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panels-wrapper {\n transition-duration: 0ms !important;\n }\n\n .marker-wrapper {\n transition-duration: 0ms !important;\n }\n}\n\n::slotted(ty-tab) {\n /* Carousel page width. --tabs-page-width is the measured pixel width, set by\n the ResizeObserver for percentage-width tabs so every panel is exactly one\n page wide. It is deliberately NOT --tabs-width: that one sizes :host, and\n feeding a measured pixel value back into the host's own width is a\n self-referential loop (see setupResizeObserver). */\n width: var(--tabs-page-width, var(--tabs-width, 100%));\n height: 100%;\n flex-shrink: 0;\n}\n";
|
package/lib/styles/tabs.js
CHANGED
|
@@ -204,7 +204,12 @@ export const tabsStyles = `
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
::slotted(ty-tab) {
|
|
207
|
-
width
|
|
207
|
+
/* Carousel page width. --tabs-page-width is the measured pixel width, set by
|
|
208
|
+
the ResizeObserver for percentage-width tabs so every panel is exactly one
|
|
209
|
+
page wide. It is deliberately NOT --tabs-width: that one sizes :host, and
|
|
210
|
+
feeding a measured pixel value back into the host's own width is a
|
|
211
|
+
self-referential loop (see setupResizeObserver). */
|
|
212
|
+
width: var(--tabs-page-width, var(--tabs-width, 100%));
|
|
208
213
|
height: 100%;
|
|
209
214
|
flex-shrink: 0;
|
|
210
215
|
}
|
package/lib/styles/textarea.d.ts
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* Styles for ty-textarea component
|
|
3
3
|
* Enhanced textarea with auto-resize functionality
|
|
4
4
|
*/
|
|
5
|
-
export declare const textareaStyles = "\n:host {\n display: block;\n font-family:
|
|
5
|
+
export declare const textareaStyles = "\n:host {\n display: block;\n font-family: inherit;\n width: 100%;\n}\n\n.textarea-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n box-sizing: border-box;\n position: relative;\n /* For absolute positioned dummy element */\n}\n\n/* Bordered container \u2014 owns the border + active state (composer layout):\n header (top slot) \u00B7 textarea (borderless) \u00B7 footer (bottom slot). */\n.textarea-wrapper {\n position: relative;\n width: 100%;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n border: 1px solid var(--input-border, var(--ty-input-border));\n border-radius: var(--ty-radius-base);\n background: var(--input-bg, var(--ty-input-bg));\n transition: var(--ty-local-transition, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out);\n}\n\n/* Active ring tied to the TEXTAREA specifically (not :focus-within) so tabbing\n to a footer button doesn't make the whole field look focused. */\n.textarea-wrapper:has(textarea:focus) {\n border-color: var(--input-border-focus, var(--ty-input-border-focus));\n box-shadow: 0 0 0 3px var(--input-shadow-focus, var(--ty-input-shadow-focus));\n}\n\n/* Error + disabled reflected from the inner textarea (both in shadow DOM, so\n :has is reliable here \u2014 unlike slotted content). */\n.textarea-wrapper:has(textarea.error) {\n border-color: var(--ty-color-danger);\n background: var(--ty-bg-danger-soft);\n}\n.textarea-wrapper:has(textarea.error):has(textarea:focus) {\n border-color: var(--ty-color-danger-bold);\n}\n.textarea-wrapper:has(textarea:disabled) {\n opacity: 0.5;\n background: var(--input-disabled-bg, var(--ty-input-disabled-bg));\n border-color: var(--input-disabled-border, var(--ty-input-disabled-border));\n}\n\n/* Scroll region wraps just the textarea so the custom scrollbar never overlaps\n the header/footer. */\n.textarea-scroll {\n position: relative;\n flex: 1 1 auto;\n min-height: 0;\n display: flex;\n}\n\n/* Custom scrollbar track positioned to the scroll region (textarea edges) */\n.textarea-scroll .ty-scrollbar-track-y {\n top: 2px;\n right: 2px;\n bottom: 2px;\n border-radius: 0 4px 4px 0;\n}\n\n/* Header / footer slot regions \u2014 zero footprint until they have content\n (the component toggles .has-content via slotchange). footer defaults to\n space-between so \"tools \u2026 submit\" works; a single wide button fills it. */\n.textarea-header,\n.textarea-footer {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.textarea-footer { justify-content: space-between; }\n.textarea-header.has-content {\n padding: 6px 10px;\n border-bottom: 1px solid var(--ty-textarea-divider, var(--input-border, var(--ty-input-border)));\n}\n.textarea-footer.has-content {\n padding: 6px 10px;\n border-top: 1px solid var(--ty-textarea-divider, var(--input-border, var(--ty-input-border)));\n}\n\n.ty-field-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: 12px;\n}\n\n.required-icon {\n display: inline-flex;\n align-items: center;\n color: var(--ty-color-danger);\n width: 12px;\n height: 12px;\n vertical-align: middle;\n}\n\n.error-message {\n font-size: var(--ty-font-xs);\n line-height: var(--ty-leading-xs);\n letter-spacing: var(--ty-tracking-xs);\n color: var(--ty-color-danger);\n margin-top: 4px;\n padding-left: 12px;\n}\n\n/* Error state is reflected on .textarea-wrapper (see above). */\n\ntextarea {\n font-family: inherit;\n /* Borderless \u2014 the .textarea-wrapper owns the border/background now. */\n box-sizing: border-box;\n width: 100%;\n flex: 1 1 auto;\n border: none;\n background: transparent;\n color: var(--input-color, var(--ty-input-color));\n font-family: inherit;\n /* Linear-paired typography */\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-normal);\n outline: none;\n\n min-height: 80px;\n padding: 12px 12px;\n\n overflow: hidden;\n /* Hide scrollbars since we're auto-resizing */\n resize: none;\n\n white-space: pre-wrap;\n word-wrap: break-word;\n}\n\n/* Hide native scrollbar for webkit when custom scrollbar is active\n (scrollbar-width: none handles Firefox; this handles Chrome/Safari) */\n:host([data-custom-scroll]) textarea::-webkit-scrollbar {\n display: none;\n}\n\n/* Disabled \u2014 visual state lives on the wrapper; just the cursor/text here. */\ntextarea:disabled {\n cursor: not-allowed;\n color: var(--input-disabled-color, var(--ty-input-disabled-color));\n}\n\ntextarea::placeholder {\n color: var(--input-placeholder, var(--ty-input-placeholder));\n font-weight: 400;\n}\n\ntextarea.resize-both {\n resize: both;\n}\n\ntextarea.resize-horizontal {\n resize: horizontal;\n}\n\ntextarea.resize-vertical {\n resize: vertical;\n}\n\ntextarea.resize-none {\n resize: none;\n}\n\n/* Auto-resize mode (default) disables manual resize */\ntextarea:not(.resize-both):not(.resize-horizontal):not(.resize-vertical) {\n resize: none;\n}\n\n.textarea-dummy {\n /* Hidden element that measures text height */\n position: absolute !important;\n top: 0 !important;\n left: 0 !important;\n visibility: hidden !important;\n white-space: pre-wrap !important;\n word-break: break-word !important;\n box-sizing: border-box !important;\n overflow: hidden !important;\n pointer-events: none !important;\n z-index: -1 !important;\n\n word-wrap: break-word !important;\n overflow-wrap: break-word !important;\n}\n\ntextarea.xs {\n min-height: 64px;\n padding: 8px 10px;\n font-size: var(--ty-font-xs);\n line-height: var(--ty-leading-xs);\n letter-spacing: var(--ty-tracking-xs);\n}\n\ntextarea.sm {\n min-height: 72px;\n padding: 10px 10px;\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n letter-spacing: var(--ty-tracking-sm);\n}\n\ntextarea.md {\n min-height: 80px;\n padding: 12px 12px;\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n letter-spacing: var(--ty-tracking-sm);\n}\n\ntextarea.lg {\n min-height: 96px;\n padding: 14px 14px;\n font-size: var(--ty-font-base);\n line-height: var(--ty-leading-base);\n letter-spacing: var(--ty-tracking-base);\n}\n\ntextarea.xl {\n min-height: 112px;\n padding: 16px 16px;\n font-size: var(--ty-font-lg);\n line-height: var(--ty-leading-lg);\n letter-spacing: var(--ty-tracking-lg);\n}\n\ntextarea:focus-visible {\n outline: none;\n /* We use box-shadow instead */\n}\n\n@media (prefers-contrast: high) {\n textarea {\n border-width: 2px;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n textarea {\n transition: none;\n }\n\n .textarea-dummy {\n transition: none;\n }\n}\n\n@container (max-width: 480px) {\n textarea.lg {\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n letter-spacing: var(--ty-tracking-sm);\n padding: 12px 12px;\n min-height: 80px;\n }\n\n textarea.xl {\n font-size: var(--ty-font-base);\n line-height: var(--ty-leading-base);\n letter-spacing: var(--ty-tracking-base);\n padding: 14px 14px;\n min-height: 96px;\n }\n}\n\n@container (max-width: 320px) {\n textarea.xl {\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n letter-spacing: var(--ty-tracking-sm);\n padding: 12px 12px;\n min-height: 80px;\n }\n}\n\n/* Fallback for browsers without container query support */\n@media (max-width: 640px) {\n textarea.lg {\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n letter-spacing: var(--ty-tracking-sm);\n padding: 12px 12px;\n min-height: 80px;\n }\n\n textarea.xl {\n font-size: var(--ty-font-base);\n line-height: var(--ty-leading-base);\n letter-spacing: var(--ty-tracking-base);\n padding: 14px 14px;\n min-height: 96px;\n }\n}\n\n@media (max-width: 480px) {\n textarea.xl {\n font-size: var(--ty-font-sm);\n line-height: var(--ty-leading-sm);\n letter-spacing: var(--ty-tracking-sm);\n padding: 12px 12px;\n min-height: 80px;\n }\n}\n\ntextarea {\n transition: var(--ty-local-transition,\n border-color 0.15s ease-in-out,\n box-shadow 0.15s ease-in-out,\n background-color 0.15s ease-in-out,\n height 0.1s ease-out);\n}\n\n/* Disable height transition on focus to avoid jarring effect */\ntextarea:focus {\n transition: var(--ty-local-transition,\n border-color 0.15s ease-in-out,\n box-shadow 0.15s ease-in-out,\n background-color 0.15s ease-in-out);\n}\n\n@media (prefers-reduced-motion: reduce) {\n textarea {\n transition: none;\n }\n}\n";
|
package/lib/styles/textarea.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const textareaStyles = `
|
|
2
2
|
:host {
|
|
3
3
|
display: block;
|
|
4
|
-
font-family:
|
|
4
|
+
font-family: inherit;
|
|
5
5
|
width: 100%;
|
|
6
6
|
}
|
|
7
7
|
|
|
@@ -117,6 +117,7 @@ export const textareaStyles = `
|
|
|
117
117
|
/* Error state is reflected on .textarea-wrapper (see above). */
|
|
118
118
|
|
|
119
119
|
textarea {
|
|
120
|
+
font-family: inherit;
|
|
120
121
|
/* Borderless — the .textarea-wrapper owns the border/background now. */
|
|
121
122
|
box-sizing: border-box;
|
|
122
123
|
width: 100%;
|