ember-primitives 0.49.0 → 0.50.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.
Files changed (107) hide show
  1. package/bin/index.mjs +271 -0
  2. package/declarations/color-scheme.d.ts +1 -1
  3. package/declarations/color-scheme.d.ts.map +1 -1
  4. package/declarations/components/rating/public-types.d.ts +0 -4
  5. package/declarations/components/rating/public-types.d.ts.map +1 -1
  6. package/declarations/components/rating/rating.d.ts +9 -1
  7. package/declarations/components/rating/rating.d.ts.map +1 -1
  8. package/declarations/components/rating/stars.d.ts.map +1 -1
  9. package/declarations/components/rating/state.d.ts +4 -0
  10. package/declarations/components/rating/state.d.ts.map +1 -1
  11. package/declarations/components/rating/utils.d.ts +0 -1
  12. package/declarations/components/rating/utils.d.ts.map +1 -1
  13. package/dist/color-scheme.js +13 -4
  14. package/dist/color-scheme.js.map +1 -1
  15. package/dist/components/rating.js +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/{rating-CjBVsX6q.js → rating-BrIiwDLw.js} +21 -17
  18. package/dist/rating-BrIiwDLw.js.map +1 -0
  19. package/package.json +6 -2
  20. package/src/-private.ts +4 -0
  21. package/src/color-scheme.ts +177 -0
  22. package/src/components/-private/typed-elements.gts +13 -0
  23. package/src/components/-private/utils.ts +16 -0
  24. package/src/components/accordion/content.gts +34 -0
  25. package/src/components/accordion/header.gts +36 -0
  26. package/src/components/accordion/item.gts +55 -0
  27. package/src/components/accordion/public.ts +64 -0
  28. package/src/components/accordion/trigger.gts +32 -0
  29. package/src/components/accordion.gts +195 -0
  30. package/src/components/avatar.gts +108 -0
  31. package/src/components/dialog.gts +234 -0
  32. package/src/components/external-link.gts +14 -0
  33. package/src/components/form.gts +75 -0
  34. package/src/components/heading.gts +36 -0
  35. package/src/components/keys.gts +53 -0
  36. package/src/components/layout/hero.css +5 -0
  37. package/src/components/layout/hero.gts +17 -0
  38. package/src/components/layout/sticky-footer.css +9 -0
  39. package/src/components/layout/sticky-footer.gts +40 -0
  40. package/src/components/link.gts +172 -0
  41. package/src/components/menu.gts +373 -0
  42. package/src/components/one-time-password/buttons.gts +31 -0
  43. package/src/components/one-time-password/input.gts +198 -0
  44. package/src/components/one-time-password/otp.gts +130 -0
  45. package/src/components/one-time-password/utils.ts +201 -0
  46. package/src/components/one-time-password.gts +2 -0
  47. package/src/components/popover.gts +248 -0
  48. package/src/components/portal-targets.gts +136 -0
  49. package/src/components/portal.gts +194 -0
  50. package/src/components/progress.gts +154 -0
  51. package/src/components/rating/public-types.ts +44 -0
  52. package/src/components/rating/range.gts +22 -0
  53. package/src/components/rating/rating.gts +228 -0
  54. package/src/components/rating/stars.gts +60 -0
  55. package/src/components/rating/state.gts +144 -0
  56. package/src/components/rating/utils.ts +7 -0
  57. package/src/components/rating.gts +5 -0
  58. package/src/components/scroller.gts +179 -0
  59. package/src/components/shadowed.gts +110 -0
  60. package/src/components/switch.gts +103 -0
  61. package/src/components/tabs.gts +519 -0
  62. package/src/components/toggle-group.gts +265 -0
  63. package/src/components/toggle.gts +81 -0
  64. package/src/components/violations.css +105 -0
  65. package/src/components/violations.css.ts +1 -0
  66. package/src/components/visually-hidden.css +14 -0
  67. package/src/components/visually-hidden.gts +15 -0
  68. package/src/components/zoetrope/index.gts +358 -0
  69. package/src/components/zoetrope/styles.css +40 -0
  70. package/src/components/zoetrope/types.ts +65 -0
  71. package/src/components/zoetrope.ts +3 -0
  72. package/src/dom-context.gts +245 -0
  73. package/src/floating-ui/component.gts +186 -0
  74. package/src/floating-ui/middleware.ts +13 -0
  75. package/src/floating-ui/modifier.ts +183 -0
  76. package/src/floating-ui.ts +2 -0
  77. package/src/head.gts +37 -0
  78. package/src/helpers/body-class.ts +94 -0
  79. package/src/helpers/link.ts +125 -0
  80. package/src/helpers/service.ts +25 -0
  81. package/src/helpers.ts +2 -0
  82. package/src/iframe.ts +31 -0
  83. package/src/index.ts +43 -0
  84. package/src/load.gts +77 -0
  85. package/src/narrowing.ts +7 -0
  86. package/src/on-resize.ts +64 -0
  87. package/src/proper-links.ts +140 -0
  88. package/src/qp.ts +107 -0
  89. package/src/resize-observer.ts +132 -0
  90. package/src/service.ts +103 -0
  91. package/src/store.ts +72 -0
  92. package/src/styles.css.ts +5 -0
  93. package/src/tabster.ts +54 -0
  94. package/src/template-registry.ts +44 -0
  95. package/src/test-support/a11y.ts +50 -0
  96. package/src/test-support/dom.ts +112 -0
  97. package/src/test-support/otp.ts +64 -0
  98. package/src/test-support/rating.ts +144 -0
  99. package/src/test-support/routing.ts +62 -0
  100. package/src/test-support/zoetrope.ts +51 -0
  101. package/src/test-support.gts +6 -0
  102. package/src/type-utils.ts +1 -0
  103. package/src/utils.ts +75 -0
  104. package/src/viewport/in-viewport.gts +128 -0
  105. package/src/viewport/viewport.ts +122 -0
  106. package/src/viewport.ts +2 -0
  107. package/dist/rating-CjBVsX6q.js.map +0 -1
@@ -0,0 +1,108 @@
1
+ import { hash } from "@ember/helper";
2
+
3
+ import { ReactiveImage } from "reactiveweb/image";
4
+ import { WaitUntil } from "reactiveweb/wait-until";
5
+
6
+ import type { TOC } from "@ember/component/template-only";
7
+ import type { WithBoundArgs } from "@glint/template";
8
+
9
+ const Fallback: TOC<{
10
+ Blocks: { default: [] };
11
+ Args: {
12
+ /**
13
+ * The number of milliseconds to wait for the image to load
14
+ * before displaying the fallback
15
+ */
16
+ delayMs?: number;
17
+ /**
18
+ * @private
19
+ * Bound internally by ember-primitives
20
+ */
21
+ isLoaded: boolean;
22
+ };
23
+ }> = <template>
24
+ {{#unless @isLoaded}}
25
+ {{#let (WaitUntil @delayMs) as |delayFinished|}}
26
+ {{#if delayFinished}}
27
+ {{yield}}
28
+ {{/if}}
29
+ {{/let}}
30
+ {{/unless}}
31
+ </template>;
32
+
33
+ const Image: TOC<{
34
+ Element: HTMLImageElement;
35
+ Args: {
36
+ /**
37
+ * @private
38
+ * The `src` value for the image.
39
+ *
40
+ * Bound internally by ember-primitives
41
+ */
42
+ src: string;
43
+ /**
44
+ * @private
45
+ * Bound internally by ember-primitives
46
+ */
47
+ isLoaded: boolean;
48
+ };
49
+ }> = <template>
50
+ {{#if @isLoaded}}
51
+ <img alt="__missing__" ...attributes src={{@src}} />
52
+ {{/if}}
53
+ </template>;
54
+
55
+ export const Avatar: TOC<{
56
+ Element: HTMLSpanElement;
57
+ Args: {
58
+ /**
59
+ * The `src` value for the image.
60
+ */
61
+ src: string;
62
+ };
63
+ Blocks: {
64
+ default: [
65
+ avatar: {
66
+ /**
67
+ * The image to render. It will only render when it has loaded.
68
+ */
69
+ Image: WithBoundArgs<typeof Image, "src" | "isLoaded">;
70
+ /**
71
+ * An element that renders when the image hasn't loaded.
72
+ * This means whilst it's loading, or if there was an error.
73
+ * If you notice a flash during loading,
74
+ * you can provide a delayMs prop to delay its rendering so it only renders for those with slower connections.
75
+ */
76
+ Fallback: WithBoundArgs<typeof Fallback, "isLoaded">;
77
+ /**
78
+ * true while the image is loading
79
+ */
80
+ isLoading: boolean;
81
+ /**
82
+ * If the image fails to load, this will be `true`
83
+ */
84
+ isError: boolean;
85
+ },
86
+ ];
87
+ };
88
+ }> = <template>
89
+ {{#let (ReactiveImage @src) as |imgState|}}
90
+ <span
91
+ data-prim-avatar
92
+ ...attributes
93
+ data-loading={{imgState.isLoading}}
94
+ data-error={{imgState.isError}}
95
+ >
96
+ {{yield
97
+ (hash
98
+ Image=(component Image src=@src isLoaded=imgState.isResolved)
99
+ Fallback=(component Fallback isLoaded=imgState.isResolved)
100
+ isLoading=imgState.isLoading
101
+ isError=imgState.isError
102
+ )
103
+ }}
104
+ </span>
105
+ {{/let}}
106
+ </template>;
107
+
108
+ export default Avatar;
@@ -0,0 +1,234 @@
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 DialogElement: 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 `<dialog>`
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 dialog element will reflect that change accordingly.
48
+ */
49
+ open?: boolean;
50
+ /**
51
+ * When the `<dialog>` is closed, this function will be called
52
+ * and the `<dialog>`'s `returnValue` will be passed.
53
+ *
54
+ * This can be used to determine which button was clicked to close the modal
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 `<dialog>` element.
66
+ */
67
+ isOpen: boolean;
68
+
69
+ /**
70
+ * Closes the `<dialog>` element
71
+ * Will throw an error if `Dialog` is not rendered.
72
+ */
73
+ close: () => void;
74
+
75
+ /**
76
+ * Opens the `<dialog>` element.
77
+ * Will throw an error if `Dialog` is not rendered.
78
+ */
79
+ open: () => void;
80
+
81
+ /**
82
+ * This modifier should be applied to the button that opens the Dialog so that it can be re-focused when the dialog closes.
83
+ *
84
+ * Example:
85
+ *
86
+ * ```gjs
87
+ * <template>
88
+ * <Modal as |m|>
89
+ * <button {{m.focusOnClose}} {{on "click" m.open}}>Open</button>
90
+ *
91
+ * <m.Dialog>...</m.Dialog>
92
+ * </Modal>
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
+ Dialog: WithBoundArgs<typeof DialogElement, "onClose" | "register" | "open">;
103
+ },
104
+ ];
105
+ };
106
+ }
107
+
108
+ class ModalDialog 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
+ Dialog=(component DialogElement 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 dialogElement: 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 dialog 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.dialogElement = element;
163
+ })();
164
+ });
165
+
166
+ /**
167
+ * Closes the dialog -- this will throw an error in development if the dialog element was not rendered
168
+ */
169
+ close = () => {
170
+ assert(
171
+ "Cannot call `close` on <Dialog> without rendering the dialog element.",
172
+ this.dialogElement,
173
+ );
174
+
175
+ /**
176
+ * If the element is already closed, don't run all this again
177
+ */
178
+ if (!this.dialogElement.hasAttribute("open")) {
179
+ return;
180
+ }
181
+
182
+ /**
183
+ * removes the `open` attribute
184
+ * handleClose will be called because the dialog has bound the `close` event.
185
+ */
186
+ this.dialogElement.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 `handleDialogClose` on <Dialog> without rendering the dialog element. This is likely a bug in ember-primitives. Please open an issue <3",
198
+ this.dialogElement,
199
+ );
200
+
201
+ this.isOpen = false;
202
+ this.args.onClose?.(this.dialogElement.returnValue);
203
+ // the return value ends up staying... which is annoying
204
+ this.dialogElement.returnValue = "";
205
+ };
206
+
207
+ /**
208
+ * Opens the dialog -- this will throw an error in development if the dialog element was not rendered
209
+ */
210
+ open = () => {
211
+ assert(
212
+ "Cannot call `open` on <Dialog> without rendering the dialog element.",
213
+ this.dialogElement,
214
+ );
215
+
216
+ /**
217
+ * If the element is already open, don't run all this again
218
+ */
219
+ if (this.dialogElement.hasAttribute("open")) {
220
+ return;
221
+ }
222
+
223
+ /**
224
+ * adds the `open` attribute
225
+ */
226
+ this.dialogElement.showModal();
227
+ this.isOpen = true;
228
+ };
229
+ }
230
+
231
+ export const Modal = ModalDialog;
232
+ export const Dialog = ModalDialog;
233
+
234
+ export default ModalDialog;
@@ -0,0 +1,14 @@
1
+ import type { TOC } from "@ember/component/template-only";
2
+
3
+ export const ExternalLink: TOC<{
4
+ Element: HTMLAnchorElement;
5
+ Blocks: {
6
+ default: [];
7
+ };
8
+ }> = <template>
9
+ <a target="_blank" rel="noreferrer noopener" href="##missing##" ...attributes>
10
+ {{yield}}
11
+ </a>
12
+ </template>;
13
+
14
+ export default ExternalLink;
@@ -0,0 +1,75 @@
1
+ import { fn } from "@ember/helper";
2
+ import { on } from "@ember/modifier";
3
+
4
+ import { dataFrom } from "form-data-utils";
5
+
6
+ import type { TOC } from "@ember/component/template-only";
7
+
8
+ type Data = ReturnType<typeof dataFrom>;
9
+
10
+ export const dataFromEvent = dataFrom;
11
+
12
+ const handleInput = (
13
+ onChange: (data: Data, eventType: "input" | "submit", event: Event) => void,
14
+ event: Event | SubmitEvent,
15
+ eventType: "input" | "submit" = "input",
16
+ ) => {
17
+ const data = dataFrom(event);
18
+
19
+ onChange(data, eventType, event);
20
+ };
21
+
22
+ const handleSubmit = (
23
+ onChange: (data: Data, eventType: "input" | "submit", event: Event | SubmitEvent) => void,
24
+ event: SubmitEvent,
25
+ ) => {
26
+ event.preventDefault();
27
+ handleInput(onChange, event, "submit");
28
+ };
29
+
30
+ export interface Signature {
31
+ Element: HTMLFormElement;
32
+ Args: {
33
+ /**
34
+ * Any time the value of any field is changed this function will be called.
35
+ */
36
+ onChange: (
37
+ /**
38
+ * The data from the form as an Object of `{ [field name] => value }` pairs.
39
+ * This is generated from the native [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
40
+ *
41
+ * Additional fields/inputs/controls can be added to this data by specifying a
42
+ * "name" attribute.
43
+ */
44
+ data: Data,
45
+ /**
46
+ * Indicates whether the `onChange` function was called from the `input` or `submit` event handlers.
47
+ */
48
+ eventType: "input" | "submit",
49
+ /**
50
+ * The raw event, if needed.
51
+ */
52
+ event: Event | SubmitEvent,
53
+ ) => void;
54
+ };
55
+ Blocks: {
56
+ /**
57
+ * The main content for the form. This is where inputs / fields / controls would go.
58
+ * Within the `<form>` content, `<button type="submit">` will submit the form, which
59
+ * triggers the `@onChange` event.
60
+ */
61
+ default: [];
62
+ };
63
+ }
64
+
65
+ export const Form: TOC<Signature> = <template>
66
+ <form
67
+ {{on "input" (fn handleInput @onChange)}}
68
+ {{on "submit" (fn handleSubmit @onChange)}}
69
+ ...attributes
70
+ >
71
+ {{yield}}
72
+ </form>
73
+ </template>;
74
+
75
+ export default Form;
@@ -0,0 +1,36 @@
1
+ import Component from "@glimmer/component";
2
+
3
+ import { element } from "ember-element-helper";
4
+ import { getSectionHeadingLevel } from "which-heading-do-i-need";
5
+
6
+ import type Owner from "@ember/owner";
7
+
8
+ export class Heading extends Component<{
9
+ Element: HTMLElement;
10
+ Blocks: { default: [] };
11
+ }> {
12
+ headingScopeAnchor: Text;
13
+ constructor(owner: Owner, args: object) {
14
+ super(owner, args);
15
+
16
+ this.headingScopeAnchor = document.createTextNode("");
17
+ }
18
+
19
+ get level() {
20
+ return getSectionHeadingLevel(this.headingScopeAnchor);
21
+ }
22
+
23
+ get hLevel() {
24
+ return `h${this.level}`;
25
+ }
26
+
27
+ <template>
28
+ {{this.headingScopeAnchor}}
29
+
30
+ {{#let (element this.hLevel) as |El|}}
31
+ <El ...attributes>
32
+ {{yield}}
33
+ </El>
34
+ {{/let}}
35
+ </template>
36
+ }
@@ -0,0 +1,53 @@
1
+ import type { TOC } from "@ember/component/template-only";
2
+
3
+ const isLast = (collection: unknown[], index: number) => index === collection.length - 1;
4
+ const isNotLast = (collection: unknown[], index: number) => !isLast(collection, index);
5
+ const isMac = navigator.userAgent.indexOf("Mac OS") >= 0;
6
+
7
+ function split(str: string) {
8
+ const keys = str.split("+").map((x) => x.trim());
9
+
10
+ return keys;
11
+ }
12
+
13
+ function getKeys(keys: string[] | string, mac?: string[] | string) {
14
+ const normalKeys = Array.isArray(keys) ? keys : split(keys);
15
+
16
+ if (!mac) {
17
+ return normalKeys;
18
+ }
19
+
20
+ const normalMac = Array.isArray(mac) ? mac : split(mac);
21
+
22
+ return isMac ? normalMac : normalKeys;
23
+ }
24
+
25
+ export interface KeyComboSignature {
26
+ Element: HTMLElement;
27
+ Args: {
28
+ keys: string[] | string;
29
+ mac?: string[] | string;
30
+ };
31
+ }
32
+
33
+ export const KeyCombo: TOC<KeyComboSignature> = <template>
34
+ <span class="ember-primitives__key-combination" ...attributes>
35
+ {{#let (getKeys @keys @mac) as |keys|}}
36
+ {{#each keys as |key i|}}
37
+ <Key>{{key}}</Key>
38
+ {{#if (isNotLast keys i)}}
39
+ <span class="ember-primitives__key-combination__separator">+</span>
40
+ {{/if}}
41
+ {{/each}}
42
+ {{/let}}
43
+ </span>
44
+ </template>;
45
+
46
+ export interface KeySignature {
47
+ Element: HTMLElement;
48
+ Blocks: { default?: [] };
49
+ }
50
+
51
+ export const Key: TOC<KeySignature> = <template>
52
+ <kbd class="ember-primitives__key" ...attributes>{{yield}}</kbd>
53
+ </template>;
@@ -0,0 +1,5 @@
1
+ .ember-primitives__hero__wrapper {
2
+ width: 100dvw;
3
+ height: 100dvh;
4
+ position: relative;
5
+ }
@@ -0,0 +1,17 @@
1
+ import "./hero.css";
2
+
3
+ import type { TOC } from "@ember/component/template-only";
4
+
5
+ export const Hero: TOC<{
6
+ /**
7
+ * The wrapper element of the whole layout.
8
+ */
9
+ Element: HTMLDivElement;
10
+ Blocks: {
11
+ default: [];
12
+ };
13
+ }> = <template>
14
+ <div class="ember-primitives__hero__wrapper" ...attributes>
15
+ {{yield}}
16
+ </div>
17
+ </template>;
@@ -0,0 +1,9 @@
1
+ .ember-primitives__sticky-footer__wrapper {
2
+ height: 100%;
3
+ overflow: auto;
4
+ }
5
+ .ember-primitives__sticky-footer__container {
6
+ min-height: 100%;
7
+ display: grid;
8
+ grid-template-rows: 1fr auto;
9
+ }
@@ -0,0 +1,40 @@
1
+ import "./sticky-footer.css";
2
+
3
+ import type { TOC } from "@ember/component/template-only";
4
+
5
+ export const StickyFooter: TOC<{
6
+ /**
7
+ * The wrapper element of the whole layout.
8
+ * Valid parents for this element must have either a set height,
9
+ * or a set max-height.
10
+ */
11
+ Element: HTMLDivElement;
12
+ Blocks: {
13
+ /**
14
+ * This is the scrollable content, contained within a `<div>` element for positioning.
15
+ * If this component is used as the main layout on a page,
16
+ * the `<main>` element would be appropriate within here.
17
+ */
18
+ content: [];
19
+ /**
20
+ * This is the footer content, contained within a `<div>` element for positioning.
21
+ * A `<footer>` element would be appropriate within here.
22
+ *
23
+ * This element will be at the bottom of the page if the content does not overflow the containing element and this element will be at the bottom of the content if there is overflow.
24
+ */
25
+ footer: [];
26
+ };
27
+ }> = <template>
28
+ <div class="ember-primitives__sticky-footer__wrapper" ...attributes>
29
+ <div class="ember-primitives__sticky-footer__container">
30
+ <div class="ember-primitives__sticky-footer__content">
31
+ {{yield to="content"}}
32
+ </div>
33
+ <div class="ember-primitives__sticky-footer__footer">
34
+ {{yield to="footer"}}
35
+ </div>
36
+ </div>
37
+ </div>
38
+ </template>;
39
+
40
+ export default StickyFooter;