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
package/lib/styles/wizard.d.ts
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
* circles read the matching accent var directly (no `-bg` indirection), and
|
|
7
7
|
* the brand-layer flavor seeds cascade through for per-shade control.
|
|
8
8
|
*/
|
|
9
|
-
export declare const wizardStyles = "\n/* Theming tokens \u2014 every default chains back to the brand layer / global\n scale tokens. */\n\n:host {\n display: block;\n width: var(--ty-wizard-width, 100%); /* set by width attribute \u2014 not a public token */\n height: var(--ty-wizard-height, 700px); /* set by height attribute \u2014 not a public token */\n box-sizing: border-box;\n\n /* State accents \u2014 the primary override surface. One variable retints the\n * matching circle background, border, and glow. */\n --ty-wizard-active-accent: var(--ty-color-primary);\n --ty-wizard-completed-accent: var(--ty-color-success);\n --ty-wizard-error-accent: var(--ty-color-danger);\n --ty-wizard-pending-accent: var(--ty-color-neutral);\n\n /* Container chrome \u2014 routes through global scales. Override --ty-radius-lg\n * or --ty-shadow-md app-wide and the wizard follows; override these locally\n * to give the wizard a distinct look. */\n --ty-wizard-bg: var(--ty-surface-floating);\n --ty-wizard-border: var(--ty-border);\n --ty-wizard-radius: var(--ty-radius-lg, 12px);\n --ty-wizard-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 --ty-wizard-header-bg: var(--ty-surface-content);\n --ty-wizard-header-border: var(--ty-border-soft, var(--ty-border));\n --ty-wizard-header-padding: 24px 24px 16px;\n\n --ty-wizard-progress-track: var(--ty-border);\n --ty-wizard-progress-fill: var(--ty-wizard-completed-accent);\n --ty-wizard-progress-height: 2px;\n\n /* Motion \u2014 routes through global motion tokens if defined. */\n --ty-wizard-transition-duration: var(--ty-transition-duration, 300ms);\n --ty-wizard-transition-easing: var(--ty-transition-easing, ease-in-out);\n\n --ty-wizard-circle-size: 32px;\n --ty-wizard-circle-border-width: 2px;\n\n --ty-wizard-panels-bg: var(--ty-surface-elevated);\n\n --ty-wizard-focus-outline: var(--ty-wizard-active-accent);\n}\n\n.wizard-container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n border-radius: var(--ty-wizard-radius);\n border: 1px solid var(--ty-wizard-border);\n background: var(--ty-wizard-bg);\n box-shadow: var(--ty-wizard-shadow);\n overflow: hidden;\n}\n\n.step-indicators-wrapper {\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n position: relative;\n padding: var(--ty-wizard-header-padding);\n border-bottom: 1px solid var(--ty-wizard-header-border);\n background: var(--ty-wizard-header-bg);\n}\n\n/* Progress line, behind the step circles */\n.progress-line {\n position: absolute;\n /*\n * With equal-width indicators (flex: 1), each takes 100%/N of the width.\n * Circle centers sit at 50%/N from the left edge of each indicator.\n * Inset = 50% / step-count from each side keeps the line between circle centres.\n */\n left: calc(50% / var(--ty-wizard-step-count, 4));\n right: calc(50% / var(--ty-wizard-step-count, 4));\n top: calc(var(--ty-wizard-circle-size, 32px) / 2 - 1px);\n height: var(--ty-wizard-progress-height);\n background: var(--ty-wizard-progress-track);\n z-index: 0;\n pointer-events: none;\n transition: opacity var(--ty-wizard-transition-duration) var(--ty-wizard-transition-easing);\n}\n\n.progress-overlay {\n position: absolute;\n left: 0;\n top: 0;\n height: 100%;\n background: var(--ty-wizard-progress-fill);\n transition: width var(--ty-wizard-transition-duration) var(--ty-wizard-transition-easing);\n will-change: width;\n}\n\n.step-indicators {\n display: flex;\n align-items: flex-start;\n position: relative;\n}\n\n.step-indicator {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n background: transparent;\n border: none;\n padding: 0;\n font: inherit;\n transition: opacity 200ms;\n flex: 1;\n min-width: 0;\n}\n\n.step-indicator[aria-disabled=\"true\"] {\n opacity: 0.4;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.step-indicator:focus-visible {\n outline: 2px solid var(--ty-wizard-focus-outline);\n outline-offset: 4px;\n border-radius: 50%;\n}\n\n.step-circle {\n width: var(--ty-wizard-circle-size);\n height: var(--ty-wizard-circle-size);\n border-radius: 50%;\n border: var(--ty-wizard-circle-border-width) solid;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 10;\n transition: var(--ty-local-transition, all var(--ty-wizard-transition-duration) var(--ty-wizard-transition-easing));\n box-sizing: border-box;\n flex-shrink: 0;\n}\n\n.step-circle[data-state=\"completed\"] {\n background: var(--ty-wizard-completed-accent);\n border-color: var(--ty-color-success-strong);\n /* Derived from the accent this circle actually paints, not from\n --ty-solid-success-fg: the two diverge in dark mode (--ty-solid-l\n dims solid fills by 0.25 L), which stranded white text at 3.5:1. */\n color: oklch(\n from var(--ty-wizard-completed-accent)\n clamp(0, (var(--ty-solid-fg-threshold, 0.
|
|
9
|
+
export declare const wizardStyles = "\n/* Theming tokens \u2014 every default chains back to the brand layer / global\n scale tokens. */\n\n:host {\n display: block;\n width: var(--ty-wizard-width, 100%); /* set by width attribute \u2014 not a public token */\n height: var(--ty-wizard-height, 700px); /* set by height attribute \u2014 not a public token */\n box-sizing: border-box;\n\n /* State accents \u2014 the primary override surface. One variable retints the\n * matching circle background, border, and glow. */\n --ty-wizard-active-accent: var(--ty-color-primary);\n --ty-wizard-completed-accent: var(--ty-color-success);\n --ty-wizard-error-accent: var(--ty-color-danger);\n --ty-wizard-pending-accent: var(--ty-color-neutral);\n\n /* Container chrome \u2014 routes through global scales. Override --ty-radius-lg\n * or --ty-shadow-md app-wide and the wizard follows; override these locally\n * to give the wizard a distinct look. */\n --ty-wizard-bg: var(--ty-surface-floating);\n --ty-wizard-border: var(--ty-border);\n --ty-wizard-radius: var(--ty-radius-lg, 12px);\n --ty-wizard-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 --ty-wizard-header-bg: var(--ty-surface-content);\n --ty-wizard-header-border: var(--ty-border-soft, var(--ty-border));\n --ty-wizard-header-padding: 24px 24px 16px;\n\n --ty-wizard-progress-track: var(--ty-border);\n --ty-wizard-progress-fill: var(--ty-wizard-completed-accent);\n --ty-wizard-progress-height: 2px;\n\n /* Motion \u2014 routes through global motion tokens if defined. */\n --ty-wizard-transition-duration: var(--ty-transition-duration, 300ms);\n --ty-wizard-transition-easing: var(--ty-transition-easing, ease-in-out);\n\n --ty-wizard-circle-size: 32px;\n --ty-wizard-circle-border-width: 2px;\n\n --ty-wizard-panels-bg: var(--ty-surface-elevated);\n\n --ty-wizard-focus-outline: var(--ty-wizard-active-accent);\n}\n\n.wizard-container {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n position: relative;\n box-sizing: border-box;\n border-radius: var(--ty-wizard-radius);\n border: 1px solid var(--ty-wizard-border);\n background: var(--ty-wizard-bg);\n box-shadow: var(--ty-wizard-shadow);\n overflow: hidden;\n}\n\n.step-indicators-wrapper {\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n position: relative;\n padding: var(--ty-wizard-header-padding);\n border-bottom: 1px solid var(--ty-wizard-header-border);\n background: var(--ty-wizard-header-bg);\n}\n\n/* Progress line, behind the step circles */\n.progress-line {\n position: absolute;\n /*\n * With equal-width indicators (flex: 1), each takes 100%/N of the width.\n * Circle centers sit at 50%/N from the left edge of each indicator.\n * Inset = 50% / step-count from each side keeps the line between circle centres.\n */\n left: calc(50% / var(--ty-wizard-step-count, 4));\n right: calc(50% / var(--ty-wizard-step-count, 4));\n top: calc(var(--ty-wizard-circle-size, 32px) / 2 - 1px);\n height: var(--ty-wizard-progress-height);\n background: var(--ty-wizard-progress-track);\n z-index: 0;\n pointer-events: none;\n transition: opacity var(--ty-wizard-transition-duration) var(--ty-wizard-transition-easing);\n}\n\n.progress-overlay {\n position: absolute;\n left: 0;\n top: 0;\n height: 100%;\n background: var(--ty-wizard-progress-fill);\n transition: width var(--ty-wizard-transition-duration) var(--ty-wizard-transition-easing);\n will-change: width;\n}\n\n.step-indicators {\n display: flex;\n align-items: flex-start;\n position: relative;\n}\n\n.step-indicator {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n background: transparent;\n border: none;\n padding: 0;\n font: inherit;\n transition: opacity 200ms;\n flex: 1;\n min-width: 0;\n}\n\n.step-indicator[aria-disabled=\"true\"] {\n opacity: 0.4;\n cursor: not-allowed;\n pointer-events: none;\n}\n\n.step-indicator:focus-visible {\n outline: 2px solid var(--ty-wizard-focus-outline);\n outline-offset: 4px;\n border-radius: 50%;\n}\n\n.step-circle {\n width: var(--ty-wizard-circle-size);\n height: var(--ty-wizard-circle-size);\n border-radius: 50%;\n border: var(--ty-wizard-circle-border-width) solid;\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n z-index: 10;\n transition: var(--ty-local-transition, all var(--ty-wizard-transition-duration) var(--ty-wizard-transition-easing));\n box-sizing: border-box;\n flex-shrink: 0;\n}\n\n.step-circle[data-state=\"completed\"] {\n background: var(--ty-wizard-completed-accent);\n border-color: var(--ty-color-success-strong);\n /* Derived from the accent this circle actually paints, not from\n --ty-solid-success-fg: the two diverge in dark mode (--ty-solid-l\n dims solid fills by 0.25 L), which stranded white text at 3.5:1. */\n color: oklch(\n from var(--ty-wizard-completed-accent)\n clamp(0, (var(--ty-solid-fg-threshold, 0.57) - l) * 1000, 1) 0 0\n );\n box-shadow: 0 0 0 4px color-mix(in oklab, var(--ty-wizard-completed-accent) 12%, transparent);\n}\n\n.step-circle[data-state=\"active\"] {\n background: var(--ty-wizard-active-accent);\n border-color: var(--ty-color-primary-strong);\n color: oklch(\n from var(--ty-wizard-active-accent)\n clamp(0, (var(--ty-solid-fg-threshold, 0.57) - l) * 1000, 1) 0 0\n );\n box-shadow: 0 0 0 4px color-mix(in oklab, var(--ty-wizard-active-accent) 12%, transparent);\n}\n\n.step-circle[data-state=\"pending\"] {\n background: var(--ty-surface-elevated);\n border-color: var(--ty-border);\n color: var(--ty-color-neutral);\n}\n\n.step-circle[data-state=\"error\"] {\n background: var(--ty-wizard-error-accent);\n border-color: var(--ty-color-danger-strong);\n color: oklch(\n from var(--ty-wizard-error-accent)\n clamp(0, (var(--ty-solid-fg-threshold, 0.57) - l) * 1000, 1) 0 0\n );\n box-shadow: 0 0 0 4px color-mix(in oklab, var(--ty-wizard-error-accent) 12%, transparent);\n}\n\n.step-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n align-items: center;\n}\n\n.step-label {\n font-size: var(--ty-font-sm, 14px);\n font-weight: var(--ty-font-semibold, 600);\n color: var(--ty-color-neutral);\n transition: var(--ty-local-transition, color 200ms);\n}\n\n.step-indicator[aria-selected=\"true\"] .step-label {\n color: var(--ty-color-neutral-strong);\n}\n\n.step-indicator[aria-selected=\"false\"] .step-label {\n color: var(--ty-color-neutral);\n}\n\n.step-description {\n font-size: var(--ty-font-xs, 12px);\n font-weight: var(--ty-font-normal, 400);\n color: var(--ty-color-neutral);\n transition: var(--ty-local-transition, color 200ms);\n text-align: center;\n max-width: 120px;\n}\n\n.step-indicator[aria-selected=\"true\"] .step-description {\n color: var(--ty-color-neutral-bold);\n}\n\n/* Custom indicator content via slots */\n::slotted([slot^=\"indicator-\"]) {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n\n.panels-viewport {\n position: relative;\n flex: 1;\n overflow: hidden;\n min-height: 0;\n background: var(--ty-wizard-panels-bg);\n}\n\n/* Panels wrapper slides horizontally */\n.panels-wrapper {\n display: flex;\n height: 100%;\n transition: transform var(--ty-wizard-transition-duration) var(--ty-wizard-transition-easing);\n will-change: transform;\n}\n\n::slotted(ty-step) {\n width: var(--ty-wizard-width, 100%);\n height: 100%;\n flex-shrink: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .panels-wrapper {\n transition-duration: 0ms !important;\n }\n\n .progress-overlay {\n transition-duration: 0ms !important;\n }\n\n .step-circle {\n transition-duration: 0ms !important;\n }\n\n .progress-line {\n transition-duration: 0ms !important;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n}\n";
|
package/lib/styles/wizard.js
CHANGED
|
@@ -151,7 +151,7 @@ export const wizardStyles = `
|
|
|
151
151
|
dims solid fills by 0.25 L), which stranded white text at 3.5:1. */
|
|
152
152
|
color: oklch(
|
|
153
153
|
from var(--ty-wizard-completed-accent)
|
|
154
|
-
clamp(0, (var(--ty-solid-fg-threshold, 0.
|
|
154
|
+
clamp(0, (var(--ty-solid-fg-threshold, 0.57) - l) * 1000, 1) 0 0
|
|
155
155
|
);
|
|
156
156
|
box-shadow: 0 0 0 4px color-mix(in oklab, var(--ty-wizard-completed-accent) 12%, transparent);
|
|
157
157
|
}
|
|
@@ -161,7 +161,7 @@ export const wizardStyles = `
|
|
|
161
161
|
border-color: var(--ty-color-primary-strong);
|
|
162
162
|
color: oklch(
|
|
163
163
|
from var(--ty-wizard-active-accent)
|
|
164
|
-
clamp(0, (var(--ty-solid-fg-threshold, 0.
|
|
164
|
+
clamp(0, (var(--ty-solid-fg-threshold, 0.57) - l) * 1000, 1) 0 0
|
|
165
165
|
);
|
|
166
166
|
box-shadow: 0 0 0 4px color-mix(in oklab, var(--ty-wizard-active-accent) 12%, transparent);
|
|
167
167
|
}
|
|
@@ -177,7 +177,7 @@ export const wizardStyles = `
|
|
|
177
177
|
border-color: var(--ty-color-danger-strong);
|
|
178
178
|
color: oklch(
|
|
179
179
|
from var(--ty-wizard-error-accent)
|
|
180
|
-
clamp(0, (var(--ty-solid-fg-threshold, 0.
|
|
180
|
+
clamp(0, (var(--ty-solid-fg-threshold, 0.57) - l) * 1000, 1) 0 0
|
|
181
181
|
);
|
|
182
182
|
box-shadow: 0 0 0 4px color-mix(in oklab, var(--ty-wizard-error-accent) 12%, transparent);
|
|
183
183
|
}
|
|
@@ -12,7 +12,7 @@ export type PlacementOrientation = 'horizontal' | 'vertical';
|
|
|
12
12
|
/**
|
|
13
13
|
* Vertical alignment options
|
|
14
14
|
*/
|
|
15
|
-
export type VerticalAlign = 'top' | 'center' | 'bottom' | 'end';
|
|
15
|
+
export type VerticalAlign = 'top' | 'start' | 'center' | 'bottom' | 'end';
|
|
16
16
|
/**
|
|
17
17
|
* Horizontal alignment options
|
|
18
18
|
*/
|
|
@@ -110,6 +110,47 @@ export declare const placementPreferences: {
|
|
|
110
110
|
tooltip: Placement[];
|
|
111
111
|
dropdown: Placement[];
|
|
112
112
|
};
|
|
113
|
+
/** The four sides a floating element can sit on. */
|
|
114
|
+
export type PlacementSide = 'top' | 'right' | 'bottom' | 'left';
|
|
115
|
+
/** Cross-axis alignment against the anchor. Omitted means centred. */
|
|
116
|
+
export type PlacementAlign = 'start' | 'end';
|
|
117
|
+
/** Split `'left-end'` into `{ side: 'left', align: 'end' }`. */
|
|
118
|
+
export declare function parsePlacement(placement: Placement): {
|
|
119
|
+
side: PlacementSide;
|
|
120
|
+
align?: PlacementAlign;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Fallback order to try for a requested placement, best-fit first.
|
|
124
|
+
*
|
|
125
|
+
* FLIP BEFORE RE-ALIGN. When a placement overflows it is almost always the
|
|
126
|
+
* SIDE axis that ran out of room, and re-aligning on the same side does not
|
|
127
|
+
* change the fit at all: `bottom-start` and `bottom-end` need identical
|
|
128
|
+
* vertical space. Flipping to the opposite side is the move that actually
|
|
129
|
+
* helps, so the requested alignment is carried across the flip first, and only
|
|
130
|
+
* then do we try other alignments.
|
|
131
|
+
*
|
|
132
|
+
* This also keeps the old hand-written chains intact for the four bare sides —
|
|
133
|
+
* `top` still degrades to `bottom` as its very next candidate, exactly as
|
|
134
|
+
* before aligned placements existed.
|
|
135
|
+
*
|
|
136
|
+
* Callers previously hand-wrote a 4-entry chain per side, which silently
|
|
137
|
+
* dropped every aligned placement onto the default chain.
|
|
138
|
+
*/
|
|
139
|
+
export declare function preferenceChain(placement: Placement): Placement[];
|
|
140
|
+
/**
|
|
141
|
+
* Map a `Placement` onto `computeAnchoredPosition`'s inputs, so dropdown-style
|
|
142
|
+
* components (ty-select, ty-date-picker) accept the same `placement` vocabulary
|
|
143
|
+
* as ty-popup / ty-tooltip.
|
|
144
|
+
*
|
|
145
|
+
* Dropdowns only ever live above or below their trigger, so a left/right
|
|
146
|
+
* placement degrades to "auto" side while keeping its alignment. Bare sides
|
|
147
|
+
* default to start-aligned, matching a native <select> and the behaviour these
|
|
148
|
+
* components had before `placement` existed.
|
|
149
|
+
*/
|
|
150
|
+
export declare function placementToAnchored(placement?: Placement | '' | null): {
|
|
151
|
+
side: 'top' | 'bottom' | 'auto';
|
|
152
|
+
align: 'start' | 'center' | 'end';
|
|
153
|
+
};
|
|
113
154
|
/**
|
|
114
155
|
* Find the best position for the floating element
|
|
115
156
|
* Tries all preference placements and returns the one that fits best
|
|
@@ -131,10 +172,14 @@ export interface AnchoredPopupOptions {
|
|
|
131
172
|
gap?: number;
|
|
132
173
|
/** Minimum distance from viewport edges (px, default 8) */
|
|
133
174
|
padding?: number;
|
|
134
|
-
/** Horizontal anchor edge: trigger's left edge ("start", default)
|
|
135
|
-
* right edge ("end"). Either way the result is clamped
|
|
136
|
-
* viewport, same as before. */
|
|
137
|
-
align?: "start" | "end";
|
|
175
|
+
/** Horizontal anchor edge: trigger's left edge ("start", default), its
|
|
176
|
+
* right edge ("end"), or centred on it. Either way the result is clamped
|
|
177
|
+
* into the viewport, same as before. */
|
|
178
|
+
align?: "start" | "center" | "end";
|
|
179
|
+
/** Preferred vertical side. "auto" (default) keeps the historic behaviour:
|
|
180
|
+
* below when it fits, otherwise whichever side has more room. Naming a
|
|
181
|
+
* side honours it when it fits and still flips rather than clipping. */
|
|
182
|
+
side?: "top" | "bottom" | "auto";
|
|
138
183
|
}
|
|
139
184
|
export interface AnchoredPopupPosition {
|
|
140
185
|
/** Popup left edge, viewport coords, clamped into view */
|
package/lib/utils/positioning.js
CHANGED
|
@@ -12,7 +12,7 @@ export const placements = {
|
|
|
12
12
|
horizontal: 'end',
|
|
13
13
|
},
|
|
14
14
|
'right-start': {
|
|
15
|
-
vertical: '
|
|
15
|
+
vertical: 'start',
|
|
16
16
|
horizontal: 'end',
|
|
17
17
|
orientation: 'vertical',
|
|
18
18
|
},
|
|
@@ -39,7 +39,7 @@ export const placements = {
|
|
|
39
39
|
horizontal: 'end',
|
|
40
40
|
},
|
|
41
41
|
'left-start': {
|
|
42
|
-
vertical: '
|
|
42
|
+
vertical: 'start',
|
|
43
43
|
horizontal: 'start',
|
|
44
44
|
orientation: 'vertical',
|
|
45
45
|
},
|
|
@@ -68,6 +68,41 @@ export const placementPreferences = {
|
|
|
68
68
|
'bottom', 'top', 'right', 'left'
|
|
69
69
|
],
|
|
70
70
|
};
|
|
71
|
+
const OPPOSITE_SIDE = {
|
|
72
|
+
top: 'bottom',
|
|
73
|
+
bottom: 'top',
|
|
74
|
+
left: 'right',
|
|
75
|
+
right: 'left',
|
|
76
|
+
};
|
|
77
|
+
export function parsePlacement(placement) {
|
|
78
|
+
const [side, align] = placement.split('-');
|
|
79
|
+
return { side, align };
|
|
80
|
+
}
|
|
81
|
+
export function preferenceChain(placement) {
|
|
82
|
+
const { side, align } = parsePlacement(placement);
|
|
83
|
+
const opposite = OPPOSITE_SIDE[side];
|
|
84
|
+
const perpendicular = side === 'top' || side === 'bottom' ? ['right', 'left'] : ['bottom', 'top'];
|
|
85
|
+
const exact = (s) => (align ? `${s}-${align}` : s);
|
|
86
|
+
const rest = (s) => (align
|
|
87
|
+
? [s, `${s}-${align === 'start' ? 'end' : 'start'}`]
|
|
88
|
+
: [`${s}-start`, `${s}-end`]);
|
|
89
|
+
return [
|
|
90
|
+
exact(side),
|
|
91
|
+
exact(opposite),
|
|
92
|
+
...rest(side),
|
|
93
|
+
...rest(opposite),
|
|
94
|
+
...perpendicular.flatMap((s) => [exact(s), ...rest(s)]),
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
export function placementToAnchored(placement) {
|
|
98
|
+
if (!placement)
|
|
99
|
+
return { side: 'auto', align: 'start' };
|
|
100
|
+
const { side, align } = parsePlacement(placement);
|
|
101
|
+
return {
|
|
102
|
+
side: side === 'top' || side === 'bottom' ? side : 'auto',
|
|
103
|
+
align: align ?? 'start',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
71
106
|
function getElementRect(el) {
|
|
72
107
|
const rect = el.getBoundingClientRect();
|
|
73
108
|
return {
|
|
@@ -123,7 +158,7 @@ function calculatePlacement(options) {
|
|
|
123
158
|
y = targetRect.bottom - floatingRect.height - containerPadding;
|
|
124
159
|
}
|
|
125
160
|
else {
|
|
126
|
-
y = targetRect.top;
|
|
161
|
+
y = targetRect.top + containerPadding;
|
|
127
162
|
}
|
|
128
163
|
}
|
|
129
164
|
else {
|
|
@@ -178,8 +213,17 @@ export function computeAnchoredPosition(o) {
|
|
|
178
213
|
const vh = window.innerHeight;
|
|
179
214
|
const spaceBelow = vh - o.anchorRect.bottom;
|
|
180
215
|
const spaceAbove = o.anchorRect.top;
|
|
181
|
-
const
|
|
182
|
-
const
|
|
216
|
+
const needed = o.popupHeight + gap + padding;
|
|
217
|
+
const fitsBelow = spaceBelow >= needed;
|
|
218
|
+
const fitsAbove = spaceAbove >= needed;
|
|
219
|
+
const side = o.side ?? "auto";
|
|
220
|
+
const below = side === "bottom" ? (fitsBelow || !fitsAbove ? true : false)
|
|
221
|
+
: side === "top" ? (fitsAbove || !fitsBelow ? false : true)
|
|
222
|
+
: fitsBelow || spaceBelow >= spaceAbove;
|
|
223
|
+
const rawX = o.align === "end" ? o.anchorRect.right - o.popupWidth
|
|
224
|
+
: o.align === "center"
|
|
225
|
+
? o.anchorRect.left + (o.anchorRect.width - o.popupWidth) / 2
|
|
226
|
+
: o.anchorRect.left;
|
|
183
227
|
const x = Math.max(padding, Math.min(rawX, vw - o.popupWidth - padding));
|
|
184
228
|
return {
|
|
185
229
|
x,
|
package/lib/version.d.ts
CHANGED
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '1.0.0-
|
|
1
|
+
export const VERSION = '1.0.0-TC47';
|