ember-primitives 0.59.1 → 0.60.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.
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Structural styles for <Slider>.
3
+ *
4
+ * These handle the annoying parts of building a custom slider on top of
5
+ * native <input type="range"> elements:
6
+ * - stretching an invisible native input over the track (so keyboard,
7
+ * pointer, and assistive-tech behavior all come from the platform)
8
+ * - letting multiple overlapping inputs coexist (multi-thumb / range
9
+ * sliders) by routing pointer events through the native thumb only
10
+ * - vertical orientation (via `writing-mode`, no rotation hacks)
11
+ * - positioning the visual thumb and keeping the active thumb on top
12
+ *
13
+ * Appearance (colors, exact sizes) is left to the consumer.
14
+ *
15
+ * Everything is wrapped in `@layer ember-primitives`, so *any* unlayered
16
+ * consumer rule overrides these -- regardless of specificity or order.
17
+ *
18
+ * This is a plain stylesheet (bundled like any other CSS), so styles are
19
+ * present at first layout -- no waiting on a render cycle. If you render
20
+ * the slider inside a shadow root, bring this stylesheet into that root
21
+ * yourself (e.g. adopt it, or @import it in the shadow tree).
22
+ *
23
+ * Knobs:
24
+ * --ember-primitives__slider__hit-area pointer target size (default 24px)
25
+ * --ember-primitives__slider__thumb-size visual thumb size (default 16px)
26
+ * --ember-primitives__slider__track-thickness rail thickness (default 4px)
27
+ * --ember-primitives__slider__vertical-size length of a vertical slider (default 10rem)
28
+ */
29
+ @layer ember-primitives {
30
+ .ember-primitives__slider {
31
+ position: relative;
32
+ display: flex;
33
+ align-items: center;
34
+ min-height: var(--ember-primitives__slider__hit-area, 24px);
35
+ }
36
+
37
+ .ember-primitives__slider[data-orientation="vertical"] {
38
+ flex-direction: column;
39
+ min-height: 0;
40
+ min-width: var(--ember-primitives__slider__hit-area, 24px);
41
+ height: var(--ember-primitives__slider__vertical-size, 10rem);
42
+ }
43
+
44
+ .ember-primitives__slider__track {
45
+ position: relative;
46
+ flex: 1;
47
+ height: var(--ember-primitives__slider__track-thickness, 4px);
48
+ }
49
+
50
+ .ember-primitives__slider[data-orientation="vertical"] .ember-primitives__slider__track {
51
+ height: auto;
52
+ width: var(--ember-primitives__slider__track-thickness, 4px);
53
+ }
54
+
55
+ .ember-primitives__slider__range {
56
+ position: absolute;
57
+ top: 0;
58
+ bottom: 0;
59
+ }
60
+
61
+ .ember-primitives__slider[data-orientation="vertical"] .ember-primitives__slider__range {
62
+ top: auto;
63
+ bottom: auto;
64
+ left: 0;
65
+ right: 0;
66
+ }
67
+
68
+ /*
69
+ The native input is stretched across the whole track, invisible, and only
70
+ used for interaction. The visual thumb (a sibling) is what users see.
71
+ */
72
+ .ember-primitives__slider__thumb-input {
73
+ position: absolute;
74
+ left: 0;
75
+ top: 50%;
76
+ translate: 0 -50%;
77
+ width: 100%;
78
+ height: var(--ember-primitives__slider__hit-area, 24px);
79
+ margin: 0;
80
+ opacity: 0;
81
+ appearance: none;
82
+ background: transparent;
83
+ cursor: pointer;
84
+ }
85
+
86
+ .ember-primitives__slider__thumb-input:disabled {
87
+ cursor: not-allowed;
88
+ }
89
+
90
+ .ember-primitives__slider__thumb-input[data-active] {
91
+ z-index: 2;
92
+ }
93
+
94
+ /*
95
+ Size the (invisible) native thumb to the hit area, so grabbing "the thumb"
96
+ feels right. These cannot be comma-combined: an unknown pseudo-element
97
+ invalidates the whole selector list in the other engine.
98
+ */
99
+ .ember-primitives__slider__thumb-input::-webkit-slider-thumb {
100
+ appearance: none;
101
+ width: var(--ember-primitives__slider__hit-area, 24px);
102
+ height: var(--ember-primitives__slider__hit-area, 24px);
103
+ }
104
+
105
+ .ember-primitives__slider__thumb-input::-moz-range-thumb {
106
+ border: none;
107
+ width: var(--ember-primitives__slider__hit-area, 24px);
108
+ height: var(--ember-primitives__slider__hit-area, 24px);
109
+ }
110
+
111
+ /*
112
+ Multi-thumb sliders overlap multiple full-width range inputs. If the
113
+ inputs themselves receive pointer events, the top-most input steals
114
+ clicks/drags from the other thumbs. Disable pointer events on the
115
+ track-sized input and re-enable them on the native thumb only.
116
+
117
+ Single-thumb sliders keep the whole input interactive, so clicking
118
+ anywhere on the track jumps to that value.
119
+ */
120
+ .ember-primitives__slider[data-multi] .ember-primitives__slider__thumb-input {
121
+ pointer-events: none;
122
+ }
123
+
124
+ .ember-primitives__slider[data-multi]
125
+ .ember-primitives__slider__thumb-input::-webkit-slider-thumb {
126
+ pointer-events: auto;
127
+ }
128
+
129
+ .ember-primitives__slider[data-multi] .ember-primitives__slider__thumb-input::-moz-range-thumb {
130
+ pointer-events: auto;
131
+ }
132
+
133
+ /*
134
+ Vertical orientation: modern engines render a native vertical range input
135
+ with `writing-mode`. `direction: rtl` puts the minimum at the bottom.
136
+ */
137
+ .ember-primitives__slider[data-orientation="vertical"] .ember-primitives__slider__thumb-input {
138
+ writing-mode: vertical-lr;
139
+ direction: rtl;
140
+ top: 0;
141
+ left: 50%;
142
+ translate: -50% 0;
143
+ width: var(--ember-primitives__slider__hit-area, 24px);
144
+ height: 100%;
145
+ }
146
+
147
+ /*
148
+ The visual thumb. The component positions it with an inline
149
+ `left`/`bottom` percentage; centering uses the `translate` property
150
+ (not `transform`) so consumer hover/active effects like
151
+ `transform: scale(1.4)` or `scale: 1.4` compose with it instead of
152
+ clobbering it.
153
+ */
154
+ .ember-primitives__slider__thumb {
155
+ position: absolute;
156
+ top: 50%;
157
+ translate: -50% -50%;
158
+ pointer-events: none;
159
+ z-index: 1;
160
+ }
161
+
162
+ .ember-primitives__slider__thumb[data-active] {
163
+ z-index: 3;
164
+ }
165
+
166
+ .ember-primitives__slider[data-orientation="vertical"] .ember-primitives__slider__thumb {
167
+ top: auto;
168
+ left: 50%;
169
+ translate: -50% 50%;
170
+ }
171
+
172
+ /*
173
+ Default appearance -- zero specificity via :where(), so any consumer rule
174
+ wins (even a layered one). Colors derive from currentColor so the slider
175
+ adapts to its context.
176
+ */
177
+ :where(.ember-primitives__slider__track) {
178
+ border-radius: calc(var(--ember-primitives__slider__track-thickness, 4px) / 2);
179
+ background: color-mix(in srgb, currentColor 20%, transparent);
180
+ }
181
+
182
+ :where(.ember-primitives__slider__range) {
183
+ border-radius: inherit;
184
+ background: currentColor;
185
+ }
186
+
187
+ :where(.ember-primitives__slider__thumb) {
188
+ width: var(--ember-primitives__slider__thumb-size, 16px);
189
+ height: var(--ember-primitives__slider__thumb-size, 16px);
190
+ border-radius: 50%;
191
+ background: currentColor;
192
+ }
193
+
194
+ :where(.ember-primitives__slider__thumb[data-disabled]) {
195
+ opacity: 0.5;
196
+ }
197
+
198
+ /* Keyboard focus is on the (invisible) input; reflect it on the visual thumb. */
199
+ :where(.ember-primitives__slider__thumb-input:focus-visible + .ember-primitives__slider__thumb) {
200
+ outline: 2px solid currentColor;
201
+ outline-offset: 2px;
202
+ }
203
+ }
@@ -0,0 +1,88 @@
1
+ import "./slider.css"
2
+ import Component from '@glimmer/component';
3
+ import { hash } from '@ember/helper';
4
+ import { on } from '@ember/modifier';
5
+ import { SliderStore } from './slider/store.js';
6
+ import { precompileTemplate } from '@ember/template-compilation';
7
+ import { setComponentTemplate } from '@ember/component';
8
+ import templateOnly from '@ember/component/template-only';
9
+
10
+ ;
11
+
12
+ const Track = setComponentTemplate(precompileTemplate("<span ...attributes class=\"ember-primitives__slider__track\">\n {{yield}}\n</span>", {
13
+ strictMode: true
14
+ }), templateOnly());
15
+ const Range = setComponentTemplate(precompileTemplate("<span ...attributes class=\"ember-primitives__slider__range\" style={{@rangeStyle}} />", {
16
+ strictMode: true
17
+ }), templateOnly());
18
+ class ThumbComponent extends Component {
19
+ get index() {
20
+ return this.args.thumb?.index ?? this.args.index ?? 0;
21
+ }
22
+ get value() {
23
+ // When using tick values, the `input` needs the internal index.
24
+ return this.args.thumb?.inputValue ?? this.args.value ?? this.args.store.internalMin;
25
+ }
26
+ get isActive() {
27
+ return this.args.store.activeThumbIndex === this.index;
28
+ }
29
+ get positionStyle() {
30
+ const percent = this.args.thumb?.percent ?? this.args.store.thumbPercents[this.index] ?? 0;
31
+ return this.args.store.thumbPositionStyle(percent);
32
+ }
33
+ readValue(event) {
34
+ // In docs live previews the component may run in an iframe/shadow realm,
35
+ // where `instanceof HTMLInputElement` is not reliable. `currentTarget` is
36
+ // the element the handler is attached to.
37
+ const el = event.currentTarget;
38
+ const raw = el?.value;
39
+ const parsed = raw === undefined ? NaN : Number.parseFloat(raw);
40
+ return Number.isFinite(parsed) ? parsed : this.value;
41
+ }
42
+ onInput = event => {
43
+ this.args.store.handleThumbActivate(this.index);
44
+ this.args.store.handleThumbInput(this.index, this.readValue(event));
45
+ };
46
+ onChange = event => {
47
+ this.args.store.handleThumbActivate(this.index);
48
+ this.args.store.handleThumbChange(this.index, this.readValue(event));
49
+ };
50
+ onPointerUp = () => {
51
+ this.args.store.handleThumbActivate(this.index);
52
+ };
53
+ onGotPointerCapture = () => {
54
+ this.args.store.handleThumbActivate(this.index);
55
+ };
56
+ onFocus = () => {
57
+ this.args.store.handleThumbActivate(this.index);
58
+ };
59
+ static {
60
+ setComponentTemplate(precompileTemplate("<input ...attributes class=\"ember-primitives__slider__thumb-input\" type=\"range\" min={{@store.internalMin}} max={{@store.internalMax}} step={{@store.internalStep}} value={{this.value}} disabled={{@store.disabled}} data-active={{if this.isActive \"\"}} {{on \"gotpointercapture\" this.onGotPointerCapture}} {{on \"pointerup\" this.onPointerUp}} {{on \"focus\" this.onFocus}} {{on \"input\" this.onInput}} {{on \"change\" this.onChange}} />\n<span class=\"ember-primitives__slider__thumb\" style={{this.positionStyle}} data-active={{if this.isActive \"\"}} data-disabled={{if @store.disabled \"\"}} aria-hidden=\"true\">{{yield}}</span>", {
61
+ strictMode: true,
62
+ scope: () => ({
63
+ on
64
+ })
65
+ }), this);
66
+ }
67
+ }
68
+ class Slider extends Component {
69
+ store;
70
+ constructor(owner, args) {
71
+ super(owner, args);
72
+ this.store = new SliderStore(() => this.args);
73
+ }
74
+ static {
75
+ setComponentTemplate(precompileTemplate("<span ...attributes class=\"ember-primitives__slider\" data-orientation={{this.store.orientation}} data-disabled={{if this.store.disabled \"\"}} data-multi={{if this.store.isMulti \"\"}}>\n {{#if (has-block)}}\n {{yield (hash Track=Track Range=(component Range rangeStyle=this.store.rangeStyle) Thumb=(component ThumbComponent store=this.store) values=this.store.values tickValues=this.store.tickValues thumbs=this.store.thumbs min=this.store.min max=this.store.max step=this.store.step)}}\n {{else}}\n <Track>\n <Range @rangeStyle={{this.store.rangeStyle}} />\n\n {{#each this.store.thumbs as |thumb|}}\n <ThumbComponent @store={{this.store}} @thumb={{thumb}} aria-label={{this.store.defaultThumbLabel thumb.index}} />\n {{/each}}\n </Track>\n {{/if}}\n</span>", {
76
+ strictMode: true,
77
+ scope: () => ({
78
+ hash,
79
+ Track,
80
+ Range,
81
+ ThumbComponent
82
+ })
83
+ }), this);
84
+ }
85
+ }
86
+
87
+ export { Slider, Slider as default };
88
+ //# sourceMappingURL=slider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slider.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ export { R as Rating } from './rating-BrIiwDLw.js';
20
20
  export { Scroller } from './components/scroller.js';
21
21
  export { Separator } from './components/separator.js';
22
22
  export { Shadowed } from './components/shadowed.js';
23
+ export { Slider } from './components/slider.js';
23
24
  export { Switch } from './components/switch.js';
24
25
  export { Toggle } from './components/toggle.js';
25
26
  export { ToggleGroup } from './components/toggle-group.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * DANGER: this is a *barrel file*\n *\n * It forces the whole library to be loaded and all dependencies.\n *\n * If you have a small app, you probably don't want to import from here -- instead import from each sub-path.\n */\nimport { importSync, isDevelopingApp, macroCondition } from '@embroider/macros';\n\nif (macroCondition(isDevelopingApp())) {\n importSync('./components/violations.css');\n}\n\nexport { Accordion } from './components/accordion.gts';\nexport type {\n AccordionContentExternalSignature,\n AccordionHeaderExternalSignature,\n AccordionItemExternalSignature,\n AccordionTriggerExternalSignature,\n} from './components/accordion/public.ts';\nexport { Avatar } from './components/avatar.gts';\nexport { Breadcrumb } from './components/breadcrumb.gts';\nexport { Dialog, Dialog as Modal } from './components/dialog.gts';\nexport { Drawer } from './components/drawer.gts';\nexport { ExternalLink } from './components/external-link.gts';\nexport { Form } from './components/form.gts';\nexport { IncrementalEach } from './components/incremental-each.gts';\nexport { Key, KeyCombo } from './components/keys.gts';\nexport { StickyFooter } from './components/layout/sticky-footer.gts';\nexport { Link } from './components/link.gts';\nexport { Menu } from './components/menu.gts';\nexport { OTP, OTPInput } from './components/one-time-password.gts';\nexport { Popover } from './components/popover.gts';\nexport { Portal } from './components/portal.gts';\nexport { PortalTargets } from './components/portal-targets.gts';\nexport { TARGETS as PORTALS } from './components/portal-targets.gts';\nexport { Progress } from './components/progress.gts';\nexport { Rating } from './components/rating.gts';\nexport { Scroller } from './components/scroller.gts';\nexport { Separator } from './components/separator.gts';\nexport { Shadowed } from './components/shadowed.gts';\nexport { Switch } from './components/switch.gts';\nexport { Toggle } from './components/toggle.gts';\nexport { ToggleGroup } from './components/toggle-group.gts';\nexport { VisuallyHidden } from './components/visually-hidden.gts';\nexport { Zoetrope } from './components/zoetrope.ts';\nexport * from './helpers.ts';\n"],"names":["macroCondition","isDevelopingApp","importSync"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA,IAAIA,cAAc,CAACC,eAAe,EAAE,CAAC,EAAE;EACrCC,UAAU,CAAC,6BAA6B,CAAC;AAC3C"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * DANGER: this is a *barrel file*\n *\n * It forces the whole library to be loaded and all dependencies.\n *\n * If you have a small app, you probably don't want to import from here -- instead import from each sub-path.\n */\nimport { importSync, isDevelopingApp, macroCondition } from '@embroider/macros';\n\nif (macroCondition(isDevelopingApp())) {\n importSync('./components/violations.css');\n}\n\nexport { Accordion } from './components/accordion.gts';\nexport type {\n AccordionContentExternalSignature,\n AccordionHeaderExternalSignature,\n AccordionItemExternalSignature,\n AccordionTriggerExternalSignature,\n} from './components/accordion/public.ts';\nexport { Avatar } from './components/avatar.gts';\nexport { Breadcrumb } from './components/breadcrumb.gts';\nexport { Dialog, Dialog as Modal } from './components/dialog.gts';\nexport { Drawer } from './components/drawer.gts';\nexport { ExternalLink } from './components/external-link.gts';\nexport { Form } from './components/form.gts';\nexport { IncrementalEach } from './components/incremental-each.gts';\nexport { Key, KeyCombo } from './components/keys.gts';\nexport { StickyFooter } from './components/layout/sticky-footer.gts';\nexport { Link } from './components/link.gts';\nexport { Menu } from './components/menu.gts';\nexport { OTP, OTPInput } from './components/one-time-password.gts';\nexport { Popover } from './components/popover.gts';\nexport { Portal } from './components/portal.gts';\nexport { PortalTargets } from './components/portal-targets.gts';\nexport { TARGETS as PORTALS } from './components/portal-targets.gts';\nexport { Progress } from './components/progress.gts';\nexport { Rating } from './components/rating.gts';\nexport { Scroller } from './components/scroller.gts';\nexport { Separator } from './components/separator.gts';\nexport { Shadowed } from './components/shadowed.gts';\nexport { Slider } from './components/slider.gts';\nexport { Switch } from './components/switch.gts';\nexport { Toggle } from './components/toggle.gts';\nexport { ToggleGroup } from './components/toggle-group.gts';\nexport { VisuallyHidden } from './components/visually-hidden.gts';\nexport { Zoetrope } from './components/zoetrope.ts';\nexport * from './helpers.ts';\n"],"names":["macroCondition","isDevelopingApp","importSync"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA,IAAIA,cAAc,CAACC,eAAe,EAAE,CAAC,EAAE;EACrCC,UAAU,CAAC,6BAA6B,CAAC;AAC3C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-primitives",
3
- "version": "0.59.1",
3
+ "version": "0.60.1",
4
4
  "description": "Making apps easier to build",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -20,7 +20,7 @@
20
20
  "@babel/runtime": "^7.28.6",
21
21
  "@embroider/addon-shim": "^1.10.2",
22
22
  "@floating-ui/dom": "^1.7.5",
23
- "decorator-transforms": "2.3.1",
23
+ "decorator-transforms": "2.3.2",
24
24
  "ember-element-helper": "^0.8.8",
25
25
  "form-data-utils": "^0.6.0",
26
26
  "reactiveweb": "^1.9.1",
@@ -28,7 +28,7 @@
28
28
  "tabster": "^8.7.0",
29
29
  "tracked-built-ins": "^4.1.0",
30
30
  "tracked-toolbox": "^3.0.0",
31
- "which-heading-do-i-need": "0.2.7"
31
+ "which-heading-do-i-need": "0.4.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@arethetypeswrong/cli": "^0.18.0",
@@ -61,6 +61,7 @@
61
61
  "ember-source": "^6.10.1",
62
62
  "ember-template-lint": "^7.9.3",
63
63
  "eslint": "^9.39.2",
64
+ "eslint-plugin-compat": "^7.0.2",
64
65
  "execa": "^9.6.0",
65
66
  "fix-bad-declaration-output": "^1.1.5",
66
67
  "prettier": "^3.8.1",
@@ -121,6 +122,12 @@
121
122
  "ember-modifier": ">= 4.1.0",
122
123
  "ember-resources": ">= 6.1.0"
123
124
  },
125
+ "browserslist": [
126
+ "last 2 Chrome versions",
127
+ "last 2 Firefox versions",
128
+ "last 2 Safari versions",
129
+ "last 2 Edge versions"
130
+ ],
124
131
  "peerDependenciesMeta": {
125
132
  "@ember/test-helpers": {
126
133
  "optional": true