ember-primitives 0.50.0 → 0.51.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.
@@ -22,6 +22,8 @@ export declare const colorScheme: {
22
22
  * the current valuel of the "color scheme"
23
23
  */
24
24
  current: string | undefined;
25
+ readonly isDark: boolean;
26
+ readonly isLight: boolean;
25
27
  };
26
28
  /**
27
29
  * Synchronizes state of `colorScheme` with the users preferences as well as reconciles with previously set theme in local storage.
@@ -36,8 +38,8 @@ export declare function sync(): void;
36
38
  export declare const prefers: {
37
39
  dark: () => boolean;
38
40
  light: () => boolean;
39
- custom: (name: string) => boolean;
40
41
  none: () => boolean;
42
+ custom: (name: string) => boolean;
41
43
  };
42
44
  /**
43
45
  * Helper methods for working with the color scheme preference in local storage
@@ -1 +1 @@
1
- {"version":3,"file":"color-scheme.d.ts","sourceRoot":"","sources":["../src/color-scheme.ts"],"names":[],"mappings":"AAgBA;;GAEG;AACH,eAAO,MAAM,WAAW;IACtB;;OAEG;oBACa,MAAM;;QAOpB;;WAEG;2BACgB,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI;;;QAKhD;;WAEG;2BACgB,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI;;IAKlD;;OAEG;aACY,MAAM,GAAG,SAAS;CAelC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,IAAI,SAyBnB;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO;;;mBAGH,MAAM;;CAEtB,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,eAAe;;;oBAGV,MAAM;;CAEvB,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,WAAW,UAInD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;AAC1E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;AAkBpD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,WAAW,QAItD"}
1
+ {"version":3,"file":"color-scheme.d.ts","sourceRoot":"","sources":["../src/color-scheme.ts"],"names":[],"mappings":"AAgBA;;GAEG;AACH,eAAO,MAAM,WAAW;IACtB;;OAEG;oBACa,MAAM;;QAOpB;;WAEG;2BACgB,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI;;;QAKhD;;WAEG;2BACgB,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI;;IAKlD;;OAEG;aACY,MAAM,GAAG,SAAS;;;CAsBlC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,IAAI,SAyBnB;AAcD;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;mBAIH,MAAM;CACtB,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,eAAe;;;oBAGV,MAAM;;CAEvB,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,WAAW,UAInD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;AAC1E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;AAkBpD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,WAAW,QAItD"}
@@ -0,0 +1,58 @@
1
+ import type { TOC } from "@ember/component/template-only";
2
+ declare const Separator: TOC<{
3
+ Element: HTMLSpanElement;
4
+ Blocks: {
5
+ default: [];
6
+ };
7
+ }>;
8
+ export interface Signature {
9
+ Element: HTMLElement;
10
+ Args: {
11
+ /**
12
+ * The accessible label for the breadcrumb navigation.
13
+ * Defaults to "Breadcrumb"
14
+ */
15
+ label?: string;
16
+ };
17
+ Blocks: {
18
+ default: [
19
+ {
20
+ /**
21
+ * A separator component to place between breadcrumb items.
22
+ * Typically renders as "/" or ">" with aria-hidden="true".
23
+ */
24
+ Separator: typeof Separator;
25
+ }
26
+ ];
27
+ };
28
+ }
29
+ /**
30
+ * A breadcrumb navigation component that displays the current page's location within a navigational hierarchy.
31
+ *
32
+ * Breadcrumbs help users understand their current location and provide a way to navigate back through the hierarchy.
33
+ *
34
+ * For example:
35
+ *
36
+ * ```gjs live preview
37
+ * import { Breadcrumb } from 'ember-primitives';
38
+ *
39
+ * <template>
40
+ * <Breadcrumb as |b|>
41
+ * <li>
42
+ * <a href="/">Home</a>
43
+ * </li>
44
+ * <b.Separator>/</b.Separator>
45
+ * <li>
46
+ * <a href="/docs">Docs</a>
47
+ * </li>
48
+ * <b.Separator>/</b.Separator>
49
+ * <li aria-current="page">
50
+ * Breadcrumb
51
+ * </li>
52
+ * </Breadcrumb>
53
+ * </template>
54
+ * ```
55
+ */
56
+ export declare const Breadcrumb: TOC<Signature>;
57
+ export default Breadcrumb;
58
+ //# sourceMappingURL=breadcrumb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumb.d.ts","sourceRoot":"","sources":["../../src/components/breadcrumb.gts"],"names":[],"mappings":"AA2EA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AAE1D,QAAA,MAAM,SAAS,EAAE,GAAG,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;CACH,CAYC,CAAC;AAEH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE;QACJ;;;WAGG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE;YACP;gBACE;;;mBAGG;gBACH,SAAS,EAAE,OAAO,SAAS,CAAC;aAC7B;SACF,CAAC;KACH,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,UAAU,EAAE,GAAG,CAAC,SAAS,CAiBpC,CAAC;AAEH,eAAe,UAAU,CAAC"}
@@ -0,0 +1,128 @@
1
+ import Component from "@glimmer/component";
2
+ import type { TOC } from "@ember/component/template-only";
3
+ import type { ModifierLike, WithBoundArgs } from "@glint/template";
4
+ declare const DrawerElement: TOC<{
5
+ Element: HTMLDialogElement;
6
+ Args: {
7
+ /**
8
+ * @internal
9
+ */
10
+ open: boolean | undefined;
11
+ /**
12
+ * @internal
13
+ */
14
+ onClose: () => void;
15
+ /**
16
+ * @internal
17
+ */
18
+ register: ModifierLike<{
19
+ Element: HTMLDialogElement;
20
+ }>;
21
+ };
22
+ Blocks: {
23
+ default: [];
24
+ };
25
+ }>;
26
+ export interface Signature {
27
+ Args: {
28
+ /**
29
+ * Optionally set the open state of the drawer
30
+ * The state will still be managed internally,
31
+ * so this does not need to be a maintained value, but whenever it changes,
32
+ * the drawer element will reflect that change accordingly.
33
+ */
34
+ open?: boolean;
35
+ /**
36
+ * When the drawer is closed, this function will be called
37
+ * and the drawer's `returnValue` will be passed.
38
+ *
39
+ * This can be used to determine which button was clicked to close the drawer
40
+ *
41
+ * Note though that this value is only populated when using
42
+ * `<form method='dialog'>`
43
+ */
44
+ onClose?: (returnValue: string) => void;
45
+ };
46
+ Blocks: {
47
+ default: [
48
+ {
49
+ /**
50
+ * Represents the open state of the drawer element.
51
+ */
52
+ isOpen: boolean;
53
+ /**
54
+ * Closes the drawer element
55
+ * Will throw an error if `Drawer` is not rendered.
56
+ */
57
+ close: () => void;
58
+ /**
59
+ * Opens the drawer element.
60
+ * Will throw an error if `Drawer` is not rendered.
61
+ */
62
+ open: () => void;
63
+ /**
64
+ * This modifier should be applied to the button that opens the Drawer so that it can be re-focused when the drawer closes.
65
+ *
66
+ * Example:
67
+ *
68
+ * ```gjs
69
+ * <template>
70
+ * <Drawer as |d|>
71
+ * <button {{d.focusOnClose}} {{on "click" d.open}}>Open</button>
72
+ *
73
+ * <d.Drawer>...</d.Drawer>
74
+ * </Drawer>
75
+ * </template>
76
+ * ```
77
+ */
78
+ focusOnClose: ModifierLike<{
79
+ Element: HTMLElement;
80
+ }>;
81
+ /**
82
+ * This is the `<dialog>` element (with some defaults pre-wired).
83
+ * This is required to be rendered.
84
+ */
85
+ Drawer: WithBoundArgs<typeof DrawerElement, "onClose" | "register" | "open">;
86
+ }
87
+ ];
88
+ };
89
+ }
90
+ declare class DrawerDialog extends Component<Signature> {
91
+ #private;
92
+ _isOpen: boolean;
93
+ get isOpen(): boolean;
94
+ set isOpen(val: boolean);
95
+ refocus: import("ember-modifier").FunctionBasedModifier<{
96
+ Args: {
97
+ Positional: unknown[];
98
+ Named: import("ember-modifier/-private/signature").EmptyObject;
99
+ };
100
+ Element: Element;
101
+ }>;
102
+ drawerElement: HTMLDialogElement | undefined;
103
+ register: import("ember-modifier").FunctionBasedModifier<{
104
+ Args: {
105
+ Positional: unknown[];
106
+ Named: import("ember-modifier/-private/signature").EmptyObject;
107
+ };
108
+ Element: HTMLDialogElement;
109
+ }>;
110
+ /**
111
+ * Closes the drawer -- this will throw an error in development if the drawer element was not rendered
112
+ */
113
+ close: () => void;
114
+ /**
115
+ * @internal
116
+ *
117
+ * handles the <dialog> element's native close behavior.
118
+ * listened to via addEventListener('close', ...);
119
+ */
120
+ handleClose: () => void;
121
+ /**
122
+ * Opens the drawer -- this will throw an error in development if the drawer element was not rendered
123
+ */
124
+ open: () => void;
125
+ }
126
+ export declare const Drawer: typeof DrawerDialog;
127
+ export default DrawerDialog;
128
+ //# sourceMappingURL=drawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawer.d.ts","sourceRoot":"","sources":["../../src/components/drawer.gts"],"names":[],"mappings":"AAyOA,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAa3C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEnE,QAAA,MAAM,aAAa,EAAE,GAAG,CAAC;IACvB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE;QACJ;;WAEG;QACH,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;QAC1B;;WAEG;QACH,OAAO,EAAE,MAAM,IAAI,CAAC;QAEpB;;WAEG;QACH,QAAQ,EAAE,YAAY,CAAC;YAAE,OAAO,EAAE,iBAAiB,CAAA;SAAE,CAAC,CAAC;KACxD,CAAC;IACF,MAAM,EAAE;QAAE,OAAO,EAAE,EAAE,CAAA;KAAE,CAAC;CACzB,CAcC,CAAC;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE;QACJ;;;;;WAKG;QACH,IAAI,CAAC,EAAE,OAAO,CAAC;QACf;;;;;;;;WAQG;QACH,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;KACzC,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE;YACP;gBACE;;mBAEG;gBACH,MAAM,EAAE,OAAO,CAAC;gBAEhB;;;mBAGG;gBACH,KAAK,EAAE,MAAM,IAAI,CAAC;gBAElB;;;mBAGG;gBACH,IAAI,EAAE,MAAM,IAAI,CAAC;gBAEjB;;;;;;;;;;;;;;mBAcG;gBACH,YAAY,EAAE,YAAY,CAAC;oBAAE,OAAO,EAAE,WAAW,CAAA;iBAAE,CAAC,CAAC;gBAErD;;;mBAGG;gBACH,MAAM,EAAE,aAAa,CAAC,OAAO,aAAa,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC,CAAC;aAC9E;SACF,CAAC;KACH,CAAC;CACH;AAED,cAAM,YAAa,SAAQ,SAAS,CAAC,SAAS,CAAC;;IAab,OAAO,EAAE,OAAO,CAAC;IAEjD,IAAI,MAAM,IAMM,OAAO,CADtB;IACD,IAAI,MAAM,CAAC,GAAG,EAAE,OAAO,EAEtB;IAGD,OAAO;;;;;;OAQJ;IAEc,aAAa,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAE9D,QAAQ;;;;;;OAiBL;IAEH;;OAEG;IACH,KAAK,aAkBH;IAEF;;;;;OAKG;IACH,WAAW,aAUT;IAEF;;OAEG;IACH,IAAI,aAkBF;CACH;AAED,eAAO,MAAM,MAAM,qBAAe,CAAC;AAEnC,eAAe,YAAY,CAAC"}
@@ -1,7 +1,9 @@
1
1
  export { Accordion } from './components/accordion';
2
2
  export type { AccordionContentExternalSignature, AccordionHeaderExternalSignature, AccordionItemExternalSignature, AccordionTriggerExternalSignature, } from './components/accordion/public.ts';
3
3
  export { Avatar } from './components/avatar';
4
+ export { Breadcrumb } from './components/breadcrumb';
4
5
  export { Dialog, Dialog as Modal } from './components/dialog';
6
+ export { Drawer } from './components/drawer';
5
7
  export { ExternalLink } from './components/external-link';
6
8
  export { Form } from './components/form';
7
9
  export { Key, KeyCombo } from './components/keys';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EACV,iCAAiC,EACjC,gCAAgC,EAChC,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EACV,iCAAiC,EACjC,gCAAgC,EAChC,8BAA8B,EAC9B,iCAAiC,GAClC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,cAAc,cAAc,CAAC"}
@@ -51,6 +51,12 @@ const colorScheme = {
51
51
  }
52
52
  localPreference.update(value);
53
53
  setColorScheme(value);
54
+ },
55
+ get isDark() {
56
+ return _colorScheme.current === 'dark';
57
+ },
58
+ get isLight() {
59
+ return _colorScheme.current !== 'dark';
54
60
  }
55
61
  };
56
62
 
@@ -82,16 +88,25 @@ function sync() {
82
88
  _colorScheme.current = 'light';
83
89
  }
84
90
  }
91
+ const queries = {
92
+ dark: window.matchMedia('(prefers-color-scheme: dark)'),
93
+ light: window.matchMedia('(prefers-color-scheme: light)'),
94
+ none: window.matchMedia('(prefers-color-scheme: no-preference)')
95
+ };
96
+ queries.dark.addEventListener('change', e => {
97
+ const mode = e.matches ? 'dark' : 'light';
98
+ colorScheme.update(mode);
99
+ });
85
100
 
86
101
  /**
87
102
  * Helper methods to determining what the user's preferred color scheme is
88
103
  * based on the system preferences rather than the users explicit preference.
89
104
  */
90
105
  const prefers = {
91
- dark: () => window.matchMedia('(prefers-color-scheme: dark)').matches,
92
- light: () => window.matchMedia('(prefers-color-scheme: light)').matches,
93
- custom: name => window.matchMedia(`(prefers-color-scheme: ${name})`).matches,
94
- none: () => window.matchMedia('(prefers-color-scheme: no-preference)').matches
106
+ dark: () => queries.dark.matches,
107
+ light: () => queries.light.matches,
108
+ none: () => queries.none.matches,
109
+ custom: name => window.matchMedia(`(prefers-color-scheme: ${name})`).matches
95
110
  };
96
111
  const LOCAL_PREF_KEY = 'ember-primitives/color-scheme#local-preference';
97
112
 
@@ -137,6 +152,7 @@ function styleOf(element) {
137
152
  }
138
153
  return document.documentElement.style;
139
154
  }
155
+ sync();
140
156
 
141
157
  export { colorScheme, getColorScheme, localPreference, prefers, removeColorScheme, setColorScheme, sync };
142
158
  //# sourceMappingURL=color-scheme.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"color-scheme.js","sources":["../src/color-scheme.ts"],"sourcesContent":["import { waitForPromise } from '@ember/test-waiters';\n\nimport { cell } from 'ember-resources';\n\nconst _colorScheme = cell<string | undefined>();\n\nlet callbacks: Set<(colorScheme: string) => void> = new Set();\n\nasync function runCallbacks(theme: string) {\n await Promise.resolve();\n\n for (const callback of callbacks.values()) {\n callback(theme);\n }\n}\n\n/**\n * Object for managing the color scheme\n */\nexport const colorScheme = {\n /**\n * Set's the current color scheme to the passed value\n */\n update: (value: string) => {\n colorScheme.current = value;\n\n void waitForPromise(runCallbacks(value));\n },\n\n on: {\n /**\n * register a function to be called when the color scheme changes.\n */\n update: (callback: (colorScheme: string) => void) => {\n callbacks.add(callback);\n },\n },\n off: {\n /**\n * unregister a function that would have been called when the color scheme changes.\n */\n update: (callback: (colorScheme: string) => void) => {\n callbacks.delete(callback);\n },\n },\n\n /**\n * the current valuel of the \"color scheme\"\n */\n get current(): string | undefined {\n return _colorScheme.current;\n },\n set current(value: string | undefined) {\n _colorScheme.current = value;\n\n if (!value) {\n localPreference.delete();\n\n return;\n }\n\n localPreference.update(value);\n setColorScheme(value);\n },\n};\n\n/**\n * Synchronizes state of `colorScheme` with the users preferences as well as reconciles with previously set theme in local storage.\n *\n * This may only be called once per app.\n */\nexport function sync() {\n /**\n * reset the callbacks\n */\n callbacks = new Set();\n\n /**\n * If local prefs are set, then we don't care what prefers-color-scheme is\n */\n const userPreference = localPreference.read();\n\n if (userPreference) {\n setColorScheme(userPreference);\n _colorScheme.current = userPreference;\n\n return;\n }\n\n if (prefers.dark()) {\n setColorScheme('dark');\n _colorScheme.current = 'dark';\n } else if (prefers.light()) {\n setColorScheme('light');\n _colorScheme.current = 'light';\n }\n}\n\n/**\n * Helper methods to determining what the user's preferred color scheme is\n * based on the system preferences rather than the users explicit preference.\n */\nexport const prefers = {\n dark: () => window.matchMedia('(prefers-color-scheme: dark)').matches,\n light: () => window.matchMedia('(prefers-color-scheme: light)').matches,\n custom: (name: string) => window.matchMedia(`(prefers-color-scheme: ${name})`).matches,\n none: () => window.matchMedia('(prefers-color-scheme: no-preference)').matches,\n};\n\nconst LOCAL_PREF_KEY = 'ember-primitives/color-scheme#local-preference';\n\n/**\n * Helper methods for working with the color scheme preference in local storage\n */\nexport const localPreference = {\n isSet: () => Boolean(localPreference.read()),\n read: () => localStorage.getItem(LOCAL_PREF_KEY),\n update: (value: string) => localStorage.setItem(LOCAL_PREF_KEY, value),\n delete: () => localStorage.removeItem(LOCAL_PREF_KEY),\n};\n\n/**\n * For the given element, returns the `color-scheme` of that element.\n */\nexport function getColorScheme(element?: HTMLElement) {\n const style = styleOf(element);\n\n return style.getPropertyValue('color-scheme');\n}\n\nexport function setColorScheme(element: HTMLElement, value: string): void;\nexport function setColorScheme(value: string): void;\n\nexport function setColorScheme(...args: [string] | [HTMLElement, string]): void {\n if (typeof args[0] === 'string') {\n styleOf().setProperty('color-scheme', args[0]);\n\n return;\n }\n\n if (typeof args[1] === 'string') {\n styleOf(args[0]).setProperty('color-scheme', args[1]);\n\n return;\n }\n\n throw new Error(`Invalid arity, expected up to 2 args, received ${args.length}`);\n}\n\n/**\n * Removes the `color-scheme` from the given element\n */\nexport function removeColorScheme(element?: HTMLElement) {\n const style = styleOf(element);\n\n style.removeProperty('color-scheme');\n}\n\nfunction styleOf(element?: HTMLElement) {\n if (element) {\n return element.style;\n }\n\n return document.documentElement.style;\n}\n"],"names":["_colorScheme","cell","callbacks","Set","runCallbacks","theme","Promise","resolve","callback","values","colorScheme","update","value","current","waitForPromise","on","add","off","delete","localPreference","setColorScheme","sync","userPreference","read","prefers","dark","light","window","matchMedia","matches","custom","name","none","LOCAL_PREF_KEY","isSet","Boolean","localStorage","getItem","setItem","removeItem","getColorScheme","element","style","styleOf","getPropertyValue","args","setProperty","Error","length","removeColorScheme","removeProperty","document","documentElement"],"mappings":"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"}
1
+ {"version":3,"file":"color-scheme.js","sources":["../src/color-scheme.ts"],"sourcesContent":["import { waitForPromise } from '@ember/test-waiters';\n\nimport { cell } from 'ember-resources';\n\nconst _colorScheme = cell<string | undefined>();\n\nlet callbacks: Set<(colorScheme: string) => void> = new Set();\n\nasync function runCallbacks(theme: string) {\n await Promise.resolve();\n\n for (const callback of callbacks.values()) {\n callback(theme);\n }\n}\n\n/**\n * Object for managing the color scheme\n */\nexport const colorScheme = {\n /**\n * Set's the current color scheme to the passed value\n */\n update: (value: string) => {\n colorScheme.current = value;\n\n void waitForPromise(runCallbacks(value));\n },\n\n on: {\n /**\n * register a function to be called when the color scheme changes.\n */\n update: (callback: (colorScheme: string) => void) => {\n callbacks.add(callback);\n },\n },\n off: {\n /**\n * unregister a function that would have been called when the color scheme changes.\n */\n update: (callback: (colorScheme: string) => void) => {\n callbacks.delete(callback);\n },\n },\n\n /**\n * the current valuel of the \"color scheme\"\n */\n get current(): string | undefined {\n return _colorScheme.current;\n },\n set current(value: string | undefined) {\n _colorScheme.current = value;\n\n if (!value) {\n localPreference.delete();\n\n return;\n }\n\n localPreference.update(value);\n setColorScheme(value);\n },\n\n get isDark() {\n return _colorScheme.current === 'dark';\n },\n get isLight() {\n return _colorScheme.current !== 'dark';\n },\n};\n\n/**\n * Synchronizes state of `colorScheme` with the users preferences as well as reconciles with previously set theme in local storage.\n *\n * This may only be called once per app.\n */\nexport function sync() {\n /**\n * reset the callbacks\n */\n callbacks = new Set();\n\n /**\n * If local prefs are set, then we don't care what prefers-color-scheme is\n */\n const userPreference = localPreference.read();\n\n if (userPreference) {\n setColorScheme(userPreference);\n _colorScheme.current = userPreference;\n\n return;\n }\n\n if (prefers.dark()) {\n setColorScheme('dark');\n _colorScheme.current = 'dark';\n } else if (prefers.light()) {\n setColorScheme('light');\n _colorScheme.current = 'light';\n }\n}\n\nconst queries = {\n dark: window.matchMedia('(prefers-color-scheme: dark)'),\n light: window.matchMedia('(prefers-color-scheme: light)'),\n none: window.matchMedia('(prefers-color-scheme: no-preference)'),\n};\n\nqueries.dark.addEventListener('change', (e) => {\n const mode = e.matches ? 'dark' : 'light';\n\n colorScheme.update(mode);\n});\n\n/**\n * Helper methods to determining what the user's preferred color scheme is\n * based on the system preferences rather than the users explicit preference.\n */\nexport const prefers = {\n dark: () => queries.dark.matches,\n light: () => queries.light.matches,\n none: () => queries.none.matches,\n custom: (name: string) => window.matchMedia(`(prefers-color-scheme: ${name})`).matches,\n};\n\nconst LOCAL_PREF_KEY = 'ember-primitives/color-scheme#local-preference';\n\n/**\n * Helper methods for working with the color scheme preference in local storage\n */\nexport const localPreference = {\n isSet: () => Boolean(localPreference.read()),\n read: () => localStorage.getItem(LOCAL_PREF_KEY),\n update: (value: string) => localStorage.setItem(LOCAL_PREF_KEY, value),\n delete: () => localStorage.removeItem(LOCAL_PREF_KEY),\n};\n\n/**\n * For the given element, returns the `color-scheme` of that element.\n */\nexport function getColorScheme(element?: HTMLElement) {\n const style = styleOf(element);\n\n return style.getPropertyValue('color-scheme');\n}\n\nexport function setColorScheme(element: HTMLElement, value: string): void;\nexport function setColorScheme(value: string): void;\n\nexport function setColorScheme(...args: [string] | [HTMLElement, string]): void {\n if (typeof args[0] === 'string') {\n styleOf().setProperty('color-scheme', args[0]);\n\n return;\n }\n\n if (typeof args[1] === 'string') {\n styleOf(args[0]).setProperty('color-scheme', args[1]);\n\n return;\n }\n\n throw new Error(`Invalid arity, expected up to 2 args, received ${args.length}`);\n}\n\n/**\n * Removes the `color-scheme` from the given element\n */\nexport function removeColorScheme(element?: HTMLElement) {\n const style = styleOf(element);\n\n style.removeProperty('color-scheme');\n}\n\nfunction styleOf(element?: HTMLElement) {\n if (element) {\n return element.style;\n }\n\n return document.documentElement.style;\n}\n\nsync();\n"],"names":["_colorScheme","cell","callbacks","Set","runCallbacks","theme","Promise","resolve","callback","values","colorScheme","update","value","current","waitForPromise","on","add","off","delete","localPreference","setColorScheme","isDark","isLight","sync","userPreference","read","prefers","dark","light","queries","window","matchMedia","none","addEventListener","e","mode","matches","custom","name","LOCAL_PREF_KEY","isSet","Boolean","localStorage","getItem","setItem","removeItem","getColorScheme","element","style","styleOf","getPropertyValue","args","setProperty","Error","length","removeColorScheme","removeProperty","document","documentElement"],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { hash } from '@ember/helper';
2
+ import { precompileTemplate } from '@ember/template-compilation';
3
+ import { setComponentTemplate } from '@ember/component';
4
+ import templateOnly from '@ember/component/template-only';
5
+
6
+ const Separator = setComponentTemplate(precompileTemplate("<span aria-hidden=\"true\" ...attributes>\n {{yield}}\n</span>", {
7
+ strictMode: true
8
+ }), templateOnly());
9
+ /**
10
+ * A breadcrumb navigation component that displays the current page's location within a navigational hierarchy.
11
+ *
12
+ * Breadcrumbs help users understand their current location and provide a way to navigate back through the hierarchy.
13
+ *
14
+ * For example:
15
+ *
16
+ * ```gjs live preview
17
+ * import { Breadcrumb } from 'ember-primitives';
18
+ *
19
+ * <template>
20
+ * <Breadcrumb as |b|>
21
+ * <li>
22
+ * <a href="/">Home</a>
23
+ * </li>
24
+ * <b.Separator>/</b.Separator>
25
+ * <li>
26
+ * <a href="/docs">Docs</a>
27
+ * </li>
28
+ * <b.Separator>/</b.Separator>
29
+ * <li aria-current="page">
30
+ * Breadcrumb
31
+ * </li>
32
+ * </Breadcrumb>
33
+ * </template>
34
+ * ```
35
+ */
36
+ const Breadcrumb = setComponentTemplate(precompileTemplate("<nav aria-label={{if @label @label \"Breadcrumb\"}} ...attributes>\n <ol>\n {{yield (hash Separator=Separator)}}\n </ol>\n</nav>", {
37
+ strictMode: true,
38
+ scope: () => ({
39
+ hash,
40
+ Separator
41
+ })
42
+ }), templateOnly());
43
+
44
+ export { Breadcrumb, Breadcrumb as default };
45
+ //# sourceMappingURL=breadcrumb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumb.js","sources":["../../src/components/breadcrumb.gts"],"sourcesContent":["import { hash } from \"@ember/helper\";\n\nimport type { TOC } from \"@ember/component/template-only\";\n\nconst Separator: TOC<{\n Element: HTMLSpanElement;\n Blocks: {\n default: [];\n };\n}> = <template>\n <span aria-hidden=\"true\" ...attributes>\n {{yield}}\n </span>\n</template>;\n\nexport interface Signature {\n Element: HTMLElement;\n Args: {\n /**\n * The accessible label for the breadcrumb navigation.\n * Defaults to \"Breadcrumb\"\n */\n label?: string;\n };\n Blocks: {\n default: [\n {\n /**\n * A separator component to place between breadcrumb items.\n * Typically renders as \"/\" or \">\" with aria-hidden=\"true\".\n */\n Separator: typeof Separator;\n },\n ];\n };\n}\n\n/**\n * A breadcrumb navigation component that displays the current page's location within a navigational hierarchy.\n *\n * Breadcrumbs help users understand their current location and provide a way to navigate back through the hierarchy.\n *\n * For example:\n *\n * ```gjs live preview\n * import { Breadcrumb } from 'ember-primitives';\n *\n * <template>\n * <Breadcrumb as |b|>\n * <li>\n * <a href=\"/\">Home</a>\n * </li>\n * <b.Separator>/</b.Separator>\n * <li>\n * <a href=\"/docs\">Docs</a>\n * </li>\n * <b.Separator>/</b.Separator>\n * <li aria-current=\"page\">\n * Breadcrumb\n * </li>\n * </Breadcrumb>\n * </template>\n * ```\n */\nexport const Breadcrumb: TOC<Signature> = <template>\n <nav aria-label={{if @label @label \"Breadcrumb\"}} ...attributes>\n <ol>\n {{yield (hash Separator=Separator)}}\n </ol>\n </nav>\n</template>;\n\nexport default Breadcrumb;\n"],"names":["Separator","setComponentTemplate","precompileTemplate","strictMode","templateOnly","Breadcrumb","scope","hash"],"mappings":";;;;;AAIA,MAAMA,SAKD,GAAAC,oBAAA,CAAAC,kBAAA,CAAA,iEAAA,EAIL;EAAAC,UAAA,EAAA;AAAU,CAAA,CAAA,EAAAC,YAAA,EAAA,CAAA;AAwBV;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BC;MACYC,UAAgB,GAAAJ,oBAAA,CAAaC,kBAAA,CAAA,uIAAA,EAM1C;EAAAC,UAAA,EAAA,IAAA;AAAAG,EAAAA,KAAA,EAAAA,OAAA;IAAAC,IAAA;AAAAP,IAAAA;AAAA,GAAA;AAAU,CAAA,CAAA,EAAAI,YAAA,EAAA;;;;"}
@@ -0,0 +1,123 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { assert } from '@ember/debug';
4
+ import { hash } from '@ember/helper';
5
+ import { on } from '@ember/modifier';
6
+ import { modifier } from 'ember-modifier';
7
+ import { localCopy } from 'tracked-toolbox';
8
+ import { precompileTemplate } from '@ember/template-compilation';
9
+ import { setComponentTemplate } from '@ember/component';
10
+ import templateOnly from '@ember/component/template-only';
11
+ import { g, i } from 'decorator-transforms/runtime';
12
+
13
+ const DrawerElement = setComponentTemplate(precompileTemplate("<dialog ...attributes open={{@open}} {{on \"close\" @onClose}} {{@register}}>\n {{yield}}\n</dialog>", {
14
+ strictMode: true,
15
+ scope: () => ({
16
+ on
17
+ })
18
+ }), templateOnly());
19
+ class DrawerDialog extends Component {
20
+ static {
21
+ setComponentTemplate(precompileTemplate("{{yield (hash isOpen=this.isOpen open=this.open close=this.close focusOnClose=this.refocus Drawer=(component DrawerElement open=@open onClose=this.handleClose register=this.register))}}", {
22
+ strictMode: true,
23
+ scope: () => ({
24
+ hash,
25
+ DrawerElement
26
+ })
27
+ }), this);
28
+ }
29
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
30
+ static {
31
+ g(this.prototype, "_isOpen", [localCopy("args.open")]);
32
+ }
33
+ #_isOpen = (i(this, "_isOpen"), void 0);
34
+ get isOpen() {
35
+ /**
36
+ * Always fallback to false (closed)
37
+ */
38
+ return this._isOpen ?? false;
39
+ }
40
+ set isOpen(val) {
41
+ this._isOpen = val;
42
+ }
43
+ #lastIsOpen = false;
44
+ refocus = modifier(element => {
45
+ assert(`focusOnClose is only valid on a HTMLElement`, element instanceof HTMLElement);
46
+ if (!this.isOpen && this.#lastIsOpen) {
47
+ element.focus();
48
+ }
49
+ this.#lastIsOpen = this.isOpen;
50
+ });
51
+ static {
52
+ g(this.prototype, "drawerElement", [tracked]);
53
+ }
54
+ #drawerElement = (i(this, "drawerElement"), void 0);
55
+ register = modifier(element => {
56
+ /**
57
+ * This is very sad.
58
+ *
59
+ * But we need the element to be 'root state'
60
+ * so that when we read things like "isOpen",
61
+ * when the drawer is finally rendered, all the
62
+ * downstream properties render.
63
+ *
64
+ * This has to be an async / delayed a bit, so that
65
+ * the tracking frame can exit, and we don't infinite loop
66
+ */
67
+ void (async () => {
68
+ await Promise.resolve();
69
+ this.drawerElement = element;
70
+ })();
71
+ });
72
+ /**
73
+ * Closes the drawer -- this will throw an error in development if the drawer element was not rendered
74
+ */
75
+ close = () => {
76
+ assert("Cannot call `close` on <Drawer> without rendering the drawer element.", this.drawerElement);
77
+ /**
78
+ * If the element is already closed, don't run all this again
79
+ */
80
+ if (!this.drawerElement.hasAttribute("open")) {
81
+ return;
82
+ }
83
+ /**
84
+ * removes the `open` attribute
85
+ * handleClose will be called because the drawer has bound the `close` event.
86
+ */
87
+ this.drawerElement.close();
88
+ };
89
+ /**
90
+ * @internal
91
+ *
92
+ * handles the <dialog> element's native close behavior.
93
+ * listened to via addEventListener('close', ...);
94
+ */
95
+ handleClose = () => {
96
+ assert("Cannot call `handleClose` on <Drawer> without rendering the drawer element. This is likely a bug in ember-primitives. Please open an issue <3", this.drawerElement);
97
+ this.isOpen = false;
98
+ this.args.onClose?.(this.drawerElement.returnValue);
99
+ // the return value ends up staying... which is annoying
100
+ this.drawerElement.returnValue = "";
101
+ };
102
+ /**
103
+ * Opens the drawer -- this will throw an error in development if the drawer element was not rendered
104
+ */
105
+ open = () => {
106
+ assert("Cannot call `open` on <Drawer> without rendering the drawer element.", this.drawerElement);
107
+ /**
108
+ * If the element is already open, don't run all this again
109
+ */
110
+ if (this.drawerElement.hasAttribute("open")) {
111
+ return;
112
+ }
113
+ /**
114
+ * adds the `open` attribute
115
+ */
116
+ this.drawerElement.showModal();
117
+ this.isOpen = true;
118
+ };
119
+ }
120
+ const Drawer = DrawerDialog;
121
+
122
+ export { Drawer, DrawerDialog as default };
123
+ //# sourceMappingURL=drawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drawer.js","sources":["../../src/components/drawer.gts"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { tracked } from \"@glimmer/tracking\";\nimport { assert } from \"@ember/debug\";\nimport { hash } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\n\nimport { modifier as eModifier } from \"ember-modifier\";\n// temp\n// https://github.com/tracked-tools/tracked-toolbox/issues/38\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-expect-error\nimport { localCopy } from \"tracked-toolbox\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { ModifierLike, WithBoundArgs } from \"@glint/template\";\n\nconst DrawerElement: TOC<{\n Element: HTMLDialogElement;\n Args: {\n /**\n * @internal\n */\n open: boolean | undefined;\n /**\n * @internal\n */\n onClose: () => void;\n\n /**\n * @internal\n */\n register: ModifierLike<{ Element: HTMLDialogElement }>;\n };\n Blocks: { default: [] };\n}> = <template>\n <dialog ...attributes open={{@open}} {{on \"close\" @onClose}} {{@register}}>\n {{yield}}\n </dialog>\n</template>;\n\nexport interface Signature {\n Args: {\n /**\n * Optionally set the open state of the drawer\n * The state will still be managed internally,\n * so this does not need to be a maintained value, but whenever it changes,\n * the drawer element will reflect that change accordingly.\n */\n open?: boolean;\n /**\n * When the drawer is closed, this function will be called\n * and the drawer's `returnValue` will be passed.\n *\n * This can be used to determine which button was clicked to close the drawer\n *\n * Note though that this value is only populated when using\n * `<form method='dialog'>`\n */\n onClose?: (returnValue: string) => void;\n };\n Blocks: {\n default: [\n {\n /**\n * Represents the open state of the drawer element.\n */\n isOpen: boolean;\n\n /**\n * Closes the drawer element\n * Will throw an error if `Drawer` is not rendered.\n */\n close: () => void;\n\n /**\n * Opens the drawer element.\n * Will throw an error if `Drawer` is not rendered.\n */\n open: () => void;\n\n /**\n * This modifier should be applied to the button that opens the Drawer so that it can be re-focused when the drawer closes.\n *\n * Example:\n *\n * ```gjs\n * <template>\n * <Drawer as |d|>\n * <button {{d.focusOnClose}} {{on \"click\" d.open}}>Open</button>\n *\n * <d.Drawer>...</d.Drawer>\n * </Drawer>\n * </template>\n * ```\n */\n focusOnClose: ModifierLike<{ Element: HTMLElement }>;\n\n /**\n * This is the `<dialog>` element (with some defaults pre-wired).\n * This is required to be rendered.\n */\n Drawer: WithBoundArgs<typeof DrawerElement, \"onClose\" | \"register\" | \"open\">;\n },\n ];\n };\n}\n\nclass DrawerDialog extends Component<Signature> {\n <template>\n {{yield\n (hash\n isOpen=this.isOpen\n open=this.open\n close=this.close\n focusOnClose=this.refocus\n Drawer=(component DrawerElement open=@open onClose=this.handleClose register=this.register)\n )\n }}\n </template>\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n @localCopy(\"args.open\") declare _isOpen: boolean;\n\n get isOpen() {\n /**\n * Always fallback to false (closed)\n */\n return this._isOpen ?? false;\n }\n set isOpen(val: boolean) {\n this._isOpen = val;\n }\n\n #lastIsOpen = false;\n refocus = eModifier((element) => {\n assert(`focusOnClose is only valid on a HTMLElement`, element instanceof HTMLElement);\n\n if (!this.isOpen && this.#lastIsOpen) {\n element.focus();\n }\n\n this.#lastIsOpen = this.isOpen;\n });\n\n @tracked declare drawerElement: HTMLDialogElement | undefined;\n\n register = eModifier((element: HTMLDialogElement) => {\n /**\n * This is very sad.\n *\n * But we need the element to be 'root state'\n * so that when we read things like \"isOpen\",\n * when the drawer is finally rendered, all the\n * downstream properties render.\n *\n * This has to be an async / delayed a bit, so that\n * the tracking frame can exit, and we don't infinite loop\n */\n void (async () => {\n await Promise.resolve();\n\n this.drawerElement = element;\n })();\n });\n\n /**\n * Closes the drawer -- this will throw an error in development if the drawer element was not rendered\n */\n close = () => {\n assert(\n \"Cannot call `close` on <Drawer> without rendering the drawer element.\",\n this.drawerElement,\n );\n\n /**\n * If the element is already closed, don't run all this again\n */\n if (!this.drawerElement.hasAttribute(\"open\")) {\n return;\n }\n\n /**\n * removes the `open` attribute\n * handleClose will be called because the drawer has bound the `close` event.\n */\n this.drawerElement.close();\n };\n\n /**\n * @internal\n *\n * handles the <dialog> element's native close behavior.\n * listened to via addEventListener('close', ...);\n */\n handleClose = () => {\n assert(\n \"Cannot call `handleClose` on <Drawer> without rendering the drawer element. This is likely a bug in ember-primitives. Please open an issue <3\",\n this.drawerElement,\n );\n\n this.isOpen = false;\n this.args.onClose?.(this.drawerElement.returnValue);\n // the return value ends up staying... which is annoying\n this.drawerElement.returnValue = \"\";\n };\n\n /**\n * Opens the drawer -- this will throw an error in development if the drawer element was not rendered\n */\n open = () => {\n assert(\n \"Cannot call `open` on <Drawer> without rendering the drawer element.\",\n this.drawerElement,\n );\n\n /**\n * If the element is already open, don't run all this again\n */\n if (this.drawerElement.hasAttribute(\"open\")) {\n return;\n }\n\n /**\n * adds the `open` attribute\n */\n this.drawerElement.showModal();\n this.isOpen = true;\n };\n}\n\nexport const Drawer = DrawerDialog;\n\nexport default DrawerDialog;\n"],"names":["DrawerElement","setComponentTemplate","precompileTemplate","strictMode","scope","on","templateOnly","DrawerDialog","Component","hash","g","prototype","localCopy","i","isOpen","_isOpen","val","refocus","eModifier","element","assert","HTMLElement","focus","tracked","register","Promise","resolve","drawerElement","close","hasAttribute","handleClose","args","onClose","returnValue","open","showModal","Drawer"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { macroCondition, isDevelopingApp, importSync } from '@embroider/macros';
2
2
  export { Accordion } from './components/accordion.js';
3
3
  export { Avatar } from './components/avatar.js';
4
+ export { Breadcrumb } from './components/breadcrumb.js';
4
5
  export { Dialog, Dialog as Modal } from './components/dialog.js';
6
+ export { Drawer } from './components/drawer.js';
5
7
  export { ExternalLink } from './components/external-link.js';
6
8
  export { Form } from './components/form.js';
7
9
  export { Key, KeyCombo } from './components/keys.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 { Dialog, Dialog as Modal } from './components/dialog.gts';\nexport { ExternalLink } from './components/external-link.gts';\nexport { Form } from './components/form.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 { 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 { 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 { 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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember-primitives",
3
- "version": "0.50.0",
3
+ "version": "0.51.0",
4
4
  "description": "Making apps easier to build",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -17,19 +17,19 @@
17
17
  ],
18
18
  "bin": "./bin/index.mjs",
19
19
  "dependencies": {
20
- "@babel/runtime": "^7.27.1",
21
- "@embroider/addon-shim": "^1.10.0",
20
+ "@babel/runtime": "^7.28.4",
21
+ "@embroider/addon-shim": "^1.10.2",
22
22
  "@embroider/macros": "^1.19.6",
23
23
  "@floating-ui/dom": "^1.7.0",
24
24
  "decorator-transforms": "^2.3.0",
25
- "ember-element-helper": ">= 0.8.6",
25
+ "ember-element-helper": "^0.8.8",
26
26
  "form-data-utils": "^0.6.0",
27
- "reactiveweb": "^1.8.0",
27
+ "reactiveweb": "^1.9.1",
28
28
  "should-handle-link": "^1.2.2",
29
29
  "tabster": "^8.7.0",
30
30
  "tracked-built-ins": "^4.1.0",
31
31
  "tracked-toolbox": "^2.0.0",
32
- "which-heading-do-i-need": "0.2.3"
32
+ "which-heading-do-i-need": "0.2.4"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@arethetypeswrong/cli": "^0.18.0",
@@ -58,7 +58,7 @@
58
58
  "concurrently": "^9.2.1",
59
59
  "ember-modifier": "^4.2.2",
60
60
  "ember-resources": "^7.0.7",
61
- "ember-source": "6.9.0",
61
+ "ember-source": "^6.9.0",
62
62
  "ember-template-lint": "^7.9.3",
63
63
  "eslint": "^9.39.2",
64
64
  "execa": "^9.6.0",
@@ -66,7 +66,7 @@
66
66
  "prettier": "^3.7.4",
67
67
  "prettier-plugin-ember-template-tag": "^2.1.2",
68
68
  "publint": "^0.3.16",
69
- "rollup": "~4.54.0",
69
+ "rollup": "^4.54.0",
70
70
  "rollup-plugin-copy": "^3.5.0",
71
71
  "typescript": "^5.9.3"
72
72
  },
@@ -144,6 +144,7 @@
144
144
  "lint:types": "ember-tsc --noEmit --declaration false --declarationMap false --emitDeclarationOnly false",
145
145
  "pack": "pnpm pack",
146
146
  "start": "rollup --config --watch",
147
+ "dev": "rollup --config --watch --no-watch.clearScreen",
147
148
  "test": "echo 'A v2 addon does not have tests, run tests in test-app'"
148
149
  }
149
150
  }
@@ -62,6 +62,13 @@ export const colorScheme = {
62
62
  localPreference.update(value);
63
63
  setColorScheme(value);
64
64
  },
65
+
66
+ get isDark() {
67
+ return _colorScheme.current === 'dark';
68
+ },
69
+ get isLight() {
70
+ return _colorScheme.current !== 'dark';
71
+ },
65
72
  };
66
73
 
67
74
  /**
@@ -96,15 +103,27 @@ export function sync() {
96
103
  }
97
104
  }
98
105
 
106
+ const queries = {
107
+ dark: window.matchMedia('(prefers-color-scheme: dark)'),
108
+ light: window.matchMedia('(prefers-color-scheme: light)'),
109
+ none: window.matchMedia('(prefers-color-scheme: no-preference)'),
110
+ };
111
+
112
+ queries.dark.addEventListener('change', (e) => {
113
+ const mode = e.matches ? 'dark' : 'light';
114
+
115
+ colorScheme.update(mode);
116
+ });
117
+
99
118
  /**
100
119
  * Helper methods to determining what the user's preferred color scheme is
101
120
  * based on the system preferences rather than the users explicit preference.
102
121
  */
103
122
  export const prefers = {
104
- dark: () => window.matchMedia('(prefers-color-scheme: dark)').matches,
105
- light: () => window.matchMedia('(prefers-color-scheme: light)').matches,
123
+ dark: () => queries.dark.matches,
124
+ light: () => queries.light.matches,
125
+ none: () => queries.none.matches,
106
126
  custom: (name: string) => window.matchMedia(`(prefers-color-scheme: ${name})`).matches,
107
- none: () => window.matchMedia('(prefers-color-scheme: no-preference)').matches,
108
127
  };
109
128
 
110
129
  const LOCAL_PREF_KEY = 'ember-primitives/color-scheme#local-preference';
@@ -163,3 +182,5 @@ function styleOf(element?: HTMLElement) {
163
182
 
164
183
  return document.documentElement.style;
165
184
  }
185
+
186
+ sync();
@@ -0,0 +1,73 @@
1
+ import { hash } from "@ember/helper";
2
+
3
+ import type { TOC } from "@ember/component/template-only";
4
+
5
+ const Separator: TOC<{
6
+ Element: HTMLSpanElement;
7
+ Blocks: {
8
+ default: [];
9
+ };
10
+ }> = <template>
11
+ <span aria-hidden="true" ...attributes>
12
+ {{yield}}
13
+ </span>
14
+ </template>;
15
+
16
+ export interface Signature {
17
+ Element: HTMLElement;
18
+ Args: {
19
+ /**
20
+ * The accessible label for the breadcrumb navigation.
21
+ * Defaults to "Breadcrumb"
22
+ */
23
+ label?: string;
24
+ };
25
+ Blocks: {
26
+ default: [
27
+ {
28
+ /**
29
+ * A separator component to place between breadcrumb items.
30
+ * Typically renders as "/" or ">" with aria-hidden="true".
31
+ */
32
+ Separator: typeof Separator;
33
+ },
34
+ ];
35
+ };
36
+ }
37
+
38
+ /**
39
+ * A breadcrumb navigation component that displays the current page's location within a navigational hierarchy.
40
+ *
41
+ * Breadcrumbs help users understand their current location and provide a way to navigate back through the hierarchy.
42
+ *
43
+ * For example:
44
+ *
45
+ * ```gjs live preview
46
+ * import { Breadcrumb } from 'ember-primitives';
47
+ *
48
+ * <template>
49
+ * <Breadcrumb as |b|>
50
+ * <li>
51
+ * <a href="/">Home</a>
52
+ * </li>
53
+ * <b.Separator>/</b.Separator>
54
+ * <li>
55
+ * <a href="/docs">Docs</a>
56
+ * </li>
57
+ * <b.Separator>/</b.Separator>
58
+ * <li aria-current="page">
59
+ * Breadcrumb
60
+ * </li>
61
+ * </Breadcrumb>
62
+ * </template>
63
+ * ```
64
+ */
65
+ export const Breadcrumb: TOC<Signature> = <template>
66
+ <nav aria-label={{if @label @label "Breadcrumb"}} ...attributes>
67
+ <ol>
68
+ {{yield (hash Separator=Separator)}}
69
+ </ol>
70
+ </nav>
71
+ </template>;
72
+
73
+ export default Breadcrumb;
@@ -0,0 +1,233 @@
1
+ import Component from "@glimmer/component";
2
+ import { tracked } from "@glimmer/tracking";
3
+ import { assert } from "@ember/debug";
4
+ import { hash } from "@ember/helper";
5
+ import { on } from "@ember/modifier";
6
+
7
+ import { modifier as eModifier } from "ember-modifier";
8
+ // temp
9
+ // https://github.com/tracked-tools/tracked-toolbox/issues/38
10
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
11
+ // @ts-expect-error
12
+ import { localCopy } from "tracked-toolbox";
13
+
14
+ import type { TOC } from "@ember/component/template-only";
15
+ import type { ModifierLike, WithBoundArgs } from "@glint/template";
16
+
17
+ const DrawerElement: TOC<{
18
+ Element: HTMLDialogElement;
19
+ Args: {
20
+ /**
21
+ * @internal
22
+ */
23
+ open: boolean | undefined;
24
+ /**
25
+ * @internal
26
+ */
27
+ onClose: () => void;
28
+
29
+ /**
30
+ * @internal
31
+ */
32
+ register: ModifierLike<{ Element: HTMLDialogElement }>;
33
+ };
34
+ Blocks: { default: [] };
35
+ }> = <template>
36
+ <dialog ...attributes open={{@open}} {{on "close" @onClose}} {{@register}}>
37
+ {{yield}}
38
+ </dialog>
39
+ </template>;
40
+
41
+ export interface Signature {
42
+ Args: {
43
+ /**
44
+ * Optionally set the open state of the drawer
45
+ * The state will still be managed internally,
46
+ * so this does not need to be a maintained value, but whenever it changes,
47
+ * the drawer element will reflect that change accordingly.
48
+ */
49
+ open?: boolean;
50
+ /**
51
+ * When the drawer is closed, this function will be called
52
+ * and the drawer's `returnValue` will be passed.
53
+ *
54
+ * This can be used to determine which button was clicked to close the drawer
55
+ *
56
+ * Note though that this value is only populated when using
57
+ * `<form method='dialog'>`
58
+ */
59
+ onClose?: (returnValue: string) => void;
60
+ };
61
+ Blocks: {
62
+ default: [
63
+ {
64
+ /**
65
+ * Represents the open state of the drawer element.
66
+ */
67
+ isOpen: boolean;
68
+
69
+ /**
70
+ * Closes the drawer element
71
+ * Will throw an error if `Drawer` is not rendered.
72
+ */
73
+ close: () => void;
74
+
75
+ /**
76
+ * Opens the drawer element.
77
+ * Will throw an error if `Drawer` is not rendered.
78
+ */
79
+ open: () => void;
80
+
81
+ /**
82
+ * This modifier should be applied to the button that opens the Drawer so that it can be re-focused when the drawer closes.
83
+ *
84
+ * Example:
85
+ *
86
+ * ```gjs
87
+ * <template>
88
+ * <Drawer as |d|>
89
+ * <button {{d.focusOnClose}} {{on "click" d.open}}>Open</button>
90
+ *
91
+ * <d.Drawer>...</d.Drawer>
92
+ * </Drawer>
93
+ * </template>
94
+ * ```
95
+ */
96
+ focusOnClose: ModifierLike<{ Element: HTMLElement }>;
97
+
98
+ /**
99
+ * This is the `<dialog>` element (with some defaults pre-wired).
100
+ * This is required to be rendered.
101
+ */
102
+ Drawer: WithBoundArgs<typeof DrawerElement, "onClose" | "register" | "open">;
103
+ },
104
+ ];
105
+ };
106
+ }
107
+
108
+ class DrawerDialog extends Component<Signature> {
109
+ <template>
110
+ {{yield
111
+ (hash
112
+ isOpen=this.isOpen
113
+ open=this.open
114
+ close=this.close
115
+ focusOnClose=this.refocus
116
+ Drawer=(component DrawerElement open=@open onClose=this.handleClose register=this.register)
117
+ )
118
+ }}
119
+ </template>
120
+
121
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
122
+ @localCopy("args.open") declare _isOpen: boolean;
123
+
124
+ get isOpen() {
125
+ /**
126
+ * Always fallback to false (closed)
127
+ */
128
+ return this._isOpen ?? false;
129
+ }
130
+ set isOpen(val: boolean) {
131
+ this._isOpen = val;
132
+ }
133
+
134
+ #lastIsOpen = false;
135
+ refocus = eModifier((element) => {
136
+ assert(`focusOnClose is only valid on a HTMLElement`, element instanceof HTMLElement);
137
+
138
+ if (!this.isOpen && this.#lastIsOpen) {
139
+ element.focus();
140
+ }
141
+
142
+ this.#lastIsOpen = this.isOpen;
143
+ });
144
+
145
+ @tracked declare drawerElement: HTMLDialogElement | undefined;
146
+
147
+ register = eModifier((element: HTMLDialogElement) => {
148
+ /**
149
+ * This is very sad.
150
+ *
151
+ * But we need the element to be 'root state'
152
+ * so that when we read things like "isOpen",
153
+ * when the drawer is finally rendered, all the
154
+ * downstream properties render.
155
+ *
156
+ * This has to be an async / delayed a bit, so that
157
+ * the tracking frame can exit, and we don't infinite loop
158
+ */
159
+ void (async () => {
160
+ await Promise.resolve();
161
+
162
+ this.drawerElement = element;
163
+ })();
164
+ });
165
+
166
+ /**
167
+ * Closes the drawer -- this will throw an error in development if the drawer element was not rendered
168
+ */
169
+ close = () => {
170
+ assert(
171
+ "Cannot call `close` on <Drawer> without rendering the drawer element.",
172
+ this.drawerElement,
173
+ );
174
+
175
+ /**
176
+ * If the element is already closed, don't run all this again
177
+ */
178
+ if (!this.drawerElement.hasAttribute("open")) {
179
+ return;
180
+ }
181
+
182
+ /**
183
+ * removes the `open` attribute
184
+ * handleClose will be called because the drawer has bound the `close` event.
185
+ */
186
+ this.drawerElement.close();
187
+ };
188
+
189
+ /**
190
+ * @internal
191
+ *
192
+ * handles the <dialog> element's native close behavior.
193
+ * listened to via addEventListener('close', ...);
194
+ */
195
+ handleClose = () => {
196
+ assert(
197
+ "Cannot call `handleClose` on <Drawer> without rendering the drawer element. This is likely a bug in ember-primitives. Please open an issue <3",
198
+ this.drawerElement,
199
+ );
200
+
201
+ this.isOpen = false;
202
+ this.args.onClose?.(this.drawerElement.returnValue);
203
+ // the return value ends up staying... which is annoying
204
+ this.drawerElement.returnValue = "";
205
+ };
206
+
207
+ /**
208
+ * Opens the drawer -- this will throw an error in development if the drawer element was not rendered
209
+ */
210
+ open = () => {
211
+ assert(
212
+ "Cannot call `open` on <Drawer> without rendering the drawer element.",
213
+ this.drawerElement,
214
+ );
215
+
216
+ /**
217
+ * If the element is already open, don't run all this again
218
+ */
219
+ if (this.drawerElement.hasAttribute("open")) {
220
+ return;
221
+ }
222
+
223
+ /**
224
+ * adds the `open` attribute
225
+ */
226
+ this.drawerElement.showModal();
227
+ this.isOpen = true;
228
+ };
229
+ }
230
+
231
+ export const Drawer = DrawerDialog;
232
+
233
+ export default DrawerDialog;
package/src/index.ts CHANGED
@@ -19,7 +19,9 @@ export type {
19
19
  AccordionTriggerExternalSignature,
20
20
  } from './components/accordion/public.ts';
21
21
  export { Avatar } from './components/avatar.gts';
22
+ export { Breadcrumb } from './components/breadcrumb.gts';
22
23
  export { Dialog, Dialog as Modal } from './components/dialog.gts';
24
+ export { Drawer } from './components/drawer.gts';
23
25
  export { ExternalLink } from './components/external-link.gts';
24
26
  export { Form } from './components/form.gts';
25
27
  export { Key, KeyCombo } from './components/keys.gts';