ember-primitives 0.48.2 → 0.50.0

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.
Files changed (218) hide show
  1. package/bin/index.mjs +271 -0
  2. package/declarations/components/portal.d.ts.map +1 -1
  3. package/declarations/components/rating/public-types.d.ts +0 -4
  4. package/declarations/components/rating/public-types.d.ts.map +1 -1
  5. package/declarations/components/rating/rating.d.ts +9 -1
  6. package/declarations/components/rating/rating.d.ts.map +1 -1
  7. package/declarations/components/rating/stars.d.ts.map +1 -1
  8. package/declarations/components/rating/state.d.ts +4 -0
  9. package/declarations/components/rating/state.d.ts.map +1 -1
  10. package/declarations/components/rating/utils.d.ts +0 -1
  11. package/declarations/components/rating/utils.d.ts.map +1 -1
  12. package/declarations/tabster.d.ts.map +1 -1
  13. package/declarations/utils.d.ts.map +1 -1
  14. package/declarations/viewport/in-viewport.d.ts +70 -0
  15. package/declarations/viewport/in-viewport.d.ts.map +1 -0
  16. package/declarations/viewport/viewport.d.ts +59 -0
  17. package/declarations/viewport/viewport.d.ts.map +1 -0
  18. package/declarations/viewport.d.ts +3 -0
  19. package/declarations/viewport.d.ts.map +1 -0
  20. package/dist/-private.js +0 -1
  21. package/dist/-private.js.map +1 -1
  22. package/dist/color-scheme.js +0 -1
  23. package/dist/color-scheme.js.map +1 -1
  24. package/dist/{component-Bs3N-G9z.js → component-BXy_iafw.js} +2 -3
  25. package/dist/component-BXy_iafw.js.map +1 -0
  26. package/dist/components/accordion.js +5 -6
  27. package/dist/components/accordion.js.map +1 -1
  28. package/dist/components/avatar.js +3 -4
  29. package/dist/components/avatar.js.map +1 -1
  30. package/dist/components/dialog.js +2 -3
  31. package/dist/components/dialog.js.map +1 -1
  32. package/dist/components/external-link.js +1 -2
  33. package/dist/components/external-link.js.map +1 -1
  34. package/dist/components/form.js +1 -2
  35. package/dist/components/form.js.map +1 -1
  36. package/dist/components/heading.js +1 -2
  37. package/dist/components/heading.js.map +1 -1
  38. package/dist/components/keys.js +2 -3
  39. package/dist/components/keys.js.map +1 -1
  40. package/dist/components/layout/hero.js +1 -1
  41. package/dist/components/layout/sticky-footer.js +1 -1
  42. package/dist/components/link.js +1 -2
  43. package/dist/components/link.js.map +1 -1
  44. package/dist/components/menu.js +6 -8
  45. package/dist/components/menu.js.map +1 -1
  46. package/dist/components/one-time-password.js +1 -2
  47. package/dist/components/popover.js +3 -4
  48. package/dist/components/popover.js.map +1 -1
  49. package/dist/components/portal-targets.js +2 -3
  50. package/dist/components/portal-targets.js.map +1 -1
  51. package/dist/components/portal.js +3 -7
  52. package/dist/components/portal.js.map +1 -1
  53. package/dist/components/progress.js +2 -3
  54. package/dist/components/progress.js.map +1 -1
  55. package/dist/components/rating.js +1 -2
  56. package/dist/components/scroller.js +1 -2
  57. package/dist/components/scroller.js.map +1 -1
  58. package/dist/components/shadowed.js +2 -3
  59. package/dist/components/shadowed.js.map +1 -1
  60. package/dist/components/switch.js +5 -6
  61. package/dist/components/switch.js.map +1 -1
  62. package/dist/components/tabs.js +6 -7
  63. package/dist/components/tabs.js.map +1 -1
  64. package/dist/components/toggle-group.js +3 -4
  65. package/dist/components/toggle-group.js.map +1 -1
  66. package/dist/components/toggle.js +2 -3
  67. package/dist/components/toggle.js.map +1 -1
  68. package/dist/components/visually-hidden.js +1 -2
  69. package/dist/components/visually-hidden.js.map +1 -1
  70. package/dist/components/zoetrope.js +1 -2
  71. package/dist/dom-context.js +2 -3
  72. package/dist/dom-context.js.map +1 -1
  73. package/dist/floating-ui.js +1 -2
  74. package/dist/head.js +1 -2
  75. package/dist/head.js.map +1 -1
  76. package/dist/helpers/body-class.js +0 -1
  77. package/dist/helpers/body-class.js.map +1 -1
  78. package/dist/helpers/link.js +0 -1
  79. package/dist/helpers/link.js.map +1 -1
  80. package/dist/helpers/service.js +0 -1
  81. package/dist/helpers/service.js.map +1 -1
  82. package/dist/helpers.js +0 -1
  83. package/dist/helpers.js.map +1 -1
  84. package/dist/iframe.js +0 -1
  85. package/dist/iframe.js.map +1 -1
  86. package/dist/{index-DKE67I8L.js → index-gRO4Cvlf.js} +2 -2
  87. package/dist/index-gRO4Cvlf.js.map +1 -0
  88. package/dist/index.js +3 -4
  89. package/dist/index.js.map +1 -1
  90. package/dist/load.js +0 -1
  91. package/dist/load.js.map +1 -1
  92. package/dist/narrowing.js +0 -1
  93. package/dist/narrowing.js.map +1 -1
  94. package/dist/on-resize.js +0 -1
  95. package/dist/on-resize.js.map +1 -1
  96. package/dist/{otp-C6hCCXKx.js → otp-7rz1PWP0.js} +6 -7
  97. package/dist/otp-7rz1PWP0.js.map +1 -0
  98. package/dist/proper-links.js +0 -1
  99. package/dist/proper-links.js.map +1 -1
  100. package/dist/qp.js +0 -1
  101. package/dist/qp.js.map +1 -1
  102. package/dist/rating-BrIiwDLw.js +152 -0
  103. package/dist/rating-BrIiwDLw.js.map +1 -0
  104. package/dist/resize-observer.js +0 -1
  105. package/dist/resize-observer.js.map +1 -1
  106. package/dist/service.js +0 -1
  107. package/dist/service.js.map +1 -1
  108. package/dist/store.js +0 -1
  109. package/dist/store.js.map +1 -1
  110. package/dist/styles.css.js +0 -1
  111. package/dist/tabster.js +0 -1
  112. package/dist/tabster.js.map +1 -1
  113. package/dist/test-support.js +0 -1
  114. package/dist/test-support.js.map +1 -1
  115. package/dist/{utils-C5796IKA.js → utils-D0v9WKmV.js} +1 -2
  116. package/dist/utils-D0v9WKmV.js.map +1 -0
  117. package/dist/utils.js +4 -1
  118. package/dist/utils.js.map +1 -1
  119. package/dist/viewport/in-viewport.js +82 -0
  120. package/dist/viewport/in-viewport.js.map +1 -0
  121. package/dist/viewport/viewport.js +92 -0
  122. package/dist/viewport/viewport.js.map +1 -0
  123. package/dist/viewport.js +3 -0
  124. package/dist/viewport.js.map +1 -0
  125. package/package.json +24 -20
  126. package/src/-private.ts +4 -0
  127. package/src/color-scheme.ts +165 -0
  128. package/src/components/-private/typed-elements.gts +13 -0
  129. package/src/components/-private/utils.ts +16 -0
  130. package/src/components/accordion/content.gts +34 -0
  131. package/src/components/accordion/header.gts +36 -0
  132. package/src/components/accordion/item.gts +55 -0
  133. package/src/components/accordion/public.ts +64 -0
  134. package/src/components/accordion/trigger.gts +32 -0
  135. package/src/components/accordion.gts +195 -0
  136. package/src/components/avatar.gts +108 -0
  137. package/src/components/dialog.gts +234 -0
  138. package/src/components/external-link.gts +14 -0
  139. package/src/components/form.gts +75 -0
  140. package/src/components/heading.gts +36 -0
  141. package/src/components/keys.gts +53 -0
  142. package/src/components/layout/hero.css +5 -0
  143. package/src/components/layout/hero.gts +17 -0
  144. package/src/components/layout/sticky-footer.css +9 -0
  145. package/src/components/layout/sticky-footer.gts +40 -0
  146. package/src/components/link.gts +172 -0
  147. package/src/components/menu.gts +373 -0
  148. package/src/components/one-time-password/buttons.gts +31 -0
  149. package/src/components/one-time-password/input.gts +198 -0
  150. package/src/components/one-time-password/otp.gts +130 -0
  151. package/src/components/one-time-password/utils.ts +201 -0
  152. package/src/components/one-time-password.gts +2 -0
  153. package/src/components/popover.gts +248 -0
  154. package/src/components/portal-targets.gts +136 -0
  155. package/src/components/portal.gts +194 -0
  156. package/src/components/progress.gts +154 -0
  157. package/src/components/rating/public-types.ts +44 -0
  158. package/src/components/rating/range.gts +22 -0
  159. package/src/components/rating/rating.gts +228 -0
  160. package/src/components/rating/stars.gts +60 -0
  161. package/src/components/rating/state.gts +144 -0
  162. package/src/components/rating/utils.ts +7 -0
  163. package/src/components/rating.gts +5 -0
  164. package/src/components/scroller.gts +179 -0
  165. package/src/components/shadowed.gts +110 -0
  166. package/src/components/switch.gts +103 -0
  167. package/src/components/tabs.gts +519 -0
  168. package/src/components/toggle-group.gts +265 -0
  169. package/src/components/toggle.gts +81 -0
  170. package/src/components/violations.css +105 -0
  171. package/src/components/violations.css.ts +1 -0
  172. package/src/components/visually-hidden.css +14 -0
  173. package/src/components/visually-hidden.gts +15 -0
  174. package/src/components/zoetrope/index.gts +358 -0
  175. package/src/components/zoetrope/styles.css +40 -0
  176. package/src/components/zoetrope/types.ts +65 -0
  177. package/src/components/zoetrope.ts +3 -0
  178. package/src/dom-context.gts +245 -0
  179. package/src/floating-ui/component.gts +186 -0
  180. package/src/floating-ui/middleware.ts +13 -0
  181. package/src/floating-ui/modifier.ts +183 -0
  182. package/src/floating-ui.ts +2 -0
  183. package/src/head.gts +37 -0
  184. package/src/helpers/body-class.ts +94 -0
  185. package/src/helpers/link.ts +125 -0
  186. package/src/helpers/service.ts +25 -0
  187. package/src/helpers.ts +2 -0
  188. package/src/iframe.ts +31 -0
  189. package/src/index.ts +43 -0
  190. package/src/load.gts +77 -0
  191. package/src/narrowing.ts +7 -0
  192. package/src/on-resize.ts +64 -0
  193. package/src/proper-links.ts +140 -0
  194. package/src/qp.ts +107 -0
  195. package/src/resize-observer.ts +132 -0
  196. package/src/service.ts +103 -0
  197. package/src/store.ts +72 -0
  198. package/src/styles.css.ts +5 -0
  199. package/src/tabster.ts +54 -0
  200. package/src/template-registry.ts +44 -0
  201. package/src/test-support/a11y.ts +50 -0
  202. package/src/test-support/dom.ts +112 -0
  203. package/src/test-support/otp.ts +64 -0
  204. package/src/test-support/rating.ts +144 -0
  205. package/src/test-support/routing.ts +62 -0
  206. package/src/test-support/zoetrope.ts +51 -0
  207. package/src/test-support.gts +6 -0
  208. package/src/type-utils.ts +1 -0
  209. package/src/utils.ts +75 -0
  210. package/src/viewport/in-viewport.gts +128 -0
  211. package/src/viewport/viewport.ts +122 -0
  212. package/src/viewport.ts +2 -0
  213. package/dist/component-Bs3N-G9z.js.map +0 -1
  214. package/dist/index-DKE67I8L.js.map +0 -1
  215. package/dist/otp-C6hCCXKx.js.map +0 -1
  216. package/dist/rating-D052JWRa.js +0 -149
  217. package/dist/rating-D052JWRa.js.map +0 -1
  218. package/dist/utils-C5796IKA.js.map +0 -1
@@ -0,0 +1,228 @@
1
+ import Component from "@glimmer/component";
2
+ import { hash } from "@ember/helper";
3
+ import { on } from "@ember/modifier";
4
+
5
+ import { uniqueId } from "../../utils.ts";
6
+ import { RatingRange } from "./range.gts";
7
+ import { Stars } from "./stars.gts";
8
+ import { RatingState } from "./state.gts";
9
+
10
+ import type { ComponentIcons, StringIcons } from "./public-types.ts";
11
+ import type { WithBoundArgs } from "@glint/template";
12
+
13
+ export interface Signature {
14
+ /*
15
+ * The element all passed attributes / modifiers are applied to.
16
+ *
17
+ * This is a `<fieldset>`, becaues the rating elements are
18
+ * powered by a group of radio buttons.
19
+ */
20
+ Element: HTMLFieldSetElement;
21
+ Args: (ComponentIcons | StringIcons) & {
22
+ /**
23
+ * The number of stars/whichever-icon to show
24
+ *
25
+ * Defaults to 5
26
+ */
27
+ max?: number;
28
+
29
+ /**
30
+ * The current number of stars/whichever-icon to show as selected
31
+ *
32
+ * Defaults to 0
33
+ */
34
+ value?: number;
35
+
36
+ /**
37
+ * When generating the radio inputs, this changes what value of rating each radio
38
+ * input will be incremented by.
39
+ *
40
+ * e.g.: Set to 0.5 for half-star ratings.
41
+ *
42
+ * Defaults to 1
43
+ */
44
+ step?: number;
45
+
46
+ /**
47
+ * Prevents click events on the icons and sets aria-readonly.
48
+ *
49
+ * Also sets data-readonly=true on the wrapping element
50
+ */
51
+ readonly?: boolean;
52
+
53
+ /**
54
+ * Toggles the ability to interact with the rating component.
55
+ * When `true` (the default), the Rating component can be as a form input
56
+ * to gather user feedback.
57
+ *
58
+ * When false, only the `@value` will be shown, and it cannot be changed.
59
+ */
60
+ interactive?: boolean;
61
+
62
+ /**
63
+ * Callback when the selected rating changes.
64
+ * Can include half-ratings if the iconHalf argument is passed.
65
+ */
66
+ onChange?: (value: number) => void;
67
+ };
68
+
69
+ Blocks: {
70
+ default: [
71
+ rating: {
72
+ /**
73
+ * The maximum rating
74
+ */
75
+ max: number;
76
+ /**
77
+ * The maxium rating
78
+ */
79
+ total: number;
80
+ /**
81
+ * The current rating
82
+ */
83
+ value: number;
84
+ /**
85
+ * The name shared by the field group
86
+ */
87
+ name: string;
88
+ /**
89
+ * If the rating can be changed
90
+ */
91
+ isReadonly: boolean;
92
+ /**
93
+ * If the rating can be changed
94
+ */
95
+ isChangeable: boolean;
96
+ /**
97
+ * The stars / items radio group
98
+ */
99
+ Stars: WithBoundArgs<
100
+ typeof Stars,
101
+ "stars" | "icon" | "isReadonly" | "name" | "total" | "currentValue"
102
+ >;
103
+ /**
104
+ * Input range for adjusting the rating via fractional means
105
+ */
106
+ Range: WithBoundArgs<typeof RatingRange, "max" | "value" | "name" | "handleChange">;
107
+ },
108
+ ];
109
+ label: [
110
+ state: {
111
+ /**
112
+ * The current rating
113
+ */
114
+ value: number;
115
+
116
+ /**
117
+ * The maximum rating
118
+ */
119
+ total: number;
120
+ },
121
+ ];
122
+ };
123
+ }
124
+
125
+ export class Rating extends Component<Signature> {
126
+ name = `rating-${uniqueId()}`;
127
+
128
+ get icon() {
129
+ return this.args.icon ?? "★";
130
+ }
131
+
132
+ get isInteractive() {
133
+ return this.args.interactive ?? true;
134
+ }
135
+
136
+ get isChangeable() {
137
+ const readonly = this.args.readonly ?? false;
138
+
139
+ return !readonly && this.isInteractive;
140
+ }
141
+
142
+ get isReadonly() {
143
+ return !this.isChangeable;
144
+ }
145
+
146
+ get needsDescription() {
147
+ return !this.isInteractive;
148
+ }
149
+
150
+ <template>
151
+ <RatingState
152
+ @max={{@max}}
153
+ @step={{@step}}
154
+ @value={{@value}}
155
+ @name={{this.name}}
156
+ @readonly={{this.isReadonly}}
157
+ @onChange={{@onChange}}
158
+ as |r publicState|
159
+ >
160
+ <fieldset
161
+ class="ember-primitives__rating"
162
+ data-total={{r.total}}
163
+ data-value={{r.value}}
164
+ data-readonly={{this.isReadonly}}
165
+ {{! We use event delegation, this isn't a primary interactive -- we're capturing events from inputs }}
166
+ {{! template-lint-disable no-invalid-interactive }}
167
+ {{on "click" r.handleClick}}
168
+ ...attributes
169
+ >
170
+ {{#let
171
+ (component
172
+ Stars
173
+ stars=r.stars
174
+ icon=this.icon
175
+ isReadonly=this.isReadonly
176
+ name=this.name
177
+ total=r.total
178
+ currentValue=r.value
179
+ )
180
+ as |RatingStars|
181
+ }}
182
+
183
+ {{#if (has-block)}}
184
+ {{yield
185
+ (hash
186
+ max=r.total
187
+ total=r.total
188
+ value=r.value
189
+ name=this.name
190
+ isReadonly=this.isReadonly
191
+ isChangeable=this.isChangeable
192
+ Stars=RatingStars
193
+ Range=(component
194
+ RatingRange
195
+ step=r.step
196
+ max=r.total
197
+ value=r.value
198
+ name=this.name
199
+ handleChange=r.handleChange
200
+ )
201
+ )
202
+ }}
203
+ {{else}}
204
+ {{#if this.needsDescription}}
205
+ {{#if (has-block "label")}}
206
+ {{yield publicState to="label"}}
207
+ {{else}}
208
+ <span visually-hidden class="ember-primitives__rating__label">Rated
209
+ {{r.value}}
210
+ out of
211
+ {{r.total}}</span>
212
+ {{/if}}
213
+ {{else}}
214
+ {{#if (has-block "label")}}
215
+ <legend>
216
+ {{yield publicState to="label"}}
217
+ </legend>
218
+ {{/if}}
219
+ {{/if}}
220
+
221
+ <RatingStars />
222
+ {{/if}}
223
+ {{/let}}
224
+
225
+ </fieldset>
226
+ </RatingState>
227
+ </template>
228
+ }
@@ -0,0 +1,60 @@
1
+ import { uniqueId } from "../../utils.ts";
2
+ import { isString, lte } from "./utils.ts";
3
+
4
+ import type { ComponentIcons, StringIcons } from "./public-types.ts";
5
+ import type { TOC } from "@ember/component/template-only";
6
+
7
+ export const Stars: TOC<{
8
+ Args: {
9
+ // Configuration
10
+ stars: number[];
11
+ icon: StringIcons["icon"] | ComponentIcons["icon"];
12
+ isReadonly: boolean;
13
+
14
+ // HTML Boilerplate
15
+ name: string;
16
+
17
+ // State
18
+ currentValue: number;
19
+ total: number;
20
+ };
21
+ }> = <template>
22
+ <div class="ember-primitives__rating__items">
23
+ {{#each @stars as |star|}}
24
+ {{#let (uniqueId) as |id|}}
25
+ <span
26
+ class="ember-primitives__rating__item"
27
+ data-number={{star}}
28
+ data-selected={{lte star @currentValue}}
29
+ data-readonly={{@isReadonly}}
30
+ >
31
+ <label for="input-{{id}}">
32
+ <span visually-hidden>{{star}} star</span>
33
+ {{#if @icon}}
34
+ <span aria-hidden="true">
35
+ {{#if (isString @icon)}}
36
+ {{@icon}}
37
+ {{else}}
38
+ <@icon
39
+ @value={{star}}
40
+ @isSelected={{lte star @currentValue}}
41
+ @readonly={{@isReadonly}}
42
+ />
43
+ {{/if}}
44
+ </span>
45
+ {{/if}}
46
+ </label>
47
+
48
+ <input
49
+ id="input-{{id}}"
50
+ type="radio"
51
+ name={{@name}}
52
+ value={{star}}
53
+ readonly={{@isReadonly}}
54
+ checked={{Object.is star @currentValue}}
55
+ />
56
+ </span>
57
+ {{/let}}
58
+ {{/each}}
59
+ </div>
60
+ </template>;
@@ -0,0 +1,144 @@
1
+ import Component from "@glimmer/component";
2
+ import { cached } from "@glimmer/tracking";
3
+ import { assert } from "@ember/debug";
4
+ import { hash } from "@ember/helper";
5
+
6
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
7
+ // @ts-expect-error
8
+ import { localCopy } from "tracked-toolbox";
9
+
10
+ export class RatingState extends Component<{
11
+ Args: {
12
+ max: number | undefined;
13
+ value: number | undefined;
14
+ step: number | undefined;
15
+ readonly: boolean | undefined;
16
+ name: string;
17
+ onChange?: (value: number) => void;
18
+ };
19
+ Blocks: {
20
+ default: [
21
+ internalApi: {
22
+ stars: number[];
23
+ step: number;
24
+ value: number;
25
+ total: number;
26
+ handleClick: (event: Event) => void;
27
+ handleChange: (event: Event) => void;
28
+ setRating: (num: number) => void;
29
+ },
30
+ publicApi: {
31
+ value: number;
32
+ total: number;
33
+ },
34
+ ];
35
+ };
36
+ }> {
37
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
38
+ @localCopy("args.value") declare _value: number;
39
+
40
+ get value() {
41
+ return this._value ?? 0;
42
+ }
43
+
44
+ get step() {
45
+ return this.args.step ?? 1;
46
+ }
47
+
48
+ get max() {
49
+ return this.args.max ?? 5;
50
+ }
51
+
52
+ @cached
53
+ get stars() {
54
+ const result = [];
55
+
56
+ // 0 is "none selected"
57
+ let current = 0;
58
+
59
+ current += this.step;
60
+
61
+ while (current <= this.max) {
62
+ result.push(current);
63
+ current += this.step;
64
+ }
65
+
66
+ return result;
67
+ }
68
+
69
+ setRating = (value: number) => {
70
+ if (this.args.readonly) {
71
+ return;
72
+ }
73
+
74
+ if (value === this._value) {
75
+ this._value = 0;
76
+ } else {
77
+ this._value = value;
78
+ }
79
+
80
+ this.args.onChange?.(value);
81
+ };
82
+
83
+ setFromString = (value: unknown) => {
84
+ assert("[BUG]: value from input must be a string.", typeof value === "string");
85
+
86
+ const num = parseFloat(value);
87
+
88
+ if (isNaN(num)) {
89
+ // something went wrong.
90
+ // Since we're using event delegation,
91
+ // this could be from an unrelated input
92
+ return;
93
+ }
94
+
95
+ this.setRating(num);
96
+ };
97
+
98
+ /**
99
+ * Click events are captured by
100
+ * - radio changes (mouse and keyboard)
101
+ * - but only range clicks
102
+ */
103
+ handleClick = (event: Event) => {
104
+ // Since we're doing event delegation on a click, we want to make sure
105
+ // we don't do anything on other elements
106
+ const isValid =
107
+ event.target instanceof HTMLInputElement &&
108
+ event.target.name === this.args.name &&
109
+ event.target.type === "radio";
110
+
111
+ if (!isValid) return;
112
+
113
+ const selected = event.target?.value;
114
+
115
+ this.setFromString(selected);
116
+ };
117
+
118
+ /**
119
+ * Only attached to a range element, if present.
120
+ * Range elements don't fire click events on keyboard usage, like radios do
121
+ */
122
+ handleChange = (event: Event) => {
123
+ const isValid = event.target !== null && "value" in event.target;
124
+
125
+ if (!isValid) return;
126
+
127
+ this.setFromString(event.target.value);
128
+ };
129
+
130
+ <template>
131
+ {{yield
132
+ (hash
133
+ stars=this.stars
134
+ total=this.stars.length
135
+ handleClick=this.handleClick
136
+ handleChange=this.handleChange
137
+ setRating=this.setRating
138
+ value=this.value
139
+ step=this.step
140
+ )
141
+ (hash total=this.stars.length value=this.value)
142
+ }}
143
+ </template>
144
+ }
@@ -0,0 +1,7 @@
1
+ export function isString(x: unknown) {
2
+ return typeof x === 'string';
3
+ }
4
+
5
+ export function lte(a: number, b: number) {
6
+ return a <= b;
7
+ }
@@ -0,0 +1,5 @@
1
+ export { Rating } from "./rating/rating.gts";
2
+
3
+ import type { ComponentIcons } from "./rating/public-types.ts";
4
+
5
+ export type IconType = ComponentIcons["icon"];
@@ -0,0 +1,179 @@
1
+ import Component from "@glimmer/component";
2
+ import { isDestroyed, isDestroying } from "@ember/destroyable";
3
+ import { hash } from "@ember/helper";
4
+
5
+ import { modifier } from "ember-modifier";
6
+
7
+ /**
8
+ * Utility component for helping with scrolling in any direction within
9
+ * any of the 4 directions: up, down, left, right.
10
+ *
11
+ * This can be used to auto-scroll content as new content is inserted into the scrollable area, or possibly to bring focus to something on the page.
12
+ */
13
+ export class Scroller extends Component<{
14
+ /**
15
+ * A containing element is required - in this case, a div.
16
+ * It must be scrollable for this component to work, but can be customized.
17
+ *
18
+ * By default, this element will have some styling applied:
19
+ * overflow: auto;
20
+ *
21
+ * By default, this element will have tabindex="0" to support keyboard usage.
22
+ *
23
+ * The scroll-behavior is "auto", which can be controlled via CSS
24
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior
25
+ *
26
+ */
27
+ Element: HTMLDivElement;
28
+ Blocks: {
29
+ default: [
30
+ {
31
+ /**
32
+ * Scroll the content to the bottom
33
+ *
34
+ * ```gjs
35
+ * import { Scroller } from 'ember-primitives';
36
+ *
37
+ * <template>
38
+ * <Scroller as |s|>
39
+ * ...
40
+ *
41
+ * {{ (s.scrollToBottom) }}
42
+ * </Scroller>
43
+ * </template>
44
+ * ```
45
+ */
46
+ scrollToBottom: () => void;
47
+ /**
48
+ * Scroll the content to the top
49
+ *
50
+ * ```gjs
51
+ * import { Scroller } from 'ember-primitives';
52
+ *
53
+ * <template>
54
+ * <Scroller as |s|>
55
+ * ...
56
+ *
57
+ * {{ (s.scrollToTop) }}
58
+ * </Scroller>
59
+ * </template>
60
+ * ```
61
+ */
62
+ scrollToTop: () => void;
63
+ /**
64
+ * Scroll the content to the left
65
+ *
66
+ * ```gjs
67
+ * import { Scroller } from 'ember-primitives';
68
+ *
69
+ * <template>
70
+ * <Scroller as |s|>
71
+ * ...
72
+ *
73
+ * {{ (s.scrollToLeft) }}
74
+ * </Scroller>
75
+ * </template>
76
+ * ```
77
+ */
78
+ scrollToLeft: () => void;
79
+ /**
80
+ * Scroll the content to the right
81
+ *
82
+ * ```gjs
83
+ * import { Scroller } from 'ember-primitives';
84
+ *
85
+ * <template>
86
+ * <Scroller as |s|>
87
+ * ...
88
+ *
89
+ * {{ (s.scrollToRight) }}
90
+ * </Scroller>
91
+ * </template>
92
+ * ```
93
+ */
94
+ scrollToRight: () => void;
95
+ },
96
+ ];
97
+ };
98
+ }> {
99
+ declare withinElement: HTMLDivElement;
100
+
101
+ ref = modifier((el: HTMLDivElement) => {
102
+ this.withinElement = el;
103
+ });
104
+
105
+ #frame?: number;
106
+
107
+ scrollToBottom = () => {
108
+ if (this.#frame) {
109
+ cancelAnimationFrame(this.#frame);
110
+ }
111
+
112
+ this.#frame = requestAnimationFrame(() => {
113
+ if (isDestroyed(this) || isDestroying(this)) return;
114
+
115
+ this.withinElement.scrollTo({
116
+ top: this.withinElement.scrollHeight,
117
+ behavior: "auto",
118
+ });
119
+ });
120
+ };
121
+
122
+ scrollToTop = () => {
123
+ if (this.#frame) {
124
+ cancelAnimationFrame(this.#frame);
125
+ }
126
+
127
+ this.#frame = requestAnimationFrame(() => {
128
+ if (isDestroyed(this) || isDestroying(this)) return;
129
+
130
+ this.withinElement.scrollTo({
131
+ top: 0,
132
+ behavior: "auto",
133
+ });
134
+ });
135
+ };
136
+
137
+ scrollToLeft = () => {
138
+ if (this.#frame) {
139
+ cancelAnimationFrame(this.#frame);
140
+ }
141
+
142
+ this.#frame = requestAnimationFrame(() => {
143
+ if (isDestroyed(this) || isDestroying(this)) return;
144
+
145
+ this.withinElement.scrollTo({
146
+ left: 0,
147
+ behavior: "auto",
148
+ });
149
+ });
150
+ };
151
+
152
+ scrollToRight = () => {
153
+ if (this.#frame) {
154
+ cancelAnimationFrame(this.#frame);
155
+ }
156
+
157
+ this.#frame = requestAnimationFrame(() => {
158
+ if (isDestroyed(this) || isDestroying(this)) return;
159
+
160
+ this.withinElement.scrollTo({
161
+ left: this.withinElement.scrollWidth,
162
+ behavior: "auto",
163
+ });
164
+ });
165
+ };
166
+
167
+ <template>
168
+ <div tabindex="0" ...attributes {{this.ref}}>
169
+ {{yield
170
+ (hash
171
+ scrollToBottom=this.scrollToBottom
172
+ scrollToTop=this.scrollToTop
173
+ scrollToLeft=this.scrollToLeft
174
+ scrollToRight=this.scrollToRight
175
+ )
176
+ }}
177
+ </div>
178
+ </template>
179
+ }