@vielzeug/refine 3.2.1 → 3.3.1
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/dist/alert.cjs +3 -3
- package/dist/alert.cjs.map +1 -1
- package/dist/alert.js +11 -4
- package/dist/alert.js.map +1 -1
- package/dist/chip.cjs +9 -9
- package/dist/chip.cjs.map +1 -1
- package/dist/chip.js +38 -35
- package/dist/chip.js.map +1 -1
- package/dist/command-palette.cjs +5 -5
- package/dist/command-palette.cjs.map +1 -1
- package/dist/command-palette.js +5 -5
- package/dist/command-palette.js.map +1 -1
- package/dist/content/card/card.css.cjs +1 -1
- package/dist/content/card/card.css.cjs.map +1 -1
- package/dist/content/card/card.css.js +1 -1
- package/dist/content/card/card.css.js.map +1 -1
- package/dist/content/index.d.ts +4 -0
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/keyboard-key/keyboard-key.css.cjs +2 -0
- package/dist/content/keyboard-key/keyboard-key.css.cjs.map +1 -0
- package/dist/content/keyboard-key/keyboard-key.css.js +6 -0
- package/dist/content/keyboard-key/keyboard-key.css.js.map +1 -0
- package/dist/content/keyboard-key/keyboard-key.d.ts +12 -0
- package/dist/content/keyboard-key/keyboard-key.d.ts.map +1 -0
- package/dist/content/keyboard-key/keyboard-shortcut.css.cjs +2 -0
- package/dist/content/keyboard-key/keyboard-shortcut.css.cjs.map +1 -0
- package/dist/content/keyboard-key/keyboard-shortcut.css.js +6 -0
- package/dist/content/keyboard-key/keyboard-shortcut.css.js.map +1 -0
- package/dist/content/qr-code/qr-code.css.cjs +2 -0
- package/dist/content/qr-code/qr-code.css.cjs.map +1 -0
- package/dist/content/qr-code/qr-code.css.js +6 -0
- package/dist/content/qr-code/qr-code.css.js.map +1 -0
- package/dist/content/qr-code/qr-code.d.ts +74 -0
- package/dist/content/qr-code/qr-code.d.ts.map +1 -0
- package/dist/core/positioner.cjs +1 -1
- package/dist/core/positioner.cjs.map +1 -1
- package/dist/core/positioner.d.ts +4 -1
- package/dist/core/positioner.d.ts.map +1 -1
- package/dist/core/positioner.js +10 -5
- package/dist/core/positioner.js.map +1 -1
- package/dist/counter.cjs +91 -0
- package/dist/counter.cjs.map +1 -0
- package/dist/counter.js +180 -0
- package/dist/counter.js.map +1 -0
- package/dist/custom-elements.json +793 -17
- package/dist/disclosure/tabs/tabs.css.cjs +1 -1
- package/dist/disclosure/tabs/tabs.css.cjs.map +1 -1
- package/dist/disclosure/tabs/tabs.css.js +1 -1
- package/dist/disclosure/tabs/tabs.css.js.map +1 -1
- package/dist/feedback/alert/alert.d.ts +8 -1
- package/dist/feedback/alert/alert.d.ts.map +1 -1
- package/dist/feedback/chip/chip.css.cjs +1 -1
- package/dist/feedback/chip/chip.css.cjs.map +1 -1
- package/dist/feedback/chip/chip.css.js +1 -1
- package/dist/feedback/chip/chip.css.js.map +1 -1
- package/dist/feedback/chip/chip.d.ts +6 -2
- package/dist/feedback/chip/chip.d.ts.map +1 -1
- package/dist/feedback/progress/progress.css.cjs +1 -1
- package/dist/feedback/progress/progress.css.cjs.map +1 -1
- package/dist/feedback/progress/progress.css.js +1 -1
- package/dist/feedback/progress/progress.css.js.map +1 -1
- package/dist/feedback/progress/progress.d.ts +6 -0
- package/dist/feedback/progress/progress.d.ts.map +1 -1
- package/dist/feedback/toast/toast.css.cjs +1 -1
- package/dist/feedback/toast/toast.css.cjs.map +1 -1
- package/dist/feedback/toast/toast.css.js +1 -1
- package/dist/feedback/toast/toast.css.js.map +1 -1
- package/dist/feedback/toast/toast.d.ts.map +1 -1
- package/dist/frameworks/elements.d.ts +1 -1
- package/dist/inputs/button/button.css.cjs +1 -1
- package/dist/inputs/button/button.css.cjs.map +1 -1
- package/dist/inputs/button/button.css.js +1 -1
- package/dist/inputs/button/button.css.js.map +1 -1
- package/dist/inputs/combobox/combobox.css.cjs +1 -1
- package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
- package/dist/inputs/combobox/combobox.css.js +1 -1
- package/dist/inputs/combobox/combobox.css.js.map +1 -1
- package/dist/inputs/counter/counter.css.cjs +2 -0
- package/dist/inputs/counter/counter.css.cjs.map +1 -0
- package/dist/inputs/counter/counter.css.js +6 -0
- package/dist/inputs/counter/counter.css.js.map +1 -0
- package/dist/inputs/counter/counter.d.ts +100 -0
- package/dist/inputs/counter/counter.d.ts.map +1 -0
- package/dist/inputs/date-picker/date-picker.css.cjs +1 -1
- package/dist/inputs/date-picker/date-picker.css.cjs.map +1 -1
- package/dist/inputs/date-picker/date-picker.css.js +1 -1
- package/dist/inputs/date-picker/date-picker.css.js.map +1 -1
- package/dist/inputs/index.d.ts +4 -0
- package/dist/inputs/index.d.ts.map +1 -1
- package/dist/inputs/input/input.css.cjs +1 -1
- package/dist/inputs/input/input.css.cjs.map +1 -1
- package/dist/inputs/input/input.css.js +1 -1
- package/dist/inputs/input/input.css.js.map +1 -1
- package/dist/inputs/number-input/number-input.css.cjs +1 -1
- package/dist/inputs/number-input/number-input.css.cjs.map +1 -1
- package/dist/inputs/number-input/number-input.css.js +1 -1
- package/dist/inputs/number-input/number-input.css.js.map +1 -1
- package/dist/inputs/qr-scanner/qr-scanner.css.cjs +2 -0
- package/dist/inputs/qr-scanner/qr-scanner.css.cjs.map +1 -0
- package/dist/inputs/qr-scanner/qr-scanner.css.js +6 -0
- package/dist/inputs/qr-scanner/qr-scanner.css.js.map +1 -0
- package/dist/inputs/qr-scanner/qr-scanner.d.ts +87 -0
- package/dist/inputs/qr-scanner/qr-scanner.d.ts.map +1 -0
- package/dist/inputs/select/select.css.cjs +1 -1
- package/dist/inputs/select/select.css.cjs.map +1 -1
- package/dist/inputs/select/select.css.js +1 -1
- package/dist/inputs/select/select.css.js.map +1 -1
- package/dist/inputs/slider/slider.css.cjs +1 -1
- package/dist/inputs/slider/slider.css.cjs.map +1 -1
- package/dist/inputs/slider/slider.css.js +1 -1
- package/dist/inputs/slider/slider.css.js.map +1 -1
- package/dist/keyboard-key.cjs +9 -0
- package/dist/keyboard-key.cjs.map +1 -0
- package/dist/keyboard-key.js +36 -0
- package/dist/keyboard-key.js.map +1 -0
- package/dist/menu.cjs +1 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +2 -1
- package/dist/menu.js.map +1 -1
- package/dist/overlay/command-palette/command-palette.css.cjs +1 -1
- package/dist/overlay/command-palette/command-palette.css.cjs.map +1 -1
- package/dist/overlay/command-palette/command-palette.css.js +1 -1
- package/dist/overlay/command-palette/command-palette.css.js.map +1 -1
- package/dist/overlay/command-palette/command-palette.d.ts +1 -0
- package/dist/overlay/command-palette/command-palette.d.ts.map +1 -1
- package/dist/progress.cjs +14 -6
- package/dist/progress.cjs.map +1 -1
- package/dist/progress.js +19 -7
- package/dist/progress.js.map +1 -1
- package/dist/qr-code.cjs +15 -0
- package/dist/qr-code.cjs.map +1 -0
- package/dist/qr-code.js +70 -0
- package/dist/qr-code.js.map +1 -0
- package/dist/qr-scanner.cjs +46 -0
- package/dist/qr-scanner.cjs.map +1 -0
- package/dist/qr-scanner.js +129 -0
- package/dist/qr-scanner.js.map +1 -0
- package/dist/refine.cjs +365 -199
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +393 -227
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +740 -574
- package/dist/refine.js.map +1 -1
- package/dist/tabs.cjs +2 -2
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js +37 -34
- package/dist/tabs.js.map +1 -1
- package/dist/toast.cjs +44 -39
- package/dist/toast.cjs.map +1 -1
- package/dist/toast.js +132 -117
- package/dist/toast.js.map +1 -1
- package/dist/types/elements.d.ts +9 -0
- package/package.json +35 -2
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type QrErrorCorrection, SigilError } from '@vielzeug/sigil';
|
|
2
|
+
import type { ComponentSize, RoundedSize } from '../../types';
|
|
3
|
+
/** QR code component properties */
|
|
4
|
+
export type OreQrCodeProps = {
|
|
5
|
+
/** Error-correction level */
|
|
6
|
+
'error-correction'?: QrErrorCorrection;
|
|
7
|
+
/** Accessible label for the rendered code */
|
|
8
|
+
label?: string;
|
|
9
|
+
/** Quiet-zone width in modules */
|
|
10
|
+
margin?: number;
|
|
11
|
+
/** Border radius size */
|
|
12
|
+
rounded?: RoundedSize;
|
|
13
|
+
/** Rendered size: named scale or pixel number */
|
|
14
|
+
size?: ComponentSize | number;
|
|
15
|
+
/** The payload to encode */
|
|
16
|
+
value?: string;
|
|
17
|
+
/** Surface variant: 'flat' (transparent) or 'card' (themed background) */
|
|
18
|
+
variant?: 'flat' | 'card';
|
|
19
|
+
};
|
|
20
|
+
/** QR code component events */
|
|
21
|
+
export type OreQrCodeEvents = {
|
|
22
|
+
/** Emitted whenever the code is (re)rendered. detail: `{ version, size }` */
|
|
23
|
+
render: {
|
|
24
|
+
version: number;
|
|
25
|
+
size: number;
|
|
26
|
+
};
|
|
27
|
+
/** Emitted when encoding fails (e.g. payload exceeds capacity). detail: `{ error }` */
|
|
28
|
+
error: {
|
|
29
|
+
error: SigilError;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Renders a QR code for `value` as inline SVG — themeable via `currentColor`,
|
|
34
|
+
* crisp at any scale, no canvas. On capacity errors the component shows an
|
|
35
|
+
* error state and emits `error` rather than throwing.
|
|
36
|
+
*
|
|
37
|
+
* @element ore-qr-code
|
|
38
|
+
*
|
|
39
|
+
* @attr {string} value - The payload to encode
|
|
40
|
+
* @attr {string} error-correction - 'L' | 'M' | 'Q' | 'H' (default 'M')
|
|
41
|
+
* @attr {number} margin - Quiet-zone modules (default 4)
|
|
42
|
+
* @attr {string} size - 'sm' | 'md' | 'lg', or a pixel value
|
|
43
|
+
* @attr {string} label - Accessible label (default "QR code")
|
|
44
|
+
* @attr {string} rounded - Border radius for the card variant
|
|
45
|
+
*
|
|
46
|
+
* @fires render - detail: `{ version, size }` after each successful render
|
|
47
|
+
* @fires error - detail: `{ error }` when encoding fails
|
|
48
|
+
*
|
|
49
|
+
* @slot caption - Optional caption rendered below the code
|
|
50
|
+
*
|
|
51
|
+
* @cssprop --qr-code-dark - Module color (default `currentColor`)
|
|
52
|
+
* @cssprop --qr-code-light - Background color (default `transparent`)
|
|
53
|
+
* @cssprop --qr-code-size - Rendered size in px (overrides `size`)
|
|
54
|
+
* @cssprop --qr-code-radius - Border radius
|
|
55
|
+
* @cssprop --qr-code-padding - Inner padding
|
|
56
|
+
* @cssprop --qr-code-bg - Card background (variant="card")
|
|
57
|
+
* @cssprop --qr-code-border - Card border width (variant="card")
|
|
58
|
+
* @cssprop --qr-code-border-color - Card border color (variant="card")
|
|
59
|
+
* @cssprop --qr-code-shadow - Card box shadow (variant="card")
|
|
60
|
+
* @cssprop --qr-code-error-color - Error-state text color
|
|
61
|
+
*
|
|
62
|
+
* @part wrapper - Outer container
|
|
63
|
+
* @part svg - The rendered `<svg>` element
|
|
64
|
+
* @part caption - Caption slot container
|
|
65
|
+
* @part error - Error-state container
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```html
|
|
69
|
+
* <ore-qr-code value="https://example.com"></ore-qr-code>
|
|
70
|
+
* <ore-qr-code value="otpauth://…" error-correction="H" size="256"></ore-qr-code>
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare const QR_CODE_TAG: "ore-qr-code";
|
|
74
|
+
//# sourceMappingURL=qr-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qr-code.d.ts","sourceRoot":"","sources":["../../../src/content/qr-code/qr-code.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,iBAAiB,EAAE,UAAU,EAAS,MAAM,iBAAiB,CAAC;AAGtF,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG9D,mCAAmC;AACnC,MAAM,MAAM,cAAc,GAAG;IAC3B,6BAA6B;IAC7B,kBAAkB,CAAC,EAAE,iBAAiB,CAAC;IACvC,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,iDAAiD;IACjD,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAC9B,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B,CAAC;AAEF,+BAA+B;AAC/B,MAAM,MAAM,eAAe,GAAG;IAC5B,6EAA6E;IAC7E,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,uFAAuF;IACvF,KAAK,EAAE;QAAE,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;CAC9B,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,WAAW,EAAG,aAAsB,CAAC"}
|
package/dist/core/positioner.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./direction.cjs");let t=require("@vielzeug/orbit");var n=e=>e.endsWith(`-start`)?e.replace(`-start`,`-end`):e.endsWith(`-end`)?e.replace(`-end`,`-start`):e;function r({boundary:r,getDir:i,getFloating:a,getPlacement:o,getReference:s,matchWidth:c=!0,offsetPx:l=0,padding:u=6,useClippingAncestor:d=!0}){function f(){let t=o?.()??`bottom-start`;return(i?.()??e.elementDirection(s()))===`rtl`?n(t):t}function p(){let e=s(),n=a();if(!e||!n)return;let i=f(),o=r??(d?(0,t.getClippingAncestorRect)(n):void 0),p=(0,t.computePosition)(e,n,{boundary:o,middleware:[...l?[(0,t.offset)(l)]:[],(0,t.flip)({padding:u}),(0,t.shift)({padding:u}),...c?[(0,t.size)({padding:u})]:[]],placement:i});n.style.left=`${
|
|
1
|
+
const e=require("./direction.cjs");let t=require("@vielzeug/orbit");var n=e=>e.endsWith(`-start`)?e.replace(`-start`,`-end`):e.endsWith(`-end`)?e.replace(`-end`,`-start`):e;function r({boundary:r,getDir:i,getFloating:a,getPlacement:o,getReference:s,matchWidth:c=!0,offsetPx:l=0,padding:u=6,useClippingAncestor:d=!0}){function f(){let t=o?.()??`bottom-start`;return(i?.()??e.elementDirection(s()))===`rtl`?n(t):t}function p(){let e=s(),n=a();if(!e||!n)return;let i=f(),o=r??(d?(0,t.getClippingAncestorRect)(n):void 0),p=e.getBoundingClientRect().width,m=0;c&&(n.style.left=`0px`,n.style.width=`auto`,n.style.minWidth=`${p}px`,m=n.getBoundingClientRect().width);let h=(0,t.computePosition)(e,n,{boundary:o,middleware:[...l?[(0,t.offset)(l)]:[],(0,t.flip)({padding:u}),(0,t.shift)({padding:u}),...c?[(0,t.size)({padding:u})]:[]],placement:i});n.style.left=`${h.x}px`,n.style.top=`${h.y}px`;let g=n.style.transition,_=n.style.transform;n.style.transition=`none`,n.style.transform=`none`;let v=n.getBoundingClientRect(),y=h.x-v.left,b=h.y-v.top;if(n.style.transition=g,n.style.transform=_,(y!==0||b!==0)&&(n.style.left=`${h.x+y}px`,n.style.top=`${h.y+b}px`),c){let e=h.middlewareData.size?.availableWidth??1/0;n.style.width=`${Math.min(Math.max(m,p),e)}px`}}function m(){let e=s(),n=a();return!e||!n?()=>{}:(0,t.autoUpdate)(e,n,p)}return{floating:a,reference:s,startAutoUpdate:m,update:p}}exports.createDropdownPositioner=r;
|
|
2
2
|
//# sourceMappingURL=positioner.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"positioner.cjs","names":[],"sources":["../../src/core/positioner.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n getClippingAncestorRect,\n offset,\n type Placement,\n type Rect,\n shift,\n size,\n} from '@vielzeug/orbit';\n\nimport { elementDirection } from './direction';\n\n// ── Overlay positioner contract ──────────────────────────────────────────────\n\nexport type OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n /** Start continuous position auto-updates. Returns a stop function. */\n startAutoUpdate?: () => () => void;\n update: () => void;\n};\n\n// ── Dropdown positioner ───────────────────────────────────────────────────────\n\nexport type DropdownPositionerOptions = {\n /**\n * Clipping boundary for flip/shift/size — the edges the dropdown must stay within.\n * Defaults to the nearest ancestor that actually clips content (`overflow:\n * hidden|auto|scroll|clip`, e.g. a dialog panel), auto-detected per update via\n * `getClippingAncestorRect()`, intersected with the viewport. Without this, flip/shift only\n * avoid the full page viewport — a dropdown can be \"in view\" by that measure while still\n * visibly overhanging a much smaller container it's actually nested in. Pass an explicit\n * boundary to override auto-detection.\n */\n boundary?: Element | Rect;\n /**\n * Returns the text direction used for placement mirroring. Defaults to the resolved\n * direction of the reference element (nearest `dir=\"ltr\"|\"rtl\"` ancestor, falling back to\n * computed style) — most callers never need to pass this explicitly.\n */\n getDir?: () => 'ltr' | 'rtl';\n /** Getter for the floating (dropdown panel) element. */\n getFloating: () => HTMLElement | null;\n /** Getter for the current placement. Defaults to 'bottom-start' (RTL-aware: flips to 'bottom-end'). */\n getPlacement?: () => Placement;\n /** Getter for the reference (trigger) element. */\n getReference: () => HTMLElement | null;\n /** Whether to match the floating element width to the reference. Default: true */\n matchWidth?: boolean;\n /** Additional offset in pixels between reference and floating element. Default: 0 */\n offsetPx?: number;\n /** Padding (px) used by flip, shift, and size middleware. Default: 6 */\n padding?: number;\n /**\n * Whether to constrain placement to a clipping ancestor when `boundary` is unset. Default:\n * true. Disable for native Popover API panels, which render in the top layer.\n */\n useClippingAncestor?: boolean;\n};\n\n/** Mirrors a start/end placement for RTL layouts. */\nconst rtlPlacement = (p: Placement): Placement => {\n if (p.endsWith('-start')) return p.replace('-start', '-end') as Placement;\n\n if (p.endsWith('-end')) return p.replace('-end', '-start') as Placement;\n\n return p;\n};\n\n/**\n * Creates an Orbit-powered positioner for dropdown overlays (select, combobox, menu).\n *\n * Uses flip + shift + optional width-matching middleware. Supports continuous\n * auto-update for repositioning on scroll or resize.\n *\n * RTL-aware: start/end placements are automatically mirrored so dropdowns open on the\n * correct side, based on `getDir` (or the reference element's resolved direction when\n * `getDir` is omitted).\n */\nexport function createDropdownPositioner({\n boundary,\n getDir,\n getFloating,\n getPlacement,\n getReference,\n matchWidth = true,\n offsetPx = 0,\n padding = 6,\n useClippingAncestor = true,\n}: DropdownPositionerOptions): OverlayPositioner {\n function resolvedPlacement(): Placement {\n const base = getPlacement?.() ?? 'bottom-start';\n const dir = getDir?.() ?? elementDirection(getReference());\n\n return dir === 'rtl' ? rtlPlacement(base) : base;\n }\n\n function updatePosition(): void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return;\n\n const placement = resolvedPlacement();\n // Defaults to `undefined` (meaning \"auto-detect\"), not the detected value itself —\n // re-running the detection on every update (rather than once, at positioner-creation time)\n // matters because the dialog/scroll-container an instance is rendered into can change\n // across opens (e.g. the same `<ore-select>` reused in different dialogs), and the detected\n // ancestor's own rect can change between opens even when it's the same element (a resized\n // dialog).\n const resolvedBoundary = boundary ?? (useClippingAncestor ? getClippingAncestorRect(floating) : undefined);\n\n const result = computePosition(ref, floating, {\n boundary: resolvedBoundary,\n middleware: [\n ...(offsetPx ? [offset(offsetPx)] : []),\n flip({ padding }),\n shift({ padding }),\n ...(matchWidth ? [size({ padding })] : []),\n ],\n placement,\n });\n\n floating.style.left = `${result.x}px`;\n floating.style.top = `${result.y}px`;\n\n // `computePosition()` assumes `left`/`top` resolve relative to the viewport (correct for\n // `position: fixed`) — true almost always, but not when some ancestor establishes a\n // containing block for fixed descendants (a non-`none` `transform`, even a visually-identity\n // one left over from an entrance transition that never resets to `none` at rest is a real,\n // easy-to-hit case — a modal dialog's panel, say). Analytically detecting *that* ancestor via\n // `getContainingBlock()` and pre-subtracting its rect turned out to be unreliable in\n // practice: browsers don't apply the \"ancestor transform traps fixed descendants\" rule\n // uniformly across every element nested under it in every case we tested — some floating\n // elements in the exact same subtree needed the correction and some didn't, for reasons we\n // couldn't fully pin down. Measuring what actually happened after writing `left`/`top`\n // and correcting only the observed gap sidesteps that: it's a no-op when the browser already\n // put the element where asked, and it fixes the same class of bug the moment there's a real\n // mismatch, regardless of which specific ancestor (if any) turns out to be responsible.\n //\n // The measurement itself must not be fooled by the floating element's *own* transform,\n // though: dropdowns/popovers here all use a `transform: translateY(...)` entrance transition\n // driven by a `[data-open]` attribute + `@starting-style` — right at open, before the\n // transition has visibly progressed, `getBoundingClientRect()` can still reflect that\n // starting offset (a handful of px, matching `--overlay-enter-translate-y`), which this\n // correction would otherwise misread as a permanent ancestor-driven mismatch and bake in\n // forever. Temporarily neutralizing the floating element's own transform for the measurement\n // (and restoring it before anything paints, so there's no visible flash) isolates the\n // correction to ancestor-driven offsets only, which is the only thing it's meant to fix.\n const prevTransition = floating.style.transition;\n const prevTransform = floating.style.transform;\n\n floating.style.transition = 'none';\n floating.style.transform = 'none';\n\n const actual = floating.getBoundingClientRect();\n const deltaX = result.x - actual.left;\n const deltaY = result.y - actual.top;\n\n floating.style.transition = prevTransition;\n floating.style.transform = prevTransform;\n\n if (deltaX !== 0 || deltaY !== 0) {\n floating.style.left = `${result.x + deltaX}px`;\n floating.style.top = `${result.y + deltaY}px`;\n }\n\n if (matchWidth) {\n floating.style.width = `${ref.getBoundingClientRect().width}px`;\n }\n }\n\n function startAutoUpdate(): () => void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return () => {};\n\n return autoUpdate(ref, floating, updatePosition);\n }\n\n return { floating: getFloating, reference: getReference, startAutoUpdate, update: updatePosition };\n}\n"],"mappings":"oEA+DA,IAAM,EAAgB,GAChB,EAAE,SAAS,QAAQ,EAAU,EAAE,QAAQ,SAAU,MAAM,EAEvD,EAAE,SAAS,MAAM,EAAU,EAAE,QAAQ,OAAQ,QAAQ,EAElD,EAaT,SAAgB,EAAyB,CACvC,WACA,SACA,cACA,eACA,eACA,aAAa,GACb,WAAW,EACX,UAAU,EACV,sBAAsB,IACyB,CAC/C,SAAS,GAA+B,CACtC,IAAM,EAAO,IAAe,GAAK,eAGjC,OAFY,IAAS,GAAK,EAAA,iBAAiB,EAAa,CAAC,KAE1C,MAAQ,EAAa,CAAI,EAAI,CAC9C,CAEA,SAAS,GAAuB,CAC9B,IAAM,EAAM,EAAa,EACnB,EAAW,EAAY,EAE7B,GAAI,CAAC,GAAO,CAAC,EAAU,OAEvB,IAAM,EAAY,EAAkB,EAO9B,EAAmB,IAAa,GAAA,EAAsB,EAAA,wBAAA,CAAwB,CAAQ,EAAI,IAAA,IAE1F,GAAA,EAAS,EAAA,gBAAA,CAAgB,EAAK,EAAU,CAC5C,SAAU,EACV,WAAY,CACV,GAAI,EAAW,EAAA,EAAC,EAAA,OAAA,CAAO,CAAQ,CAAC,EAAI,CAAC,GACrC,EAAA,EAAA,KAAA,CAAK,CAAE,SAAQ,CAAC,GAChB,EAAA,EAAA,MAAA,CAAM,CAAE,SAAQ,CAAC,EACjB,GAAI,EAAa,EAAA,EAAC,EAAA,KAAA,CAAK,CAAE,SAAQ,CAAC,CAAC,EAAI,CAAC,CAC1C,EACA,WACF,CAAC,EAED,EAAS,MAAM,KAAO,GAAG,EAAO,EAAE,IAClC,EAAS,MAAM,IAAM,GAAG,EAAO,EAAE,IAyBjC,IAAM,EAAiB,EAAS,MAAM,WAChC,EAAgB,EAAS,MAAM,UAErC,EAAS,MAAM,WAAa,OAC5B,EAAS,MAAM,UAAY,OAE3B,IAAM,EAAS,EAAS,sBAAsB,EACxC,EAAS,EAAO,EAAI,EAAO,KAC3B,EAAS,EAAO,EAAI,EAAO,IAEjC,EAAS,MAAM,WAAa,EAC5B,EAAS,MAAM,UAAY,GAEvB,IAAW,GAAK,IAAW,KAC7B,EAAS,MAAM,KAAO,GAAG,EAAO,EAAI,EAAO,IAC3C,EAAS,MAAM,IAAM,GAAG,EAAO,EAAI,EAAO,KAGxC,IACF,EAAS,MAAM,MAAQ,GAAG,EAAI,sBAAsB,CAAC,CAAC,MAAM,IAEhE,CAEA,SAAS,GAA8B,CACrC,IAAM,EAAM,EAAa,EACnB,EAAW,EAAY,EAI7B,MAFI,CAAC,GAAO,CAAC,MAAuB,CAAC,GAErC,EAAO,EAAA,WAAA,CAAW,EAAK,EAAU,CAAc,CACjD,CAEA,MAAO,CAAE,SAAU,EAAa,UAAW,EAAc,kBAAiB,OAAQ,CAAe,CACnG"}
|
|
1
|
+
{"version":3,"file":"positioner.cjs","names":[],"sources":["../../src/core/positioner.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n getClippingAncestorRect,\n offset,\n type Placement,\n type Rect,\n type SizeData,\n shift,\n size,\n} from '@vielzeug/orbit';\n\nimport { elementDirection } from './direction';\n\n// ── Overlay positioner contract ──────────────────────────────────────────────\n\nexport type OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n /** Start continuous position auto-updates. Returns a stop function. */\n startAutoUpdate?: () => () => void;\n update: () => void;\n};\n\n// ── Dropdown positioner ───────────────────────────────────────────────────────\n\nexport type DropdownPositionerOptions = {\n /**\n * Clipping boundary for flip/shift/size — the edges the dropdown must stay within.\n * Defaults to the nearest ancestor that actually clips content (`overflow:\n * hidden|auto|scroll|clip`, e.g. a dialog panel), auto-detected per update via\n * `getClippingAncestorRect()`, intersected with the viewport. Without this, flip/shift only\n * avoid the full page viewport — a dropdown can be \"in view\" by that measure while still\n * visibly overhanging a much smaller container it's actually nested in. Pass an explicit\n * boundary to override auto-detection.\n */\n boundary?: Element | Rect;\n /**\n * Returns the text direction used for placement mirroring. Defaults to the resolved\n * direction of the reference element (nearest `dir=\"ltr\"|\"rtl\"` ancestor, falling back to\n * computed style) — most callers never need to pass this explicitly.\n */\n getDir?: () => 'ltr' | 'rtl';\n /** Getter for the floating (dropdown panel) element. */\n getFloating: () => HTMLElement | null;\n /** Getter for the current placement. Defaults to 'bottom-start' (RTL-aware: flips to 'bottom-end'). */\n getPlacement?: () => Placement;\n /** Getter for the reference (trigger) element. */\n getReference: () => HTMLElement | null;\n /**\n * Whether to size the floating element from the reference: at least as wide as the reference,\n * growing to fit its own content, capped by the boundary. Default: true\n */\n matchWidth?: boolean;\n /** Additional offset in pixels between reference and floating element. Default: 0 */\n offsetPx?: number;\n /** Padding (px) used by flip, shift, and size middleware. Default: 6 */\n padding?: number;\n /**\n * Whether to constrain placement to a clipping ancestor when `boundary` is unset. Default:\n * true. Disable for native Popover API panels, which render in the top layer.\n */\n useClippingAncestor?: boolean;\n};\n\n/** Mirrors a start/end placement for RTL layouts. */\nconst rtlPlacement = (p: Placement): Placement => {\n if (p.endsWith('-start')) return p.replace('-start', '-end') as Placement;\n\n if (p.endsWith('-end')) return p.replace('-end', '-start') as Placement;\n\n return p;\n};\n\n/**\n * Creates an Orbit-powered positioner for dropdown overlays (select, combobox, menu).\n *\n * Uses flip + shift + optional width-matching middleware. Supports continuous\n * auto-update for repositioning on scroll or resize.\n *\n * RTL-aware: start/end placements are automatically mirrored so dropdowns open on the\n * correct side, based on `getDir` (or the reference element's resolved direction when\n * `getDir` is omitted).\n */\nexport function createDropdownPositioner({\n boundary,\n getDir,\n getFloating,\n getPlacement,\n getReference,\n matchWidth = true,\n offsetPx = 0,\n padding = 6,\n useClippingAncestor = true,\n}: DropdownPositionerOptions): OverlayPositioner {\n function resolvedPlacement(): Placement {\n const base = getPlacement?.() ?? 'bottom-start';\n const dir = getDir?.() ?? elementDirection(getReference());\n\n return dir === 'rtl' ? rtlPlacement(base) : base;\n }\n\n function updatePosition(): void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return;\n\n const placement = resolvedPlacement();\n // Defaults to `undefined` (meaning \"auto-detect\"), not the detected value itself —\n // re-running the detection on every update (rather than once, at positioner-creation time)\n // matters because the dialog/scroll-container an instance is rendered into can change\n // across opens (e.g. the same `<ore-select>` reused in different dialogs), and the detected\n // ancestor's own rect can change between opens even when it's the same element (a resized\n // dialog).\n const resolvedBoundary = boundary ?? (useClippingAncestor ? getClippingAncestorRect(floating) : undefined);\n\n const refWidth = ref.getBoundingClientRect().width;\n let naturalWidth = 0;\n\n if (matchWidth) {\n // Measure the panel at its natural content width, floored at the reference width, so\n // `computePosition()` places (and `shift` keeps inside the boundary) the box that will\n // actually render. `left: 0` frees the measurement from the shrink-to-fit clamp a previous\n // `left` would impose on a `position: fixed` element near the right edge.\n floating.style.left = '0px';\n floating.style.width = 'auto';\n floating.style.minWidth = `${refWidth}px`;\n naturalWidth = floating.getBoundingClientRect().width;\n }\n\n const result = computePosition(ref, floating, {\n boundary: resolvedBoundary,\n middleware: [\n ...(offsetPx ? [offset(offsetPx)] : []),\n flip({ padding }),\n shift({ padding }),\n ...(matchWidth ? [size({ padding })] : []),\n ],\n placement,\n });\n\n floating.style.left = `${result.x}px`;\n floating.style.top = `${result.y}px`;\n\n // `computePosition()` assumes `left`/`top` resolve relative to the viewport (correct for\n // `position: fixed`) — true almost always, but not when some ancestor establishes a\n // containing block for fixed descendants (a non-`none` `transform`, even a visually-identity\n // one left over from an entrance transition that never resets to `none` at rest is a real,\n // easy-to-hit case — a modal dialog's panel, say). Analytically detecting *that* ancestor via\n // `getContainingBlock()` and pre-subtracting its rect turned out to be unreliable in\n // practice: browsers don't apply the \"ancestor transform traps fixed descendants\" rule\n // uniformly across every element nested under it in every case we tested — some floating\n // elements in the exact same subtree needed the correction and some didn't, for reasons we\n // couldn't fully pin down. Measuring what actually happened after writing `left`/`top`\n // and correcting only the observed gap sidesteps that: it's a no-op when the browser already\n // put the element where asked, and it fixes the same class of bug the moment there's a real\n // mismatch, regardless of which specific ancestor (if any) turns out to be responsible.\n //\n // The measurement itself must not be fooled by the floating element's *own* transform,\n // though: dropdowns/popovers here all use a `transform: translateY(...)` entrance transition\n // driven by a `[data-open]` attribute + `@starting-style` — right at open, before the\n // transition has visibly progressed, `getBoundingClientRect()` can still reflect that\n // starting offset (a handful of px, matching `--overlay-enter-translate-y`), which this\n // correction would otherwise misread as a permanent ancestor-driven mismatch and bake in\n // forever. Temporarily neutralizing the floating element's own transform for the measurement\n // (and restoring it before anything paints, so there's no visible flash) isolates the\n // correction to ancestor-driven offsets only, which is the only thing it's meant to fix.\n const prevTransition = floating.style.transition;\n const prevTransform = floating.style.transform;\n\n floating.style.transition = 'none';\n floating.style.transform = 'none';\n\n const actual = floating.getBoundingClientRect();\n const deltaX = result.x - actual.left;\n const deltaY = result.y - actual.top;\n\n floating.style.transition = prevTransition;\n floating.style.transform = prevTransform;\n\n if (deltaX !== 0 || deltaY !== 0) {\n floating.style.left = `${result.x + deltaX}px`;\n floating.style.top = `${result.y + deltaY}px`;\n }\n\n if (matchWidth) {\n const available =\n (result.middlewareData.size as SizeData | undefined)?.availableWidth ?? Number.POSITIVE_INFINITY;\n // A panel measured while not yet rendered reports 0 — fall back to the reference width.\n floating.style.width = `${Math.min(Math.max(naturalWidth, refWidth), available)}px`;\n }\n }\n\n function startAutoUpdate(): () => void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return () => {};\n\n return autoUpdate(ref, floating, updatePosition);\n }\n\n return { floating: getFloating, reference: getReference, startAutoUpdate, update: updatePosition };\n}\n"],"mappings":"oEAmEA,IAAM,EAAgB,GAChB,EAAE,SAAS,QAAQ,EAAU,EAAE,QAAQ,SAAU,MAAM,EAEvD,EAAE,SAAS,MAAM,EAAU,EAAE,QAAQ,OAAQ,QAAQ,EAElD,EAaT,SAAgB,EAAyB,CACvC,WACA,SACA,cACA,eACA,eACA,aAAa,GACb,WAAW,EACX,UAAU,EACV,sBAAsB,IACyB,CAC/C,SAAS,GAA+B,CACtC,IAAM,EAAO,IAAe,GAAK,eAGjC,OAFY,IAAS,GAAK,EAAA,iBAAiB,EAAa,CAAC,KAE1C,MAAQ,EAAa,CAAI,EAAI,CAC9C,CAEA,SAAS,GAAuB,CAC9B,IAAM,EAAM,EAAa,EACnB,EAAW,EAAY,EAE7B,GAAI,CAAC,GAAO,CAAC,EAAU,OAEvB,IAAM,EAAY,EAAkB,EAO9B,EAAmB,IAAa,GAAA,EAAsB,EAAA,wBAAA,CAAwB,CAAQ,EAAI,IAAA,IAE1F,EAAW,EAAI,sBAAsB,CAAC,CAAC,MACzC,EAAe,EAEf,IAKF,EAAS,MAAM,KAAO,MACtB,EAAS,MAAM,MAAQ,OACvB,EAAS,MAAM,SAAW,GAAG,EAAS,IACtC,EAAe,EAAS,sBAAsB,CAAC,CAAC,OAGlD,IAAM,GAAA,EAAS,EAAA,gBAAA,CAAgB,EAAK,EAAU,CAC5C,SAAU,EACV,WAAY,CACV,GAAI,EAAW,EAAA,EAAC,EAAA,OAAA,CAAO,CAAQ,CAAC,EAAI,CAAC,GACrC,EAAA,EAAA,KAAA,CAAK,CAAE,SAAQ,CAAC,GAChB,EAAA,EAAA,MAAA,CAAM,CAAE,SAAQ,CAAC,EACjB,GAAI,EAAa,EAAA,EAAC,EAAA,KAAA,CAAK,CAAE,SAAQ,CAAC,CAAC,EAAI,CAAC,CAC1C,EACA,WACF,CAAC,EAED,EAAS,MAAM,KAAO,GAAG,EAAO,EAAE,IAClC,EAAS,MAAM,IAAM,GAAG,EAAO,EAAE,IAyBjC,IAAM,EAAiB,EAAS,MAAM,WAChC,EAAgB,EAAS,MAAM,UAErC,EAAS,MAAM,WAAa,OAC5B,EAAS,MAAM,UAAY,OAE3B,IAAM,EAAS,EAAS,sBAAsB,EACxC,EAAS,EAAO,EAAI,EAAO,KAC3B,EAAS,EAAO,EAAI,EAAO,IAUjC,GARA,EAAS,MAAM,WAAa,EAC5B,EAAS,MAAM,UAAY,GAEvB,IAAW,GAAK,IAAW,KAC7B,EAAS,MAAM,KAAO,GAAG,EAAO,EAAI,EAAO,IAC3C,EAAS,MAAM,IAAM,GAAG,EAAO,EAAI,EAAO,KAGxC,EAAY,CACd,IAAM,EACH,EAAO,eAAe,MAA+B,gBAAkB,IAE1E,EAAS,MAAM,MAAQ,GAAG,KAAK,IAAI,KAAK,IAAI,EAAc,CAAQ,EAAG,CAAS,EAAE,GAClF,CACF,CAEA,SAAS,GAA8B,CACrC,IAAM,EAAM,EAAa,EACnB,EAAW,EAAY,EAI7B,MAFI,CAAC,GAAO,CAAC,MAAuB,CAAC,GAErC,EAAO,EAAA,WAAA,CAAW,EAAK,EAAU,CAAc,CACjD,CAEA,MAAO,CAAE,SAAU,EAAa,UAAW,EAAc,kBAAiB,OAAQ,CAAe,CACnG"}
|
|
@@ -29,7 +29,10 @@ export type DropdownPositionerOptions = {
|
|
|
29
29
|
getPlacement?: () => Placement;
|
|
30
30
|
/** Getter for the reference (trigger) element. */
|
|
31
31
|
getReference: () => HTMLElement | null;
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Whether to size the floating element from the reference: at least as wide as the reference,
|
|
34
|
+
* growing to fit its own content, capped by the boundary. Default: true
|
|
35
|
+
*/
|
|
33
36
|
matchWidth?: boolean;
|
|
34
37
|
/** Additional offset in pixels between reference and floating element. Default: 0 */
|
|
35
38
|
offsetPx?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"positioner.d.ts","sourceRoot":"","sources":["../../src/core/positioner.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACd,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"positioner.d.ts","sourceRoot":"","sources":["../../src/core/positioner.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACd,KAAK,IAAI,EAIV,MAAM,iBAAiB,CAAC;AAMzB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACpC,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,MAAM,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAIF,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,KAAK,GAAG,KAAK,CAAC;IAC7B,wDAAwD;IACxD,WAAW,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACtC,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,SAAS,CAAC;IAC/B,kDAAkD;IAClD,YAAY,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAWF;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,MAAM,EACN,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,UAAiB,EACjB,QAAY,EACZ,OAAW,EACX,mBAA0B,GAC3B,EAAE,yBAAyB,GAAG,iBAAiB,CA8G/C"}
|
package/dist/core/positioner.js
CHANGED
|
@@ -10,7 +10,9 @@ function l({ boundary: l, getDir: u, getFloating: d, getPlacement: f, getReferen
|
|
|
10
10
|
function y() {
|
|
11
11
|
let e = p(), t = d();
|
|
12
12
|
if (!e || !t) return;
|
|
13
|
-
let c = v(), u = l ?? (_ ? i(t) : void 0), f =
|
|
13
|
+
let c = v(), u = l ?? (_ ? i(t) : void 0), f = e.getBoundingClientRect().width, y = 0;
|
|
14
|
+
m && (t.style.left = "0px", t.style.width = "auto", t.style.minWidth = `${f}px`, y = t.getBoundingClientRect().width);
|
|
15
|
+
let b = n(e, t, {
|
|
14
16
|
boundary: u,
|
|
15
17
|
middleware: [
|
|
16
18
|
...h ? [a(h)] : [],
|
|
@@ -20,11 +22,14 @@ function l({ boundary: l, getDir: u, getFloating: d, getPlacement: f, getReferen
|
|
|
20
22
|
],
|
|
21
23
|
placement: c
|
|
22
24
|
});
|
|
23
|
-
t.style.left = `${
|
|
24
|
-
let
|
|
25
|
+
t.style.left = `${b.x}px`, t.style.top = `${b.y}px`;
|
|
26
|
+
let x = t.style.transition, S = t.style.transform;
|
|
25
27
|
t.style.transition = "none", t.style.transform = "none";
|
|
26
|
-
let
|
|
27
|
-
t.style.transition =
|
|
28
|
+
let C = t.getBoundingClientRect(), w = b.x - C.left, T = b.y - C.top;
|
|
29
|
+
if (t.style.transition = x, t.style.transform = S, (w !== 0 || T !== 0) && (t.style.left = `${b.x + w}px`, t.style.top = `${b.y + T}px`), m) {
|
|
30
|
+
let e = b.middlewareData.size?.availableWidth ?? Infinity;
|
|
31
|
+
t.style.width = `${Math.min(Math.max(y, f), e)}px`;
|
|
32
|
+
}
|
|
28
33
|
}
|
|
29
34
|
function b() {
|
|
30
35
|
let e = p(), n = d();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"positioner.js","names":[],"sources":["../../src/core/positioner.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n getClippingAncestorRect,\n offset,\n type Placement,\n type Rect,\n shift,\n size,\n} from '@vielzeug/orbit';\n\nimport { elementDirection } from './direction';\n\n// ── Overlay positioner contract ──────────────────────────────────────────────\n\nexport type OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n /** Start continuous position auto-updates. Returns a stop function. */\n startAutoUpdate?: () => () => void;\n update: () => void;\n};\n\n// ── Dropdown positioner ───────────────────────────────────────────────────────\n\nexport type DropdownPositionerOptions = {\n /**\n * Clipping boundary for flip/shift/size — the edges the dropdown must stay within.\n * Defaults to the nearest ancestor that actually clips content (`overflow:\n * hidden|auto|scroll|clip`, e.g. a dialog panel), auto-detected per update via\n * `getClippingAncestorRect()`, intersected with the viewport. Without this, flip/shift only\n * avoid the full page viewport — a dropdown can be \"in view\" by that measure while still\n * visibly overhanging a much smaller container it's actually nested in. Pass an explicit\n * boundary to override auto-detection.\n */\n boundary?: Element | Rect;\n /**\n * Returns the text direction used for placement mirroring. Defaults to the resolved\n * direction of the reference element (nearest `dir=\"ltr\"|\"rtl\"` ancestor, falling back to\n * computed style) — most callers never need to pass this explicitly.\n */\n getDir?: () => 'ltr' | 'rtl';\n /** Getter for the floating (dropdown panel) element. */\n getFloating: () => HTMLElement | null;\n /** Getter for the current placement. Defaults to 'bottom-start' (RTL-aware: flips to 'bottom-end'). */\n getPlacement?: () => Placement;\n /** Getter for the reference (trigger) element. */\n getReference: () => HTMLElement | null;\n /** Whether to match the floating element width to the reference. Default: true */\n matchWidth?: boolean;\n /** Additional offset in pixels between reference and floating element. Default: 0 */\n offsetPx?: number;\n /** Padding (px) used by flip, shift, and size middleware. Default: 6 */\n padding?: number;\n /**\n * Whether to constrain placement to a clipping ancestor when `boundary` is unset. Default:\n * true. Disable for native Popover API panels, which render in the top layer.\n */\n useClippingAncestor?: boolean;\n};\n\n/** Mirrors a start/end placement for RTL layouts. */\nconst rtlPlacement = (p: Placement): Placement => {\n if (p.endsWith('-start')) return p.replace('-start', '-end') as Placement;\n\n if (p.endsWith('-end')) return p.replace('-end', '-start') as Placement;\n\n return p;\n};\n\n/**\n * Creates an Orbit-powered positioner for dropdown overlays (select, combobox, menu).\n *\n * Uses flip + shift + optional width-matching middleware. Supports continuous\n * auto-update for repositioning on scroll or resize.\n *\n * RTL-aware: start/end placements are automatically mirrored so dropdowns open on the\n * correct side, based on `getDir` (or the reference element's resolved direction when\n * `getDir` is omitted).\n */\nexport function createDropdownPositioner({\n boundary,\n getDir,\n getFloating,\n getPlacement,\n getReference,\n matchWidth = true,\n offsetPx = 0,\n padding = 6,\n useClippingAncestor = true,\n}: DropdownPositionerOptions): OverlayPositioner {\n function resolvedPlacement(): Placement {\n const base = getPlacement?.() ?? 'bottom-start';\n const dir = getDir?.() ?? elementDirection(getReference());\n\n return dir === 'rtl' ? rtlPlacement(base) : base;\n }\n\n function updatePosition(): void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return;\n\n const placement = resolvedPlacement();\n // Defaults to `undefined` (meaning \"auto-detect\"), not the detected value itself —\n // re-running the detection on every update (rather than once, at positioner-creation time)\n // matters because the dialog/scroll-container an instance is rendered into can change\n // across opens (e.g. the same `<ore-select>` reused in different dialogs), and the detected\n // ancestor's own rect can change between opens even when it's the same element (a resized\n // dialog).\n const resolvedBoundary = boundary ?? (useClippingAncestor ? getClippingAncestorRect(floating) : undefined);\n\n const result = computePosition(ref, floating, {\n boundary: resolvedBoundary,\n middleware: [\n ...(offsetPx ? [offset(offsetPx)] : []),\n flip({ padding }),\n shift({ padding }),\n ...(matchWidth ? [size({ padding })] : []),\n ],\n placement,\n });\n\n floating.style.left = `${result.x}px`;\n floating.style.top = `${result.y}px`;\n\n // `computePosition()` assumes `left`/`top` resolve relative to the viewport (correct for\n // `position: fixed`) — true almost always, but not when some ancestor establishes a\n // containing block for fixed descendants (a non-`none` `transform`, even a visually-identity\n // one left over from an entrance transition that never resets to `none` at rest is a real,\n // easy-to-hit case — a modal dialog's panel, say). Analytically detecting *that* ancestor via\n // `getContainingBlock()` and pre-subtracting its rect turned out to be unreliable in\n // practice: browsers don't apply the \"ancestor transform traps fixed descendants\" rule\n // uniformly across every element nested under it in every case we tested — some floating\n // elements in the exact same subtree needed the correction and some didn't, for reasons we\n // couldn't fully pin down. Measuring what actually happened after writing `left`/`top`\n // and correcting only the observed gap sidesteps that: it's a no-op when the browser already\n // put the element where asked, and it fixes the same class of bug the moment there's a real\n // mismatch, regardless of which specific ancestor (if any) turns out to be responsible.\n //\n // The measurement itself must not be fooled by the floating element's *own* transform,\n // though: dropdowns/popovers here all use a `transform: translateY(...)` entrance transition\n // driven by a `[data-open]` attribute + `@starting-style` — right at open, before the\n // transition has visibly progressed, `getBoundingClientRect()` can still reflect that\n // starting offset (a handful of px, matching `--overlay-enter-translate-y`), which this\n // correction would otherwise misread as a permanent ancestor-driven mismatch and bake in\n // forever. Temporarily neutralizing the floating element's own transform for the measurement\n // (and restoring it before anything paints, so there's no visible flash) isolates the\n // correction to ancestor-driven offsets only, which is the only thing it's meant to fix.\n const prevTransition = floating.style.transition;\n const prevTransform = floating.style.transform;\n\n floating.style.transition = 'none';\n floating.style.transform = 'none';\n\n const actual = floating.getBoundingClientRect();\n const deltaX = result.x - actual.left;\n const deltaY = result.y - actual.top;\n\n floating.style.transition = prevTransition;\n floating.style.transform = prevTransform;\n\n if (deltaX !== 0 || deltaY !== 0) {\n floating.style.left = `${result.x + deltaX}px`;\n floating.style.top = `${result.y + deltaY}px`;\n }\n\n if (matchWidth) {\n floating.style.width = `${ref.getBoundingClientRect().width}px`;\n }\n }\n\n function startAutoUpdate(): () => void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return () => {};\n\n return autoUpdate(ref, floating, updatePosition);\n }\n\n return { floating: getFloating, reference: getReference, startAutoUpdate, update: updatePosition };\n}\n"],"mappings":";;;AA+DA,IAAM,KAAgB,MAChB,EAAE,SAAS,QAAQ,IAAU,EAAE,QAAQ,UAAU,MAAM,IAEvD,EAAE,SAAS,MAAM,IAAU,EAAE,QAAQ,QAAQ,QAAQ,IAElD;AAaT,SAAgB,EAAyB,EACvC,aACA,WACA,gBACA,iBACA,iBACA,gBAAa,IACb,cAAW,GACX,aAAU,GACV,yBAAsB,MACyB;CAC/C,SAAS,IAA+B;EACtC,IAAM,IAAO,IAAe,KAAK;EAGjC,QAFY,IAAS,KAAK,EAAiB,EAAa,CAAC,OAE1C,QAAQ,EAAa,CAAI,IAAI;CAC9C;CAEA,SAAS,IAAuB;EAC9B,IAAM,IAAM,EAAa,GACnB,IAAW,EAAY;EAE7B,IAAI,CAAC,KAAO,CAAC,GAAU;EAEvB,IAAM,IAAY,EAAkB,GAO9B,IAAmB,MAAa,IAAsB,EAAwB,CAAQ,IAAI,KAAA,IAE1F,IAAS,EAAgB,GAAK,GAAU;GAC5C,UAAU;GACV,YAAY;IACV,GAAI,IAAW,CAAC,EAAO,CAAQ,CAAC,IAAI,CAAC;IACrC,EAAK,EAAE,WAAQ,CAAC;IAChB,EAAM,EAAE,WAAQ,CAAC;IACjB,GAAI,IAAa,CAAC,EAAK,EAAE,WAAQ,CAAC,CAAC,IAAI,CAAC;GAC1C;GACA;EACF,CAAC;EAGD,AADA,EAAS,MAAM,OAAO,GAAG,EAAO,EAAE,KAClC,EAAS,MAAM,MAAM,GAAG,EAAO,EAAE;EAyBjC,IAAM,IAAiB,EAAS,MAAM,YAChC,IAAgB,EAAS,MAAM;EAGrC,AADA,EAAS,MAAM,aAAa,QAC5B,EAAS,MAAM,YAAY;EAE3B,IAAM,IAAS,EAAS,sBAAsB,GACxC,IAAS,EAAO,IAAI,EAAO,MAC3B,IAAS,EAAO,IAAI,EAAO;EAUjC,AARA,EAAS,MAAM,aAAa,GAC5B,EAAS,MAAM,YAAY,IAEvB,MAAW,KAAK,MAAW,OAC7B,EAAS,MAAM,OAAO,GAAG,EAAO,IAAI,EAAO,KAC3C,EAAS,MAAM,MAAM,GAAG,EAAO,IAAI,EAAO,MAGxC,MACF,EAAS,MAAM,QAAQ,GAAG,EAAI,sBAAsB,CAAC,CAAC,MAAM;CAEhE;CAEA,SAAS,IAA8B;EACrC,IAAM,IAAM,EAAa,GACnB,IAAW,EAAY;EAI7B,OAFI,CAAC,KAAO,CAAC,UAAuB,CAAC,IAE9B,EAAW,GAAK,GAAU,CAAc;CACjD;CAEA,OAAO;EAAE,UAAU;EAAa,WAAW;EAAc;EAAiB,QAAQ;CAAe;AACnG"}
|
|
1
|
+
{"version":3,"file":"positioner.js","names":[],"sources":["../../src/core/positioner.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n getClippingAncestorRect,\n offset,\n type Placement,\n type Rect,\n type SizeData,\n shift,\n size,\n} from '@vielzeug/orbit';\n\nimport { elementDirection } from './direction';\n\n// ── Overlay positioner contract ──────────────────────────────────────────────\n\nexport type OverlayPositioner = {\n floating: () => HTMLElement | null;\n reference: () => HTMLElement | null;\n /** Start continuous position auto-updates. Returns a stop function. */\n startAutoUpdate?: () => () => void;\n update: () => void;\n};\n\n// ── Dropdown positioner ───────────────────────────────────────────────────────\n\nexport type DropdownPositionerOptions = {\n /**\n * Clipping boundary for flip/shift/size — the edges the dropdown must stay within.\n * Defaults to the nearest ancestor that actually clips content (`overflow:\n * hidden|auto|scroll|clip`, e.g. a dialog panel), auto-detected per update via\n * `getClippingAncestorRect()`, intersected with the viewport. Without this, flip/shift only\n * avoid the full page viewport — a dropdown can be \"in view\" by that measure while still\n * visibly overhanging a much smaller container it's actually nested in. Pass an explicit\n * boundary to override auto-detection.\n */\n boundary?: Element | Rect;\n /**\n * Returns the text direction used for placement mirroring. Defaults to the resolved\n * direction of the reference element (nearest `dir=\"ltr\"|\"rtl\"` ancestor, falling back to\n * computed style) — most callers never need to pass this explicitly.\n */\n getDir?: () => 'ltr' | 'rtl';\n /** Getter for the floating (dropdown panel) element. */\n getFloating: () => HTMLElement | null;\n /** Getter for the current placement. Defaults to 'bottom-start' (RTL-aware: flips to 'bottom-end'). */\n getPlacement?: () => Placement;\n /** Getter for the reference (trigger) element. */\n getReference: () => HTMLElement | null;\n /**\n * Whether to size the floating element from the reference: at least as wide as the reference,\n * growing to fit its own content, capped by the boundary. Default: true\n */\n matchWidth?: boolean;\n /** Additional offset in pixels between reference and floating element. Default: 0 */\n offsetPx?: number;\n /** Padding (px) used by flip, shift, and size middleware. Default: 6 */\n padding?: number;\n /**\n * Whether to constrain placement to a clipping ancestor when `boundary` is unset. Default:\n * true. Disable for native Popover API panels, which render in the top layer.\n */\n useClippingAncestor?: boolean;\n};\n\n/** Mirrors a start/end placement for RTL layouts. */\nconst rtlPlacement = (p: Placement): Placement => {\n if (p.endsWith('-start')) return p.replace('-start', '-end') as Placement;\n\n if (p.endsWith('-end')) return p.replace('-end', '-start') as Placement;\n\n return p;\n};\n\n/**\n * Creates an Orbit-powered positioner for dropdown overlays (select, combobox, menu).\n *\n * Uses flip + shift + optional width-matching middleware. Supports continuous\n * auto-update for repositioning on scroll or resize.\n *\n * RTL-aware: start/end placements are automatically mirrored so dropdowns open on the\n * correct side, based on `getDir` (or the reference element's resolved direction when\n * `getDir` is omitted).\n */\nexport function createDropdownPositioner({\n boundary,\n getDir,\n getFloating,\n getPlacement,\n getReference,\n matchWidth = true,\n offsetPx = 0,\n padding = 6,\n useClippingAncestor = true,\n}: DropdownPositionerOptions): OverlayPositioner {\n function resolvedPlacement(): Placement {\n const base = getPlacement?.() ?? 'bottom-start';\n const dir = getDir?.() ?? elementDirection(getReference());\n\n return dir === 'rtl' ? rtlPlacement(base) : base;\n }\n\n function updatePosition(): void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return;\n\n const placement = resolvedPlacement();\n // Defaults to `undefined` (meaning \"auto-detect\"), not the detected value itself —\n // re-running the detection on every update (rather than once, at positioner-creation time)\n // matters because the dialog/scroll-container an instance is rendered into can change\n // across opens (e.g. the same `<ore-select>` reused in different dialogs), and the detected\n // ancestor's own rect can change between opens even when it's the same element (a resized\n // dialog).\n const resolvedBoundary = boundary ?? (useClippingAncestor ? getClippingAncestorRect(floating) : undefined);\n\n const refWidth = ref.getBoundingClientRect().width;\n let naturalWidth = 0;\n\n if (matchWidth) {\n // Measure the panel at its natural content width, floored at the reference width, so\n // `computePosition()` places (and `shift` keeps inside the boundary) the box that will\n // actually render. `left: 0` frees the measurement from the shrink-to-fit clamp a previous\n // `left` would impose on a `position: fixed` element near the right edge.\n floating.style.left = '0px';\n floating.style.width = 'auto';\n floating.style.minWidth = `${refWidth}px`;\n naturalWidth = floating.getBoundingClientRect().width;\n }\n\n const result = computePosition(ref, floating, {\n boundary: resolvedBoundary,\n middleware: [\n ...(offsetPx ? [offset(offsetPx)] : []),\n flip({ padding }),\n shift({ padding }),\n ...(matchWidth ? [size({ padding })] : []),\n ],\n placement,\n });\n\n floating.style.left = `${result.x}px`;\n floating.style.top = `${result.y}px`;\n\n // `computePosition()` assumes `left`/`top` resolve relative to the viewport (correct for\n // `position: fixed`) — true almost always, but not when some ancestor establishes a\n // containing block for fixed descendants (a non-`none` `transform`, even a visually-identity\n // one left over from an entrance transition that never resets to `none` at rest is a real,\n // easy-to-hit case — a modal dialog's panel, say). Analytically detecting *that* ancestor via\n // `getContainingBlock()` and pre-subtracting its rect turned out to be unreliable in\n // practice: browsers don't apply the \"ancestor transform traps fixed descendants\" rule\n // uniformly across every element nested under it in every case we tested — some floating\n // elements in the exact same subtree needed the correction and some didn't, for reasons we\n // couldn't fully pin down. Measuring what actually happened after writing `left`/`top`\n // and correcting only the observed gap sidesteps that: it's a no-op when the browser already\n // put the element where asked, and it fixes the same class of bug the moment there's a real\n // mismatch, regardless of which specific ancestor (if any) turns out to be responsible.\n //\n // The measurement itself must not be fooled by the floating element's *own* transform,\n // though: dropdowns/popovers here all use a `transform: translateY(...)` entrance transition\n // driven by a `[data-open]` attribute + `@starting-style` — right at open, before the\n // transition has visibly progressed, `getBoundingClientRect()` can still reflect that\n // starting offset (a handful of px, matching `--overlay-enter-translate-y`), which this\n // correction would otherwise misread as a permanent ancestor-driven mismatch and bake in\n // forever. Temporarily neutralizing the floating element's own transform for the measurement\n // (and restoring it before anything paints, so there's no visible flash) isolates the\n // correction to ancestor-driven offsets only, which is the only thing it's meant to fix.\n const prevTransition = floating.style.transition;\n const prevTransform = floating.style.transform;\n\n floating.style.transition = 'none';\n floating.style.transform = 'none';\n\n const actual = floating.getBoundingClientRect();\n const deltaX = result.x - actual.left;\n const deltaY = result.y - actual.top;\n\n floating.style.transition = prevTransition;\n floating.style.transform = prevTransform;\n\n if (deltaX !== 0 || deltaY !== 0) {\n floating.style.left = `${result.x + deltaX}px`;\n floating.style.top = `${result.y + deltaY}px`;\n }\n\n if (matchWidth) {\n const available =\n (result.middlewareData.size as SizeData | undefined)?.availableWidth ?? Number.POSITIVE_INFINITY;\n // A panel measured while not yet rendered reports 0 — fall back to the reference width.\n floating.style.width = `${Math.min(Math.max(naturalWidth, refWidth), available)}px`;\n }\n }\n\n function startAutoUpdate(): () => void {\n const ref = getReference();\n const floating = getFloating();\n\n if (!ref || !floating) return () => {};\n\n return autoUpdate(ref, floating, updatePosition);\n }\n\n return { floating: getFloating, reference: getReference, startAutoUpdate, update: updatePosition };\n}\n"],"mappings":";;;AAmEA,IAAM,KAAgB,MAChB,EAAE,SAAS,QAAQ,IAAU,EAAE,QAAQ,UAAU,MAAM,IAEvD,EAAE,SAAS,MAAM,IAAU,EAAE,QAAQ,QAAQ,QAAQ,IAElD;AAaT,SAAgB,EAAyB,EACvC,aACA,WACA,gBACA,iBACA,iBACA,gBAAa,IACb,cAAW,GACX,aAAU,GACV,yBAAsB,MACyB;CAC/C,SAAS,IAA+B;EACtC,IAAM,IAAO,IAAe,KAAK;EAGjC,QAFY,IAAS,KAAK,EAAiB,EAAa,CAAC,OAE1C,QAAQ,EAAa,CAAI,IAAI;CAC9C;CAEA,SAAS,IAAuB;EAC9B,IAAM,IAAM,EAAa,GACnB,IAAW,EAAY;EAE7B,IAAI,CAAC,KAAO,CAAC,GAAU;EAEvB,IAAM,IAAY,EAAkB,GAO9B,IAAmB,MAAa,IAAsB,EAAwB,CAAQ,IAAI,KAAA,IAE1F,IAAW,EAAI,sBAAsB,CAAC,CAAC,OACzC,IAAe;EAEnB,AAAI,MAKF,EAAS,MAAM,OAAO,OACtB,EAAS,MAAM,QAAQ,QACvB,EAAS,MAAM,WAAW,GAAG,EAAS,KACtC,IAAe,EAAS,sBAAsB,CAAC,CAAC;EAGlD,IAAM,IAAS,EAAgB,GAAK,GAAU;GAC5C,UAAU;GACV,YAAY;IACV,GAAI,IAAW,CAAC,EAAO,CAAQ,CAAC,IAAI,CAAC;IACrC,EAAK,EAAE,WAAQ,CAAC;IAChB,EAAM,EAAE,WAAQ,CAAC;IACjB,GAAI,IAAa,CAAC,EAAK,EAAE,WAAQ,CAAC,CAAC,IAAI,CAAC;GAC1C;GACA;EACF,CAAC;EAGD,AADA,EAAS,MAAM,OAAO,GAAG,EAAO,EAAE,KAClC,EAAS,MAAM,MAAM,GAAG,EAAO,EAAE;EAyBjC,IAAM,IAAiB,EAAS,MAAM,YAChC,IAAgB,EAAS,MAAM;EAGrC,AADA,EAAS,MAAM,aAAa,QAC5B,EAAS,MAAM,YAAY;EAE3B,IAAM,IAAS,EAAS,sBAAsB,GACxC,IAAS,EAAO,IAAI,EAAO,MAC3B,IAAS,EAAO,IAAI,EAAO;EAUjC,IARA,EAAS,MAAM,aAAa,GAC5B,EAAS,MAAM,YAAY,IAEvB,MAAW,KAAK,MAAW,OAC7B,EAAS,MAAM,OAAO,GAAG,EAAO,IAAI,EAAO,KAC3C,EAAS,MAAM,MAAM,GAAG,EAAO,IAAI,EAAO,MAGxC,GAAY;GACd,IAAM,IACH,EAAO,eAAe,MAA+B,kBAAkB;GAE1E,EAAS,MAAM,QAAQ,GAAG,KAAK,IAAI,KAAK,IAAI,GAAc,CAAQ,GAAG,CAAS,EAAE;EAClF;CACF;CAEA,SAAS,IAA8B;EACrC,IAAM,IAAM,EAAa,GACnB,IAAW,EAAY;EAI7B,OAFI,CAAC,KAAO,CAAC,UAAuB,CAAC,IAE9B,EAAW,GAAK,GAAU,CAAc;CACjD;CAEA,OAAO;EAAE,UAAU;EAAa,WAAW;EAAc;EAAiB,QAAQ;CAAe;AACnG"}
|
package/dist/counter.cjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/states.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./core/spinner.cjs"),i=require("./shared/prop-bundles.cjs"),a=require("./inputs/counter/counter.css.cjs");let o=require("@vielzeug/ore"),s=require("@vielzeug/ripple");var c=400,l=120,u=`ore-counter`;(0,o.define)(u,{props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,hint:o.prop.string(),label:o.prop.string(),"large-step":o.prop.json(void 0),max:o.prop.json(void 0),min:o.prop.number(0),"quick-steps":o.prop.bool(!1),readonly:o.prop.bool(!1),step:o.prop.number(1),value:o.prop.number(0)},setup(e){let t=(0,o.getHost)(),n=(0,o.useEmit)(),i=(0,o.createId)(`counter-label`),a=(0,o.createId)(`counter-hint`),c=(0,s.signal)(Number(e.value.value)||0);(0,s.watch)(e.value,e=>{let t=Number(e);Number.isFinite(t)&&t!==c.value&&(c.value=t)});let l=(0,s.computed)(()=>!!e.disabled.value),u=(0,s.computed)(()=>!!e.readonly.value),d=(0,s.computed)(()=>!l.value&&!u.value),f=(0,s.computed)(()=>!!e.hint.value),p=(0,s.signal)(!1),m=null,h=r.createSpinnerControl({commit:e=>{if(e==null)return;let r=e-c.value;r!==0&&(c.value=e,t.setAttribute(`value`,String(e)),n(`change`,{delta:r,value:e}),m&&clearTimeout(m),p.value=!1,p.value=!0,m=setTimeout(()=>{p.value=!1,m=null},200))},disabled:l,largeStep:e[`large-step`],max:e.max,min:e.min,parse:()=>c.value,readonly:u,step:e.step}),g=()=>Number(e.step.value)||1,_=()=>Number(e[`large-step`].value)||g()*10,v=(0,s.computed)(()=>!!e[`quick-steps`].value),y=null,b=null,x=!1,S=()=>{y&&clearTimeout(y),b&&clearInterval(b),y=null,b=null},C=(e,t)=>{d.value&&t.button===0&&(S(),x=!1,y=setTimeout(()=>{x=!0,h.incrementBy(e(),t),b=setInterval(()=>{let n=e();if(n>0&&h.atMax()||n<0&&h.atMin()){S();return}h.incrementBy(n,t)},120)},400))},w=(e,t)=>{if(x){x=!1;return}h.incrementBy(e(),t)},T=e=>()=>e*g(),E=e=>()=>e*_(),D=e=>()=>`${e>0?`+`:`−`}${_()}`;(0,o.onCleanup)(()=>{S(),m&&clearTimeout(m)}),(0,o.bind)({attr:{"data-bump":()=>p.value?``:null,size:e.size}});let O=(0,s.computed)(()=>f.value?a:null);return o.html`
|
|
2
|
+
<div class="counter" part="counter" role="group" aria-labelledby="${i}">
|
|
3
|
+
<div class="header" part="header">
|
|
4
|
+
<slot name="icon"></slot>
|
|
5
|
+
<span class="label" part="label" id="${i}">${()=>e.label.value??``}</span>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="controls" part="controls" @keydown="${e=>h.handleKeydown(e)}">
|
|
8
|
+
<button
|
|
9
|
+
class="btn btn-large"
|
|
10
|
+
part="decrement-large-btn"
|
|
11
|
+
type="button"
|
|
12
|
+
tabindex="-1"
|
|
13
|
+
aria-label="${()=>`Decrease ${e.label.value??``} by ${_()}`.trim()}"
|
|
14
|
+
?hidden="${()=>u.value||!v.value}"
|
|
15
|
+
?disabled="${()=>!d.value||h.atMin()}"
|
|
16
|
+
@pointerdown="${e=>C(E(-1),e)}"
|
|
17
|
+
@pointerup="${S}"
|
|
18
|
+
@pointercancel="${S}"
|
|
19
|
+
@pointerleave="${S}"
|
|
20
|
+
@click="${e=>w(E(-1),e)}">
|
|
21
|
+
${D(-1)}
|
|
22
|
+
</button>
|
|
23
|
+
<button
|
|
24
|
+
class="btn"
|
|
25
|
+
part="decrement-btn"
|
|
26
|
+
type="button"
|
|
27
|
+
tabindex="-1"
|
|
28
|
+
aria-label="${()=>`Decrease ${e.label.value??``}`.trim()}"
|
|
29
|
+
?hidden="${u}"
|
|
30
|
+
?disabled="${()=>!d.value||h.atMin()}"
|
|
31
|
+
@pointerdown="${e=>C(T(-1),e)}"
|
|
32
|
+
@pointerup="${S}"
|
|
33
|
+
@pointercancel="${S}"
|
|
34
|
+
@pointerleave="${S}"
|
|
35
|
+
@click="${e=>w(T(-1),e)}">
|
|
36
|
+
<ore-icon name="minus" size="var(--_icon-size)" stroke-width="2.5" aria-hidden="true"></ore-icon>
|
|
37
|
+
</button>
|
|
38
|
+
<output
|
|
39
|
+
class="value"
|
|
40
|
+
part="value"
|
|
41
|
+
role="spinbutton"
|
|
42
|
+
aria-live="polite"
|
|
43
|
+
aria-labelledby="${i}"
|
|
44
|
+
aria-describedby="${O}"
|
|
45
|
+
aria-valuenow="${()=>String(c.value)}"
|
|
46
|
+
aria-valuemin="${()=>e.min.value==null?null:String(e.min.value)}"
|
|
47
|
+
aria-valuemax="${()=>e.max.value==null?null:String(e.max.value)}"
|
|
48
|
+
aria-readonly="${()=>u.value?`true`:null}"
|
|
49
|
+
aria-disabled="${()=>l.value?`true`:null}"
|
|
50
|
+
tabindex="${()=>l.value?`-1`:`0`}">
|
|
51
|
+
${()=>String(c.value)}
|
|
52
|
+
</output>
|
|
53
|
+
<button
|
|
54
|
+
class="btn"
|
|
55
|
+
part="increment-btn"
|
|
56
|
+
type="button"
|
|
57
|
+
tabindex="-1"
|
|
58
|
+
aria-label="${()=>`Increase ${e.label.value??``}`.trim()}"
|
|
59
|
+
?hidden="${u}"
|
|
60
|
+
?disabled="${()=>!d.value||h.atMax()}"
|
|
61
|
+
@pointerdown="${e=>C(T(1),e)}"
|
|
62
|
+
@pointerup="${S}"
|
|
63
|
+
@pointercancel="${S}"
|
|
64
|
+
@pointerleave="${S}"
|
|
65
|
+
@click="${e=>w(T(1),e)}">
|
|
66
|
+
<ore-icon name="plus" size="var(--_icon-size)" stroke-width="2.5" aria-hidden="true"></ore-icon>
|
|
67
|
+
</button>
|
|
68
|
+
<button
|
|
69
|
+
class="btn btn-large"
|
|
70
|
+
part="increment-large-btn"
|
|
71
|
+
type="button"
|
|
72
|
+
tabindex="-1"
|
|
73
|
+
aria-label="${()=>`Increase ${e.label.value??``} by ${_()}`.trim()}"
|
|
74
|
+
?hidden="${()=>u.value||!v.value}"
|
|
75
|
+
?disabled="${()=>!d.value||h.atMax()}"
|
|
76
|
+
@pointerdown="${e=>C(E(1),e)}"
|
|
77
|
+
@pointerup="${S}"
|
|
78
|
+
@pointercancel="${S}"
|
|
79
|
+
@pointerleave="${S}"
|
|
80
|
+
@click="${e=>w(E(1),e)}">
|
|
81
|
+
${D(1)}
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
<slot name="hint">
|
|
85
|
+
<span class="hint" part="hint" id="${a}" ?hidden="${()=>!f.value}">
|
|
86
|
+
${()=>e.hint.value??``}
|
|
87
|
+
</span>
|
|
88
|
+
</slot>
|
|
89
|
+
</div>
|
|
90
|
+
`},shadow:{delegatesFocus:!0},styles:[n.colorThemeMixin,t.disabledStateMixin,e.coarsePointerMixin,e.reducedMotionMixin,a.default]}),exports.COUNTER_HOLD_DELAY_MS=c,exports.COUNTER_HOLD_REPEAT_MS=l,exports.COUNTER_TAG=u;
|
|
91
|
+
//# sourceMappingURL=counter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"counter.cjs","names":[],"sources":["../src/inputs/counter/counter.ts"],"sourcesContent":["import { bind, createId, define, getHost, html, onCleanup, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createSpinnerControl } from '../../core';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport '../../content/icon/icon';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { coarsePointerMixin, colorThemeMixin, disabledStateMixin, reducedMotionMixin } from '../../styles';\nimport componentStyles from './counter.css?inline';\n\nexport type OreCounterChangeDetail = {\n /** Difference between the new and the previous value */\n delta: number;\n /** New value after clamping */\n value: number;\n};\n\nexport type OreCounterEvents = {\n change: OreCounterChangeDetail;\n};\n\n/** Counter props */\nexport type OreCounterProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** One-line timing or usage hint rendered under the controls */\n hint?: string;\n /** Visible label; also names the control for assistive technology */\n label?: string;\n /** Step for Page Up/Down (default: 10 × step) */\n 'large-step'?: number;\n /** Maximum allowed value */\n max?: number;\n /** Minimum allowed value (default: 0) */\n min?: number;\n /** Render an outer pair of −/+ buttons that move by `large-step` */\n 'quick-steps'?: boolean;\n /** Show the value without controls */\n readonly?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Increment/decrement step (default: 1) */\n step?: number;\n /** Current value */\n value?: number;\n};\n\n/** Hold delay before auto-repeat starts, then the repeat interval. */\nexport const COUNTER_HOLD_DELAY_MS = 400;\nexport const COUNTER_HOLD_REPEAT_MS = 120;\n\n/**\n * A large-target tally control for counting things at a glance: `−` / value / `+` with an\n * optional icon, label and hint. Designed for touch and arm's-length use (tabletop trackers,\n * inventories, scoreboards) rather than typed form entry — use `ore-number-input` for that.\n *\n * Holding any button auto-repeats. The value is announced through a live region and is\n * keyboard-operable as a spinbutton (Arrow keys, Home/End, Page Up/Down). Set `quick-steps` to add an\n * outer pair of buttons that move by `large-step`, for values that change in chunks (damage, scores).\n *\n * @element ore-counter\n *\n * @attr {number} value - Current value (default: 0)\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value\n * @attr {number} step - Increment/decrement step (default: 1)\n * @attr {number} large-step - Step for Page Up/Down and the quick-step buttons (default: 10 × step)\n * @attr {boolean} quick-steps - Adds an outer −/+ pair that moves by `large-step`\n * @attr {boolean} disabled - Disables the control\n * @attr {boolean} readonly - Shows the value without the −/+ buttons\n * @attr {string} label - Visible label and accessible name\n * @attr {string} hint - One-line hint under the controls\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n *\n * @fires change - Fired after every accepted change. detail: { value: number, delta: number }\n *\n * @slot icon - Leading icon or token artwork shown before the label\n * @slot hint - Custom hint content (replaces the `hint` attribute)\n *\n * @cssprop --counter-value-size - Font size of the value\n * @cssprop --counter-button-size - Width/height of the −/+ buttons\n * @cssprop --counter-gap - Gap between the buttons and the value\n * @cssprop --counter-bg - Background of the control\n * @cssprop --counter-border-color - Border color of the control\n * @cssprop --counter-radius - Border radius of the control\n * @cssprop --counter-icon-size - Size of the slotted icon\n *\n * @part counter - Root container.\n * @part header - Icon and label row.\n * @part label - Label element.\n * @part controls - Buttons and value row.\n * @part decrement-large-btn - Quick-step decrement button (`quick-steps` only).\n * @part decrement-btn - Decrement button.\n * @part value - Value output.\n * @part increment-btn - Increment button.\n * @part increment-large-btn - Quick-step increment button (`quick-steps` only).\n * @part hint - Hint element.\n *\n * @example\n * ```html\n * <ore-counter label=\"Stamina\" value=\"3\" hint=\"Pay for actions\">\n * <ore-icon slot=\"icon\" name=\"zap\"></ore-icon>\n * </ore-counter>\n * <ore-counter label=\"Score\" value=\"10\" min=\"0\" max=\"99\" size=\"lg\" color=\"primary\"></ore-counter>\n * <ore-counter label=\"Damage\" value=\"12\" large-step=\"5\" quick-steps></ore-counter>\n * ```\n */\nexport const COUNTER_TAG = 'ore-counter' as const;\ndefine<OreCounterProps>(COUNTER_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n hint: prop.string(),\n label: prop.string(),\n 'large-step': prop.json(undefined as number | undefined),\n max: prop.json(undefined as number | undefined),\n min: prop.number(0),\n 'quick-steps': prop.bool(false),\n readonly: prop.bool(false),\n step: prop.number(1),\n value: prop.number(0),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCounterEvents>();\n const labelId = createId('counter-label');\n const hintId = createId('counter-hint');\n\n const current = signal(Number(props.value.value) || 0);\n\n watch(props.value, (next) => {\n const parsed = Number(next);\n\n if (Number.isFinite(parsed) && parsed !== current.value) current.value = parsed;\n });\n\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isReadonly = computed(() => Boolean(props.readonly.value));\n const isInteractive = computed(() => !isDisabled.value && !isReadonly.value);\n const hasHint = computed(() => Boolean(props.hint.value));\n\n // Toggled briefly on every change so the CSS bump animation restarts.\n const bumped = signal(false);\n let bumpTimer: ReturnType<typeof setTimeout> | null = null;\n\n const commit = (next: number | null): void => {\n if (next == null) return;\n\n const delta = next - current.value;\n\n if (delta === 0) return;\n\n current.value = next;\n el.setAttribute('value', String(next));\n emit('change', { delta, value: next });\n\n if (bumpTimer) clearTimeout(bumpTimer);\n bumped.value = false;\n bumped.value = true;\n bumpTimer = setTimeout(() => {\n bumped.value = false;\n bumpTimer = null;\n }, 200);\n };\n\n const spinner = createSpinnerControl({\n commit,\n disabled: isDisabled,\n largeStep: props['large-step'],\n max: props.max,\n min: props.min,\n parse: () => current.value,\n readonly: isReadonly,\n step: props.step,\n });\n\n const stepSize = (): number => Number(props.step.value) || 1;\n const largeStepSize = (): number => Number(props['large-step'].value) || stepSize() * 10;\n const hasQuickSteps = computed(() => Boolean(props['quick-steps'].value));\n\n // ── Press-and-hold auto-repeat ──────────────────────────────────────────\n let holdTimer: ReturnType<typeof setTimeout> | null = null;\n let repeatTimer: ReturnType<typeof setInterval> | null = null;\n let repeated = false;\n\n const stopHold = (): void => {\n if (holdTimer) clearTimeout(holdTimer);\n if (repeatTimer) clearInterval(repeatTimer);\n holdTimer = null;\n repeatTimer = null;\n };\n\n const startHold = (amount: () => number, e: PointerEvent): void => {\n if (!isInteractive.value || e.button !== 0) return;\n\n stopHold();\n repeated = false;\n holdTimer = setTimeout(() => {\n repeated = true;\n spinner.incrementBy(amount(), e);\n repeatTimer = setInterval(() => {\n const direction = amount();\n\n if ((direction > 0 && spinner.atMax()) || (direction < 0 && spinner.atMin())) {\n stopHold();\n\n return;\n }\n\n spinner.incrementBy(direction, e);\n }, COUNTER_HOLD_REPEAT_MS);\n }, COUNTER_HOLD_DELAY_MS);\n };\n\n // The click that ends a long-press must not add one more step on top of the repeats.\n const handleClick = (amount: () => number, e: Event): void => {\n if (repeated) {\n repeated = false;\n\n return;\n }\n\n spinner.incrementBy(amount(), e);\n };\n\n const small = (direction: 1 | -1) => (): number => direction * stepSize();\n const large = (direction: 1 | -1) => (): number => direction * largeStepSize();\n const quickLabel = (direction: 1 | -1) => (): string => `${direction > 0 ? '+' : '−'}${largeStepSize()}`;\n\n onCleanup(() => {\n stopHold();\n if (bumpTimer) clearTimeout(bumpTimer);\n });\n\n bind({\n attr: {\n 'data-bump': () => (bumped.value ? '' : null),\n size: props.size,\n },\n });\n\n const describedBy = computed(() => (hasHint.value ? hintId : null));\n\n return html`\n <div class=\"counter\" part=\"counter\" role=\"group\" aria-labelledby=\"${labelId}\">\n <div class=\"header\" part=\"header\">\n <slot name=\"icon\"></slot>\n <span class=\"label\" part=\"label\" id=\"${labelId}\">${() => props.label.value ?? ''}</span>\n </div>\n <div class=\"controls\" part=\"controls\" @keydown=\"${(e: KeyboardEvent) => spinner.handleKeydown(e)}\">\n <button\n class=\"btn btn-large\"\n part=\"decrement-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(-1), e)}\">\n ${quickLabel(-1)}\n </button>\n <button\n class=\"btn\"\n part=\"decrement-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(-1), e)}\">\n <ore-icon name=\"minus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <output\n class=\"value\"\n part=\"value\"\n role=\"spinbutton\"\n aria-live=\"polite\"\n aria-labelledby=\"${labelId}\"\n aria-describedby=\"${describedBy}\"\n aria-valuenow=\"${() => String(current.value)}\"\n aria-valuemin=\"${() => (props.min.value != null ? String(props.min.value) : null)}\"\n aria-valuemax=\"${() => (props.max.value != null ? String(props.max.value) : null)}\"\n aria-readonly=\"${() => (isReadonly.value ? 'true' : null)}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\">\n ${() => String(current.value)}\n </output>\n <button\n class=\"btn\"\n part=\"increment-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(1), e)}\">\n <ore-icon name=\"plus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <button\n class=\"btn btn-large\"\n part=\"increment-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(1), e)}\">\n ${quickLabel(1)}\n </button>\n </div>\n <slot name=\"hint\">\n <span class=\"hint\" part=\"hint\" id=\"${hintId}\" ?hidden=\"${() => !hasHint.value}\">\n ${() => props.hint.value ?? ''}\n </span>\n </slot>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, disabledStateMixin, coarsePointerMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|
package/dist/counter.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { coarsePointerMixin as e, reducedMotionMixin as t } from "./styles/mixins/accessibility.css.js";
|
|
2
|
+
import { disabledStateMixin as n } from "./styles/mixins/states.css.js";
|
|
3
|
+
import { colorThemeMixin as r } from "./styles/mixins/theme.css.js";
|
|
4
|
+
import { createSpinnerControl as i } from "./core/spinner.js";
|
|
5
|
+
import { disablableBundle as a, sizableBundle as o, themableBundle as s } from "./shared/prop-bundles.js";
|
|
6
|
+
import c from "./inputs/counter/counter.css.js";
|
|
7
|
+
import { bind as l, createId as u, define as d, getHost as f, html as p, onCleanup as m, prop as h, useEmit as g } from "@vielzeug/ore";
|
|
8
|
+
import { computed as _, signal as v, watch as y } from "@vielzeug/ripple";
|
|
9
|
+
//#region src/inputs/counter/counter.ts
|
|
10
|
+
var b = 400, x = 120, S = "ore-counter";
|
|
11
|
+
d(S, {
|
|
12
|
+
props: {
|
|
13
|
+
...s,
|
|
14
|
+
...o,
|
|
15
|
+
...a,
|
|
16
|
+
hint: h.string(),
|
|
17
|
+
label: h.string(),
|
|
18
|
+
"large-step": h.json(void 0),
|
|
19
|
+
max: h.json(void 0),
|
|
20
|
+
min: h.number(0),
|
|
21
|
+
"quick-steps": h.bool(!1),
|
|
22
|
+
readonly: h.bool(!1),
|
|
23
|
+
step: h.number(1),
|
|
24
|
+
value: h.number(0)
|
|
25
|
+
},
|
|
26
|
+
setup(e) {
|
|
27
|
+
let t = f(), n = g(), r = u("counter-label"), a = u("counter-hint"), o = v(Number(e.value.value) || 0);
|
|
28
|
+
y(e.value, (e) => {
|
|
29
|
+
let t = Number(e);
|
|
30
|
+
Number.isFinite(t) && t !== o.value && (o.value = t);
|
|
31
|
+
});
|
|
32
|
+
let s = _(() => !!e.disabled.value), c = _(() => !!e.readonly.value), d = _(() => !s.value && !c.value), h = _(() => !!e.hint.value), b = v(!1), x = null, S = i({
|
|
33
|
+
commit: (e) => {
|
|
34
|
+
if (e == null) return;
|
|
35
|
+
let r = e - o.value;
|
|
36
|
+
r !== 0 && (o.value = e, t.setAttribute("value", String(e)), n("change", {
|
|
37
|
+
delta: r,
|
|
38
|
+
value: e
|
|
39
|
+
}), x && clearTimeout(x), b.value = !1, b.value = !0, x = setTimeout(() => {
|
|
40
|
+
b.value = !1, x = null;
|
|
41
|
+
}, 200));
|
|
42
|
+
},
|
|
43
|
+
disabled: s,
|
|
44
|
+
largeStep: e["large-step"],
|
|
45
|
+
max: e.max,
|
|
46
|
+
min: e.min,
|
|
47
|
+
parse: () => o.value,
|
|
48
|
+
readonly: c,
|
|
49
|
+
step: e.step
|
|
50
|
+
}), C = () => Number(e.step.value) || 1, w = () => Number(e["large-step"].value) || C() * 10, T = _(() => !!e["quick-steps"].value), E = null, D = null, O = !1, k = () => {
|
|
51
|
+
E && clearTimeout(E), D && clearInterval(D), E = null, D = null;
|
|
52
|
+
}, A = (e, t) => {
|
|
53
|
+
d.value && t.button === 0 && (k(), O = !1, E = setTimeout(() => {
|
|
54
|
+
O = !0, S.incrementBy(e(), t), D = setInterval(() => {
|
|
55
|
+
let n = e();
|
|
56
|
+
if (n > 0 && S.atMax() || n < 0 && S.atMin()) {
|
|
57
|
+
k();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
S.incrementBy(n, t);
|
|
61
|
+
}, 120);
|
|
62
|
+
}, 400));
|
|
63
|
+
}, j = (e, t) => {
|
|
64
|
+
if (O) {
|
|
65
|
+
O = !1;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
S.incrementBy(e(), t);
|
|
69
|
+
}, M = (e) => () => e * C(), N = (e) => () => e * w(), P = (e) => () => `${e > 0 ? "+" : "−"}${w()}`;
|
|
70
|
+
m(() => {
|
|
71
|
+
k(), x && clearTimeout(x);
|
|
72
|
+
}), l({ attr: {
|
|
73
|
+
"data-bump": () => b.value ? "" : null,
|
|
74
|
+
size: e.size
|
|
75
|
+
} });
|
|
76
|
+
let F = _(() => h.value ? a : null);
|
|
77
|
+
return p`
|
|
78
|
+
<div class="counter" part="counter" role="group" aria-labelledby="${r}">
|
|
79
|
+
<div class="header" part="header">
|
|
80
|
+
<slot name="icon"></slot>
|
|
81
|
+
<span class="label" part="label" id="${r}">${() => e.label.value ?? ""}</span>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="controls" part="controls" @keydown="${(e) => S.handleKeydown(e)}">
|
|
84
|
+
<button
|
|
85
|
+
class="btn btn-large"
|
|
86
|
+
part="decrement-large-btn"
|
|
87
|
+
type="button"
|
|
88
|
+
tabindex="-1"
|
|
89
|
+
aria-label="${() => `Decrease ${e.label.value ?? ""} by ${w()}`.trim()}"
|
|
90
|
+
?hidden="${() => c.value || !T.value}"
|
|
91
|
+
?disabled="${() => !d.value || S.atMin()}"
|
|
92
|
+
@pointerdown="${(e) => A(N(-1), e)}"
|
|
93
|
+
@pointerup="${k}"
|
|
94
|
+
@pointercancel="${k}"
|
|
95
|
+
@pointerleave="${k}"
|
|
96
|
+
@click="${(e) => j(N(-1), e)}">
|
|
97
|
+
${P(-1)}
|
|
98
|
+
</button>
|
|
99
|
+
<button
|
|
100
|
+
class="btn"
|
|
101
|
+
part="decrement-btn"
|
|
102
|
+
type="button"
|
|
103
|
+
tabindex="-1"
|
|
104
|
+
aria-label="${() => `Decrease ${e.label.value ?? ""}`.trim()}"
|
|
105
|
+
?hidden="${c}"
|
|
106
|
+
?disabled="${() => !d.value || S.atMin()}"
|
|
107
|
+
@pointerdown="${(e) => A(M(-1), e)}"
|
|
108
|
+
@pointerup="${k}"
|
|
109
|
+
@pointercancel="${k}"
|
|
110
|
+
@pointerleave="${k}"
|
|
111
|
+
@click="${(e) => j(M(-1), e)}">
|
|
112
|
+
<ore-icon name="minus" size="var(--_icon-size)" stroke-width="2.5" aria-hidden="true"></ore-icon>
|
|
113
|
+
</button>
|
|
114
|
+
<output
|
|
115
|
+
class="value"
|
|
116
|
+
part="value"
|
|
117
|
+
role="spinbutton"
|
|
118
|
+
aria-live="polite"
|
|
119
|
+
aria-labelledby="${r}"
|
|
120
|
+
aria-describedby="${F}"
|
|
121
|
+
aria-valuenow="${() => String(o.value)}"
|
|
122
|
+
aria-valuemin="${() => e.min.value == null ? null : String(e.min.value)}"
|
|
123
|
+
aria-valuemax="${() => e.max.value == null ? null : String(e.max.value)}"
|
|
124
|
+
aria-readonly="${() => c.value ? "true" : null}"
|
|
125
|
+
aria-disabled="${() => s.value ? "true" : null}"
|
|
126
|
+
tabindex="${() => s.value ? "-1" : "0"}">
|
|
127
|
+
${() => String(o.value)}
|
|
128
|
+
</output>
|
|
129
|
+
<button
|
|
130
|
+
class="btn"
|
|
131
|
+
part="increment-btn"
|
|
132
|
+
type="button"
|
|
133
|
+
tabindex="-1"
|
|
134
|
+
aria-label="${() => `Increase ${e.label.value ?? ""}`.trim()}"
|
|
135
|
+
?hidden="${c}"
|
|
136
|
+
?disabled="${() => !d.value || S.atMax()}"
|
|
137
|
+
@pointerdown="${(e) => A(M(1), e)}"
|
|
138
|
+
@pointerup="${k}"
|
|
139
|
+
@pointercancel="${k}"
|
|
140
|
+
@pointerleave="${k}"
|
|
141
|
+
@click="${(e) => j(M(1), e)}">
|
|
142
|
+
<ore-icon name="plus" size="var(--_icon-size)" stroke-width="2.5" aria-hidden="true"></ore-icon>
|
|
143
|
+
</button>
|
|
144
|
+
<button
|
|
145
|
+
class="btn btn-large"
|
|
146
|
+
part="increment-large-btn"
|
|
147
|
+
type="button"
|
|
148
|
+
tabindex="-1"
|
|
149
|
+
aria-label="${() => `Increase ${e.label.value ?? ""} by ${w()}`.trim()}"
|
|
150
|
+
?hidden="${() => c.value || !T.value}"
|
|
151
|
+
?disabled="${() => !d.value || S.atMax()}"
|
|
152
|
+
@pointerdown="${(e) => A(N(1), e)}"
|
|
153
|
+
@pointerup="${k}"
|
|
154
|
+
@pointercancel="${k}"
|
|
155
|
+
@pointerleave="${k}"
|
|
156
|
+
@click="${(e) => j(N(1), e)}">
|
|
157
|
+
${P(1)}
|
|
158
|
+
</button>
|
|
159
|
+
</div>
|
|
160
|
+
<slot name="hint">
|
|
161
|
+
<span class="hint" part="hint" id="${a}" ?hidden="${() => !h.value}">
|
|
162
|
+
${() => e.hint.value ?? ""}
|
|
163
|
+
</span>
|
|
164
|
+
</slot>
|
|
165
|
+
</div>
|
|
166
|
+
`;
|
|
167
|
+
},
|
|
168
|
+
shadow: { delegatesFocus: !0 },
|
|
169
|
+
styles: [
|
|
170
|
+
r,
|
|
171
|
+
n,
|
|
172
|
+
e,
|
|
173
|
+
t,
|
|
174
|
+
c
|
|
175
|
+
]
|
|
176
|
+
});
|
|
177
|
+
//#endregion
|
|
178
|
+
export { b as COUNTER_HOLD_DELAY_MS, x as COUNTER_HOLD_REPEAT_MS, S as COUNTER_TAG };
|
|
179
|
+
|
|
180
|
+
//# sourceMappingURL=counter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"counter.js","names":[],"sources":["../src/inputs/counter/counter.ts"],"sourcesContent":["import { bind, createId, define, getHost, html, onCleanup, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { createSpinnerControl } from '../../core';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport '../../content/icon/icon';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { coarsePointerMixin, colorThemeMixin, disabledStateMixin, reducedMotionMixin } from '../../styles';\nimport componentStyles from './counter.css?inline';\n\nexport type OreCounterChangeDetail = {\n /** Difference between the new and the previous value */\n delta: number;\n /** New value after clamping */\n value: number;\n};\n\nexport type OreCounterEvents = {\n change: OreCounterChangeDetail;\n};\n\n/** Counter props */\nexport type OreCounterProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** One-line timing or usage hint rendered under the controls */\n hint?: string;\n /** Visible label; also names the control for assistive technology */\n label?: string;\n /** Step for Page Up/Down (default: 10 × step) */\n 'large-step'?: number;\n /** Maximum allowed value */\n max?: number;\n /** Minimum allowed value (default: 0) */\n min?: number;\n /** Render an outer pair of −/+ buttons that move by `large-step` */\n 'quick-steps'?: boolean;\n /** Show the value without controls */\n readonly?: boolean;\n /** Component size */\n size?: ComponentSize;\n /** Increment/decrement step (default: 1) */\n step?: number;\n /** Current value */\n value?: number;\n};\n\n/** Hold delay before auto-repeat starts, then the repeat interval. */\nexport const COUNTER_HOLD_DELAY_MS = 400;\nexport const COUNTER_HOLD_REPEAT_MS = 120;\n\n/**\n * A large-target tally control for counting things at a glance: `−` / value / `+` with an\n * optional icon, label and hint. Designed for touch and arm's-length use (tabletop trackers,\n * inventories, scoreboards) rather than typed form entry — use `ore-number-input` for that.\n *\n * Holding any button auto-repeats. The value is announced through a live region and is\n * keyboard-operable as a spinbutton (Arrow keys, Home/End, Page Up/Down). Set `quick-steps` to add an\n * outer pair of buttons that move by `large-step`, for values that change in chunks (damage, scores).\n *\n * @element ore-counter\n *\n * @attr {number} value - Current value (default: 0)\n * @attr {number} min - Minimum value (default: 0)\n * @attr {number} max - Maximum value\n * @attr {number} step - Increment/decrement step (default: 1)\n * @attr {number} large-step - Step for Page Up/Down and the quick-step buttons (default: 10 × step)\n * @attr {boolean} quick-steps - Adds an outer −/+ pair that moves by `large-step`\n * @attr {boolean} disabled - Disables the control\n * @attr {boolean} readonly - Shows the value without the −/+ buttons\n * @attr {string} label - Visible label and accessible name\n * @attr {string} hint - One-line hint under the controls\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - 'sm' | 'md' | 'lg'\n *\n * @fires change - Fired after every accepted change. detail: { value: number, delta: number }\n *\n * @slot icon - Leading icon or token artwork shown before the label\n * @slot hint - Custom hint content (replaces the `hint` attribute)\n *\n * @cssprop --counter-value-size - Font size of the value\n * @cssprop --counter-button-size - Width/height of the −/+ buttons\n * @cssprop --counter-gap - Gap between the buttons and the value\n * @cssprop --counter-bg - Background of the control\n * @cssprop --counter-border-color - Border color of the control\n * @cssprop --counter-radius - Border radius of the control\n * @cssprop --counter-icon-size - Size of the slotted icon\n *\n * @part counter - Root container.\n * @part header - Icon and label row.\n * @part label - Label element.\n * @part controls - Buttons and value row.\n * @part decrement-large-btn - Quick-step decrement button (`quick-steps` only).\n * @part decrement-btn - Decrement button.\n * @part value - Value output.\n * @part increment-btn - Increment button.\n * @part increment-large-btn - Quick-step increment button (`quick-steps` only).\n * @part hint - Hint element.\n *\n * @example\n * ```html\n * <ore-counter label=\"Stamina\" value=\"3\" hint=\"Pay for actions\">\n * <ore-icon slot=\"icon\" name=\"zap\"></ore-icon>\n * </ore-counter>\n * <ore-counter label=\"Score\" value=\"10\" min=\"0\" max=\"99\" size=\"lg\" color=\"primary\"></ore-counter>\n * <ore-counter label=\"Damage\" value=\"12\" large-step=\"5\" quick-steps></ore-counter>\n * ```\n */\nexport const COUNTER_TAG = 'ore-counter' as const;\ndefine<OreCounterProps>(COUNTER_TAG, {\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n hint: prop.string(),\n label: prop.string(),\n 'large-step': prop.json(undefined as number | undefined),\n max: prop.json(undefined as number | undefined),\n min: prop.number(0),\n 'quick-steps': prop.bool(false),\n readonly: prop.bool(false),\n step: prop.number(1),\n value: prop.number(0),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCounterEvents>();\n const labelId = createId('counter-label');\n const hintId = createId('counter-hint');\n\n const current = signal(Number(props.value.value) || 0);\n\n watch(props.value, (next) => {\n const parsed = Number(next);\n\n if (Number.isFinite(parsed) && parsed !== current.value) current.value = parsed;\n });\n\n const isDisabled = computed(() => Boolean(props.disabled.value));\n const isReadonly = computed(() => Boolean(props.readonly.value));\n const isInteractive = computed(() => !isDisabled.value && !isReadonly.value);\n const hasHint = computed(() => Boolean(props.hint.value));\n\n // Toggled briefly on every change so the CSS bump animation restarts.\n const bumped = signal(false);\n let bumpTimer: ReturnType<typeof setTimeout> | null = null;\n\n const commit = (next: number | null): void => {\n if (next == null) return;\n\n const delta = next - current.value;\n\n if (delta === 0) return;\n\n current.value = next;\n el.setAttribute('value', String(next));\n emit('change', { delta, value: next });\n\n if (bumpTimer) clearTimeout(bumpTimer);\n bumped.value = false;\n bumped.value = true;\n bumpTimer = setTimeout(() => {\n bumped.value = false;\n bumpTimer = null;\n }, 200);\n };\n\n const spinner = createSpinnerControl({\n commit,\n disabled: isDisabled,\n largeStep: props['large-step'],\n max: props.max,\n min: props.min,\n parse: () => current.value,\n readonly: isReadonly,\n step: props.step,\n });\n\n const stepSize = (): number => Number(props.step.value) || 1;\n const largeStepSize = (): number => Number(props['large-step'].value) || stepSize() * 10;\n const hasQuickSteps = computed(() => Boolean(props['quick-steps'].value));\n\n // ── Press-and-hold auto-repeat ──────────────────────────────────────────\n let holdTimer: ReturnType<typeof setTimeout> | null = null;\n let repeatTimer: ReturnType<typeof setInterval> | null = null;\n let repeated = false;\n\n const stopHold = (): void => {\n if (holdTimer) clearTimeout(holdTimer);\n if (repeatTimer) clearInterval(repeatTimer);\n holdTimer = null;\n repeatTimer = null;\n };\n\n const startHold = (amount: () => number, e: PointerEvent): void => {\n if (!isInteractive.value || e.button !== 0) return;\n\n stopHold();\n repeated = false;\n holdTimer = setTimeout(() => {\n repeated = true;\n spinner.incrementBy(amount(), e);\n repeatTimer = setInterval(() => {\n const direction = amount();\n\n if ((direction > 0 && spinner.atMax()) || (direction < 0 && spinner.atMin())) {\n stopHold();\n\n return;\n }\n\n spinner.incrementBy(direction, e);\n }, COUNTER_HOLD_REPEAT_MS);\n }, COUNTER_HOLD_DELAY_MS);\n };\n\n // The click that ends a long-press must not add one more step on top of the repeats.\n const handleClick = (amount: () => number, e: Event): void => {\n if (repeated) {\n repeated = false;\n\n return;\n }\n\n spinner.incrementBy(amount(), e);\n };\n\n const small = (direction: 1 | -1) => (): number => direction * stepSize();\n const large = (direction: 1 | -1) => (): number => direction * largeStepSize();\n const quickLabel = (direction: 1 | -1) => (): string => `${direction > 0 ? '+' : '−'}${largeStepSize()}`;\n\n onCleanup(() => {\n stopHold();\n if (bumpTimer) clearTimeout(bumpTimer);\n });\n\n bind({\n attr: {\n 'data-bump': () => (bumped.value ? '' : null),\n size: props.size,\n },\n });\n\n const describedBy = computed(() => (hasHint.value ? hintId : null));\n\n return html`\n <div class=\"counter\" part=\"counter\" role=\"group\" aria-labelledby=\"${labelId}\">\n <div class=\"header\" part=\"header\">\n <slot name=\"icon\"></slot>\n <span class=\"label\" part=\"label\" id=\"${labelId}\">${() => props.label.value ?? ''}</span>\n </div>\n <div class=\"controls\" part=\"controls\" @keydown=\"${(e: KeyboardEvent) => spinner.handleKeydown(e)}\">\n <button\n class=\"btn btn-large\"\n part=\"decrement-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(-1), e)}\">\n ${quickLabel(-1)}\n </button>\n <button\n class=\"btn\"\n part=\"decrement-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Decrease ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMin()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(-1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(-1), e)}\">\n <ore-icon name=\"minus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <output\n class=\"value\"\n part=\"value\"\n role=\"spinbutton\"\n aria-live=\"polite\"\n aria-labelledby=\"${labelId}\"\n aria-describedby=\"${describedBy}\"\n aria-valuenow=\"${() => String(current.value)}\"\n aria-valuemin=\"${() => (props.min.value != null ? String(props.min.value) : null)}\"\n aria-valuemax=\"${() => (props.max.value != null ? String(props.max.value) : null)}\"\n aria-readonly=\"${() => (isReadonly.value ? 'true' : null)}\"\n aria-disabled=\"${() => (isDisabled.value ? 'true' : null)}\"\n tabindex=\"${() => (isDisabled.value ? '-1' : '0')}\">\n ${() => String(current.value)}\n </output>\n <button\n class=\"btn\"\n part=\"increment-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''}`.trim()}\"\n ?hidden=\"${isReadonly}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(small(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(small(1), e)}\">\n <ore-icon name=\"plus\" size=\"var(--_icon-size)\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n <button\n class=\"btn btn-large\"\n part=\"increment-large-btn\"\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"${() => `Increase ${props.label.value ?? ''} by ${largeStepSize()}`.trim()}\"\n ?hidden=\"${() => isReadonly.value || !hasQuickSteps.value}\"\n ?disabled=\"${() => !isInteractive.value || spinner.atMax()}\"\n @pointerdown=\"${(e: PointerEvent) => startHold(large(1), e)}\"\n @pointerup=\"${stopHold}\"\n @pointercancel=\"${stopHold}\"\n @pointerleave=\"${stopHold}\"\n @click=\"${(e: Event) => handleClick(large(1), e)}\">\n ${quickLabel(1)}\n </button>\n </div>\n <slot name=\"hint\">\n <span class=\"hint\" part=\"hint\" id=\"${hintId}\" ?hidden=\"${() => !hasHint.value}\">\n ${() => props.hint.value ?? ''}\n </span>\n </slot>\n </div>\n `;\n },\n shadow: { delegatesFocus: true },\n styles: [colorThemeMixin, disabledStateMixin, coarsePointerMixin, reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|