ember-primitives 0.27.2 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/declarations/color-scheme.d.ts.map +1 -1
- package/declarations/components/-private/typed-elements.d.ts +1 -1
- package/declarations/components/accordion/content.d.ts +2 -2
- package/declarations/components/accordion/header.d.ts +2 -2
- package/declarations/components/accordion/item.d.ts +3 -3
- package/declarations/components/accordion/public.d.ts +3 -3
- package/declarations/components/accordion/trigger.d.ts +2 -2
- package/declarations/components/accordion.d.ts +7 -7
- package/declarations/components/accordion.d.ts.map +1 -1
- package/declarations/components/avatar.d.ts +4 -4
- package/declarations/components/dialog.d.ts +30 -4
- package/declarations/components/dialog.d.ts.map +1 -1
- package/declarations/components/external-link.d.ts +1 -1
- package/declarations/components/form.d.ts +3 -3
- package/declarations/components/keys.d.ts +17 -0
- package/declarations/components/keys.d.ts.map +1 -0
- package/declarations/components/layout/hero.d.ts +2 -2
- package/declarations/components/layout/sticky-footer.d.ts +2 -2
- package/declarations/components/menu.d.ts +19 -19
- package/declarations/components/menu.d.ts.map +1 -1
- package/declarations/components/one-time-password/buttons.d.ts +1 -1
- package/declarations/components/one-time-password/index.d.ts +2 -2
- package/declarations/components/one-time-password/input.d.ts +5 -5
- package/declarations/components/one-time-password/input.d.ts.map +1 -1
- package/declarations/components/one-time-password/otp.d.ts +5 -5
- package/declarations/components/one-time-password/otp.d.ts.map +1 -1
- package/declarations/components/one-time-password/utils.d.ts.map +1 -1
- package/declarations/components/popover.d.ts +17 -26
- package/declarations/components/popover.d.ts.map +1 -1
- package/declarations/components/portal-targets.d.ts +1 -1
- package/declarations/components/portal.d.ts +4 -3
- package/declarations/components/portal.d.ts.map +1 -1
- package/declarations/components/progress.d.ts +4 -4
- package/declarations/components/scroller.d.ts +1 -1
- package/declarations/components/shadowed.d.ts +1 -1
- package/declarations/components/switch.d.ts +5 -5
- package/declarations/components/toggle-group.d.ts +4 -4
- package/declarations/components/toggle.d.ts +2 -2
- package/declarations/components/toggle.d.ts.map +1 -1
- package/declarations/components/zoetrope/index.d.ts +3 -3
- package/declarations/components/zoetrope.d.ts +2 -2
- package/declarations/floating-ui/component.d.ts +19 -16
- package/declarations/floating-ui/component.d.ts.map +1 -1
- package/declarations/floating-ui/modifier.d.ts.map +1 -1
- package/declarations/floating-ui.d.ts +1 -1
- package/declarations/helpers/link.d.ts.map +1 -1
- package/declarations/index.d.ts +20 -19
- package/declarations/index.d.ts.map +1 -1
- package/declarations/proper-links.d.ts.map +1 -1
- package/declarations/test-support/routing.d.ts.map +1 -1
- package/declarations/utils.d.ts.map +1 -1
- package/dist/color-scheme.js +5 -5
- package/dist/color-scheme.js.map +1 -1
- package/dist/components/-private/typed-elements.js.map +1 -1
- package/dist/components/-private/utils.js +1 -3
- package/dist/components/-private/utils.js.map +1 -1
- package/dist/components/accordion/content.js +1 -1
- package/dist/components/accordion/header.js +1 -1
- package/dist/components/accordion/item.js +1 -1
- package/dist/components/accordion/trigger.js +1 -1
- package/dist/components/accordion.js +26 -25
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/dialog.js +24 -15
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/external-link.js.map +1 -1
- package/dist/components/form.js +6 -6
- package/dist/components/form.js.map +1 -1
- package/dist/components/keys.js +33 -0
- package/dist/components/keys.js.map +1 -0
- package/dist/components/layout/hero.js.map +1 -1
- package/dist/components/layout/sticky-footer.js.map +1 -1
- package/dist/components/link.js.map +1 -1
- package/dist/components/menu.js +47 -46
- package/dist/components/menu.js.map +1 -1
- package/dist/components/one-time-password/buttons.js +5 -5
- package/dist/components/one-time-password/buttons.js.map +1 -1
- package/dist/components/one-time-password/input.js +14 -14
- package/dist/components/one-time-password/input.js.map +1 -1
- package/dist/components/one-time-password/otp.js +26 -25
- package/dist/components/one-time-password/otp.js.map +1 -1
- package/dist/components/one-time-password/utils.js +18 -18
- package/dist/components/one-time-password/utils.js.map +1 -1
- package/dist/components/popover.js +44 -44
- package/dist/components/popover.js.map +1 -1
- package/dist/components/portal-targets.js +16 -16
- package/dist/components/portal-targets.js.map +1 -1
- package/dist/components/portal.js +5 -3
- package/dist/components/portal.js.map +1 -1
- package/dist/components/progress.js +16 -16
- package/dist/components/progress.js.map +1 -1
- package/dist/components/scroller.js +6 -6
- package/dist/components/scroller.js.map +1 -1
- package/dist/components/shadowed.js +9 -9
- package/dist/components/shadowed.js.map +1 -1
- package/dist/components/switch.js.map +1 -1
- package/dist/components/toggle-group.js +24 -24
- package/dist/components/toggle-group.js.map +1 -1
- package/dist/components/toggle.js +4 -4
- package/dist/components/toggle.js.map +1 -1
- package/dist/components/violations.css +8 -8
- package/dist/components/zoetrope/index.js +102 -102
- package/dist/components/zoetrope/index.js.map +1 -1
- package/dist/floating-ui/component.js +6 -6
- package/dist/floating-ui/component.js.map +1 -1
- package/dist/floating-ui/modifier.js +10 -6
- package/dist/floating-ui/modifier.js.map +1 -1
- package/dist/helpers/link.js +11 -7
- package/dist/helpers/link.js.map +1 -1
- package/dist/helpers/service.js +1 -1
- package/dist/helpers/service.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/{item-DmpElnSZ.js → item-D6pwWzMs.js} +3 -3
- package/dist/item-D6pwWzMs.js.map +1 -0
- package/dist/proper-links.js +10 -10
- package/dist/proper-links.js.map +1 -1
- package/dist/tabster.js +2 -2
- package/dist/tabster.js.map +1 -1
- package/dist/test-support/a11y.js +4 -4
- package/dist/test-support/a11y.js.map +1 -1
- package/dist/test-support/otp.js +6 -6
- package/dist/test-support/otp.js.map +1 -1
- package/dist/test-support/routing.js +3 -2
- package/dist/test-support/routing.js.map +1 -1
- package/dist/test-support/zoetrope.js.map +1 -1
- package/dist/utils.js +1 -0
- package/dist/utils.js.map +1 -1
- package/package.json +20 -30
- package/dist/item-DmpElnSZ.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"otp.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"otp.js","sources":["../../../src/components/one-time-password/otp.gts"],"sourcesContent":["import { assert } from \"@ember/debug\";\nimport { fn, hash } from \"@ember/helper\";\nimport { on } from \"@ember/modifier\";\nimport { buildWaiter } from \"@ember/test-waiters\";\n\nimport { Reset, Submit } from \"./buttons.gts\";\nimport { OTPInput } from \"./input.gts\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { WithBoundArgs } from \"@glint/template\";\n\nconst waiter = buildWaiter(\"ember-primitives:OTP:handleAutoSubmitAttempt\");\n\nconst handleFormSubmit = (submit: (data: { code: string }) => void, event: SubmitEvent) => {\n event.preventDefault();\n\n assert(\n \"[BUG]: handleFormSubmit was not attached to a form. Please open an issue.\",\n event.currentTarget instanceof HTMLFormElement,\n );\n\n const formData = new FormData(event.currentTarget);\n\n let code = \"\";\n\n for (const [key, value] of formData.entries()) {\n if (key.startsWith(\"code\")) {\n // eslint-disable-next-line @typescript-eslint/restrict-plus-operands, @typescript-eslint/no-base-to-string\n code += value;\n }\n }\n\n submit({\n code,\n });\n};\n\nfunction handleChange(\n autoSubmit: boolean | undefined,\n data: { code: string; complete: boolean },\n event: Event,\n) {\n if (!autoSubmit) return;\n if (!data.complete) return;\n\n assert(\n \"[BUG]: event target is not a known element type\",\n event.target instanceof HTMLElement || event.target instanceof SVGElement,\n );\n\n const form = event.target.closest(\"form\");\n\n assert(\"[BUG]: Cannot handle event when <OTP> Inputs are not rendered within their <form>\", form);\n\n const token = waiter.beginAsync();\n const finished = () => {\n waiter.endAsync(token);\n form.removeEventListener(\"submit\", finished);\n };\n\n form.addEventListener(\"submit\", finished);\n\n // NOTE: when calling .submit() the submit event handlers are not run\n form.requestSubmit();\n}\n\nexport const OTP: TOC<{\n /**\n * The overall OTP Input is in its own form.\n * Modern UI/UX Patterns usually have this sort of field\n * as its own page, thus within its own form.\n *\n * By default, only the 'submit' event is bound, and is\n * what calls the `@onSubmit` argument.\n */\n Element: HTMLFormElement;\n Args: {\n /**\n * How many characters the one-time-password field should be\n * Defaults to 6\n */\n length?: number;\n\n /**\n * The on submit callback will give you the entered\n * one-time-password code.\n *\n * It will be called when the user manually clicks the 'submit'\n * button or when the full code is pasted and meats the validation\n * criteria.\n */\n onSubmit: (data: { code: string }) => void;\n\n /**\n * Whether or not to auto-submit after the code has been pasted\n * in to the collective \"field\". Default is true\n */\n autoSubmit?: boolean;\n };\n Blocks: {\n default: [\n {\n /**\n * The collective input field that the OTP code will be typed/pasted in to\n */\n Input: WithBoundArgs<typeof OTPInput, \"length\" | \"onChange\">;\n /**\n * Button with `type=\"submit\"` to submit the form\n */\n Submit: typeof Submit;\n /**\n * Pre-wired button to reset the form\n */\n Reset: typeof Reset;\n },\n ];\n };\n}> = <template>\n <form {{on \"submit\" (fn handleFormSubmit @onSubmit)}} ...attributes>\n {{yield\n (hash\n Input=(component\n OTPInput length=@length onChange=(if @autoSubmit (fn handleChange @autoSubmit))\n )\n Submit=Submit\n Reset=Reset\n )\n }}\n </form>\n</template>;\n"],"names":["waiter","buildWaiter","handleFormSubmit","submit","event","preventDefault","assert","currentTarget","HTMLFormElement","formData","FormData","code","key","value","entries","startsWith","handleChange","autoSubmit","data","complete","target","HTMLElement","SVGElement","form","closest","token","beginAsync","finished","endAsync","removeEventListener","addEventListener","requestSubmit","OTP","setComponentTemplate","precompileTemplate","strictMode","scope","on","fn","hash","OTPInput","Submit","Reset","templateOnly"],"mappings":";;;;;;;;;;AAWA,MAAMA,SAASC,WAAY,CAAA,8CAAA,CAAA;AAE3B,MAAMC,gBAAA,GAAmBA,CAACC,MAAwC,EAAEC,KAAO,KAAA;EACzEA,KAAA,CAAMC,cAAc,EAAA;EAEpBC,MACE,CAAA,2EAAA,EACAF,KAAM,CAAAG,aAAa,YAAYC,eAAA,CAAA;EAGjC,MAAMC,QAAW,GAAA,IAAIC,QAAS,CAAAN,KAAA,CAAMG,aAAa,CAAA;EAEjD,IAAII,IAAO,GAAA,EAAA;AAEX,EAAA,KAAK,MAAM,CAACC,GAAA,EAAKC,MAAM,IAAIJ,QAAA,CAASK,OAAO,EAAI,EAAA;AAC7C,IAAA,IAAIF,GAAA,CAAIG,UAAU,CAAC,MAAS,CAAA,EAAA;AAC1B;AACAJ,MAAAA,IAAQ,IAAAE,KAAA;AACV;AACF;AAEAV,EAAAA,MAAO,CAAA;AACLQ,IAAAA;AACF,GAAA,CAAA;AACF,CAAA;AAEA,SAASK,YACPA,CAAAC,UAA+B,EAC/BC,IAAyC,EACzCd,KAAY,EAAA;EAEZ,IAAI,CAACa,UAAY,EAAA;AACjB,EAAA,IAAI,CAACC,IAAK,CAAAC,QAAQ,EAAE;AAEpBb,EAAAA,MAAA,CACE,mDACAF,KAAM,CAAAgB,MAAM,YAAYC,WAAe,IAAAjB,KAAA,CAAMgB,MAAM,YAAYE,UAAA,CAAA;EAGjE,MAAMC,IAAO,GAAAnB,KAAA,CAAMgB,MAAM,CAACI,OAAO,CAAC,MAAA,CAAA;AAElClB,EAAAA,MAAA,CAAO,mFAAqF,EAAAiB,IAAA,CAAA;AAE5F,EAAA,MAAME,KAAA,GAAQzB,OAAO0B,UAAU,EAAA;EAC/B,MAAMC,QAAW,GAAAA,MAAA;AACf3B,IAAAA,MAAA,CAAO4B,QAAQ,CAACH,KAAA,CAAA;AAChBF,IAAAA,IAAK,CAAAM,mBAAmB,CAAC,QAAU,EAAAF,QAAA,CAAA;GACrC;AAEAJ,EAAAA,IAAK,CAAAO,gBAAgB,CAAC,QAAU,EAAAH,QAAA,CAAA;AAEhC;EACAJ,IAAA,CAAKQ,aAAa,EAAA;AACpB;MAEaC,GAmDR,GAAAC,oBAAA,CAAAC,kBAAA,CAYL,2OAAA,EAAA;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;IAAAC,EAAA;IAAAC,EAAA;IAAApC,gBAAA;IAAAqC,IAAA;IAAAC,QAAA;IAAAxB,YAAA;IAAAyB,MAAA;AAAAC,IAAAA;AAAA,GAAA;AAAU,CAAE,CAAA,EAAAC,YAAA,EAAA;;;;"}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { assert } from '@ember/debug';
|
|
2
2
|
|
|
3
3
|
function getInputs(current) {
|
|
4
|
-
|
|
4
|
+
const fieldset = current.closest('fieldset');
|
|
5
5
|
assert('[BUG]: fieldset went missing', fieldset);
|
|
6
6
|
return [...fieldset.querySelectorAll('input')];
|
|
7
7
|
}
|
|
8
8
|
function nextInput(current) {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const inputs = getInputs(current);
|
|
10
|
+
const currentIndex = inputs.indexOf(current);
|
|
11
11
|
return inputs[currentIndex + 1];
|
|
12
12
|
}
|
|
13
13
|
function selectAll(event) {
|
|
14
|
-
|
|
14
|
+
const target = event.target;
|
|
15
15
|
assert(`selectAll is only meant for use with input elements`, target instanceof HTMLInputElement);
|
|
16
16
|
target.select();
|
|
17
17
|
}
|
|
18
18
|
function handlePaste(event) {
|
|
19
|
-
|
|
19
|
+
const target = event.target;
|
|
20
20
|
assert(`handlePaste is only meant for use with input elements`, target instanceof HTMLInputElement);
|
|
21
|
-
|
|
21
|
+
const clipboardData = event.clipboardData;
|
|
22
22
|
assert(`Could not get clipboardData while handling the paste event on OTP. Please report this issue on the ember-primitives repo with a reproduction. Thanks!`, clipboardData);
|
|
23
23
|
|
|
24
24
|
// This is typically not good to prevent paste.
|
|
@@ -26,13 +26,13 @@ function handlePaste(event) {
|
|
|
26
26
|
// we want to split the pasted value across
|
|
27
27
|
// multiple text fields
|
|
28
28
|
event.preventDefault();
|
|
29
|
-
|
|
29
|
+
const value = clipboardData.getData('Text');
|
|
30
30
|
const digits = value;
|
|
31
31
|
let i = 0;
|
|
32
32
|
let currElement = target;
|
|
33
33
|
while (currElement) {
|
|
34
34
|
currElement.value = digits[i++] || '';
|
|
35
|
-
|
|
35
|
+
const next = nextInput(currElement);
|
|
36
36
|
if (next instanceof HTMLInputElement) {
|
|
37
37
|
currElement = next;
|
|
38
38
|
} else {
|
|
@@ -56,18 +56,18 @@ function handleNavigation(event) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
function focusLeft(event) {
|
|
59
|
-
|
|
59
|
+
const target = event.target;
|
|
60
60
|
assert(`only allowed on input elements`, target instanceof HTMLInputElement);
|
|
61
|
-
|
|
61
|
+
const input = previousInput(target);
|
|
62
62
|
input?.focus();
|
|
63
63
|
requestAnimationFrame(() => {
|
|
64
64
|
input?.select();
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
function focusRight(event) {
|
|
68
|
-
|
|
68
|
+
const target = event.target;
|
|
69
69
|
assert(`only allowed on input elements`, target instanceof HTMLInputElement);
|
|
70
|
-
|
|
70
|
+
const input = nextInput(target);
|
|
71
71
|
input?.focus();
|
|
72
72
|
requestAnimationFrame(() => {
|
|
73
73
|
input?.select();
|
|
@@ -84,7 +84,7 @@ function handleBackspace(event) {
|
|
|
84
84
|
* speed of the user's computer
|
|
85
85
|
*/
|
|
86
86
|
event.preventDefault();
|
|
87
|
-
|
|
87
|
+
const target = event.target;
|
|
88
88
|
if (target && 'value' in target) {
|
|
89
89
|
if (target.value === '') {
|
|
90
90
|
focusLeft({
|
|
@@ -97,13 +97,13 @@ function handleBackspace(event) {
|
|
|
97
97
|
target?.dispatchEvent(syntheticEvent);
|
|
98
98
|
}
|
|
99
99
|
function previousInput(current) {
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
const inputs = getInputs(current);
|
|
101
|
+
const currentIndex = inputs.indexOf(current);
|
|
102
102
|
return inputs[currentIndex - 1];
|
|
103
103
|
}
|
|
104
104
|
const autoAdvance = event => {
|
|
105
105
|
assert('[BUG]: autoAdvance called on non-input element', event.target instanceof HTMLInputElement);
|
|
106
|
-
|
|
106
|
+
const value = event.target.value;
|
|
107
107
|
if (value.length === 0) return;
|
|
108
108
|
if (value.length > 0) {
|
|
109
109
|
if ('data' in event && event.data && typeof event.data === 'string') {
|
|
@@ -129,10 +129,10 @@ function getCollectiveValue(elementTarget, length) {
|
|
|
129
129
|
parent = elementTarget.closest('fieldset');
|
|
130
130
|
}
|
|
131
131
|
assert(`[BUG]: somehow the input fields were rendered without a parent element`, parent);
|
|
132
|
-
|
|
132
|
+
const elements = parent.querySelectorAll('input');
|
|
133
133
|
let value = '';
|
|
134
134
|
assert(`found elements (${elements.length}) do not match length (${length}). Was the same OTP input rendered more than once?`, elements.length === length);
|
|
135
|
-
for (
|
|
135
|
+
for (const element of elements) {
|
|
136
136
|
assert('[BUG]: how did the queried elements become a non-input element?', element instanceof HTMLInputElement);
|
|
137
137
|
value += element.value;
|
|
138
138
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["../../../src/components/one-time-password/utils.ts"],"sourcesContent":["import { assert } from '@ember/debug';\n\nfunction getInputs(current: HTMLInputElement) {\n let fieldset = current.closest('fieldset');\n\n assert('[BUG]: fieldset went missing', fieldset);\n\n return [...fieldset.querySelectorAll('input')];\n}\n\nfunction nextInput(current: HTMLInputElement) {\n let inputs = getInputs(current);\n let currentIndex = inputs.indexOf(current);\n\n return inputs[currentIndex + 1];\n}\n\nexport function selectAll(event: Event) {\n let target = event.target;\n\n assert(`selectAll is only meant for use with input elements`, target instanceof HTMLInputElement);\n\n target.select();\n}\n\nexport function handlePaste(event: Event) {\n let target = event.target;\n\n assert(\n `handlePaste is only meant for use with input elements`,\n target instanceof HTMLInputElement\n );\n\n let clipboardData = (event as ClipboardEvent).clipboardData;\n\n assert(\n `Could not get clipboardData while handling the paste event on OTP. Please report this issue on the ember-primitives repo with a reproduction. Thanks!`,\n clipboardData\n );\n\n // This is typically not good to prevent paste.\n // But because of the UX we're implementing,\n // we want to split the pasted value across\n // multiple text fields\n event.preventDefault();\n\n let value = clipboardData.getData('Text');\n const digits = value;\n let i = 0;\n let currElement: HTMLInputElement | null = target;\n\n while (currElement) {\n currElement.value = digits[i++] || '';\n\n let next = nextInput(currElement);\n\n if (next instanceof HTMLInputElement) {\n currElement = next;\n } else {\n break;\n }\n }\n\n // We want to select the first field again\n // so that if someone holds paste, or\n // pastes again, they get the same result.\n target.select();\n}\n\nexport function handleNavigation(event: KeyboardEvent) {\n switch (event.key) {\n case 'Backspace':\n return handleBackspace(event);\n case 'ArrowLeft':\n return focusLeft(event);\n case 'ArrowRight':\n return focusRight(event);\n }\n}\n\nfunction focusLeft(event: Pick<Event, 'target'>) {\n let target = event.target;\n\n assert(`only allowed on input elements`, target instanceof HTMLInputElement);\n\n let input = previousInput(target);\n\n input?.focus();\n requestAnimationFrame(() => {\n input?.select();\n });\n}\n\nfunction focusRight(event: Pick<Event, 'target'>) {\n let target = event.target;\n\n assert(`only allowed on input elements`, target instanceof HTMLInputElement);\n\n let input = nextInput(target);\n\n input?.focus();\n requestAnimationFrame(() => {\n input?.select();\n });\n}\n\nconst syntheticEvent = new InputEvent('input');\n\nfunction handleBackspace(event: KeyboardEvent) {\n if (event.key !== 'Backspace') return;\n\n /**\n * We have to prevent default because we\n * - want to clear the whole field\n * - have the focus behavior keep up with the key-repeat\n * speed of the user's computer\n */\n event.preventDefault();\n\n let target = event.target;\n\n if (target && 'value' in target) {\n if (target.value === '') {\n focusLeft({ target });\n } else {\n target.value = '';\n }\n }\n\n target?.dispatchEvent(syntheticEvent);\n}\n\nfunction previousInput(current: HTMLInputElement) {\n let inputs = getInputs(current);\n let currentIndex = inputs.indexOf(current);\n\n return inputs[currentIndex - 1];\n}\n\nexport const autoAdvance = (event: Event) => {\n assert(\n '[BUG]: autoAdvance called on non-input element',\n event.target instanceof HTMLInputElement\n );\n\n let value = event.target.value;\n\n if (value.length === 0) return;\n\n if (value.length > 0) {\n if ('data' in event && event.data && typeof event.data === 'string') {\n event.target.value = event.data;\n }\n\n return focusRight(event);\n }\n};\n\nexport function getCollectiveValue(elementTarget: EventTarget | null, length: number) {\n if (!elementTarget) return;\n\n assert(\n `[BUG]: somehow the element target is not HTMLElement`,\n elementTarget instanceof HTMLElement\n );\n\n let parent: null | HTMLElement | ShadowRoot;\n\n // TODO: should this logic be extracted?\n // why is getting the target element within a shadow root hard?\n if (!(elementTarget instanceof HTMLInputElement)) {\n if (elementTarget.shadowRoot) {\n parent = elementTarget.shadowRoot;\n } else {\n parent = elementTarget.closest('fieldset');\n }\n } else {\n parent = elementTarget.closest('fieldset');\n }\n\n assert(`[BUG]: somehow the input fields were rendered without a parent element`, parent);\n\n let elements = parent.querySelectorAll('input');\n\n let value = '';\n\n assert(\n `found elements (${elements.length}) do not match length (${length}). Was the same OTP input rendered more than once?`,\n elements.length === length\n );\n\n for (let element of elements) {\n assert(\n '[BUG]: how did the queried elements become a non-input element?',\n element instanceof HTMLInputElement\n );\n value += element.value;\n }\n\n return value;\n}\n"],"names":["getInputs","current","fieldset","closest","assert","querySelectorAll","nextInput","inputs","currentIndex","indexOf","selectAll","event","target","HTMLInputElement","select","handlePaste","clipboardData","preventDefault","value","getData","digits","i","currElement","next","handleNavigation","key","handleBackspace","focusLeft","focusRight","input","previousInput","focus","requestAnimationFrame","syntheticEvent","InputEvent","dispatchEvent","autoAdvance","length","data","getCollectiveValue","elementTarget","HTMLElement","parent","shadowRoot","elements","element"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["../../../src/components/one-time-password/utils.ts"],"sourcesContent":["import { assert } from '@ember/debug';\n\nfunction getInputs(current: HTMLInputElement) {\n const fieldset = current.closest('fieldset');\n\n assert('[BUG]: fieldset went missing', fieldset);\n\n return [...fieldset.querySelectorAll('input')];\n}\n\nfunction nextInput(current: HTMLInputElement) {\n const inputs = getInputs(current);\n const currentIndex = inputs.indexOf(current);\n\n return inputs[currentIndex + 1];\n}\n\nexport function selectAll(event: Event) {\n const target = event.target;\n\n assert(`selectAll is only meant for use with input elements`, target instanceof HTMLInputElement);\n\n target.select();\n}\n\nexport function handlePaste(event: Event) {\n const target = event.target;\n\n assert(\n `handlePaste is only meant for use with input elements`,\n target instanceof HTMLInputElement\n );\n\n const clipboardData = (event as ClipboardEvent).clipboardData;\n\n assert(\n `Could not get clipboardData while handling the paste event on OTP. Please report this issue on the ember-primitives repo with a reproduction. Thanks!`,\n clipboardData\n );\n\n // This is typically not good to prevent paste.\n // But because of the UX we're implementing,\n // we want to split the pasted value across\n // multiple text fields\n event.preventDefault();\n\n const value = clipboardData.getData('Text');\n const digits = value;\n let i = 0;\n let currElement: HTMLInputElement | null = target;\n\n while (currElement) {\n currElement.value = digits[i++] || '';\n\n const next = nextInput(currElement);\n\n if (next instanceof HTMLInputElement) {\n currElement = next;\n } else {\n break;\n }\n }\n\n // We want to select the first field again\n // so that if someone holds paste, or\n // pastes again, they get the same result.\n target.select();\n}\n\nexport function handleNavigation(event: KeyboardEvent) {\n switch (event.key) {\n case 'Backspace':\n return handleBackspace(event);\n case 'ArrowLeft':\n return focusLeft(event);\n case 'ArrowRight':\n return focusRight(event);\n }\n}\n\nfunction focusLeft(event: Pick<Event, 'target'>) {\n const target = event.target;\n\n assert(`only allowed on input elements`, target instanceof HTMLInputElement);\n\n const input = previousInput(target);\n\n input?.focus();\n requestAnimationFrame(() => {\n input?.select();\n });\n}\n\nfunction focusRight(event: Pick<Event, 'target'>) {\n const target = event.target;\n\n assert(`only allowed on input elements`, target instanceof HTMLInputElement);\n\n const input = nextInput(target);\n\n input?.focus();\n requestAnimationFrame(() => {\n input?.select();\n });\n}\n\nconst syntheticEvent = new InputEvent('input');\n\nfunction handleBackspace(event: KeyboardEvent) {\n if (event.key !== 'Backspace') return;\n\n /**\n * We have to prevent default because we\n * - want to clear the whole field\n * - have the focus behavior keep up with the key-repeat\n * speed of the user's computer\n */\n event.preventDefault();\n\n const target = event.target;\n\n if (target && 'value' in target) {\n if (target.value === '') {\n focusLeft({ target });\n } else {\n target.value = '';\n }\n }\n\n target?.dispatchEvent(syntheticEvent);\n}\n\nfunction previousInput(current: HTMLInputElement) {\n const inputs = getInputs(current);\n const currentIndex = inputs.indexOf(current);\n\n return inputs[currentIndex - 1];\n}\n\nexport const autoAdvance = (event: Event) => {\n assert(\n '[BUG]: autoAdvance called on non-input element',\n event.target instanceof HTMLInputElement\n );\n\n const value = event.target.value;\n\n if (value.length === 0) return;\n\n if (value.length > 0) {\n if ('data' in event && event.data && typeof event.data === 'string') {\n event.target.value = event.data;\n }\n\n return focusRight(event);\n }\n};\n\nexport function getCollectiveValue(elementTarget: EventTarget | null, length: number) {\n if (!elementTarget) return;\n\n assert(\n `[BUG]: somehow the element target is not HTMLElement`,\n elementTarget instanceof HTMLElement\n );\n\n let parent: null | HTMLElement | ShadowRoot;\n\n // TODO: should this logic be extracted?\n // why is getting the target element within a shadow root hard?\n if (!(elementTarget instanceof HTMLInputElement)) {\n if (elementTarget.shadowRoot) {\n parent = elementTarget.shadowRoot;\n } else {\n parent = elementTarget.closest('fieldset');\n }\n } else {\n parent = elementTarget.closest('fieldset');\n }\n\n assert(`[BUG]: somehow the input fields were rendered without a parent element`, parent);\n\n const elements = parent.querySelectorAll('input');\n\n let value = '';\n\n assert(\n `found elements (${elements.length}) do not match length (${length}). Was the same OTP input rendered more than once?`,\n elements.length === length\n );\n\n for (const element of elements) {\n assert(\n '[BUG]: how did the queried elements become a non-input element?',\n element instanceof HTMLInputElement\n );\n value += element.value;\n }\n\n return value;\n}\n"],"names":["getInputs","current","fieldset","closest","assert","querySelectorAll","nextInput","inputs","currentIndex","indexOf","selectAll","event","target","HTMLInputElement","select","handlePaste","clipboardData","preventDefault","value","getData","digits","i","currElement","next","handleNavigation","key","handleBackspace","focusLeft","focusRight","input","previousInput","focus","requestAnimationFrame","syntheticEvent","InputEvent","dispatchEvent","autoAdvance","length","data","getCollectiveValue","elementTarget","HTMLElement","parent","shadowRoot","elements","element"],"mappings":"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"}
|
|
@@ -11,8 +11,8 @@ import { precompileTemplate } from '@ember/template-compilation';
|
|
|
11
11
|
import { setComponentTemplate } from '@ember/component';
|
|
12
12
|
import templateOnly from '@ember/component/template-only';
|
|
13
13
|
|
|
14
|
-
function getElementTag(
|
|
15
|
-
return
|
|
14
|
+
function getElementTag(tagName) {
|
|
15
|
+
return tagName || "div";
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Allows lazy evaluation of the portal target (do nothing until rendered)
|
|
@@ -28,69 +28,69 @@ const Content = setComponentTemplate(precompileTemplate("\n {{#let (element (ge
|
|
|
28
28
|
})
|
|
29
29
|
}), templateOnly());
|
|
30
30
|
const arrowSides = {
|
|
31
|
-
top:
|
|
32
|
-
right:
|
|
33
|
-
bottom:
|
|
34
|
-
left:
|
|
31
|
+
top: "bottom",
|
|
32
|
+
right: "left",
|
|
33
|
+
bottom: "top",
|
|
34
|
+
left: "right"
|
|
35
35
|
};
|
|
36
|
-
const attachArrow = modifier((
|
|
37
|
-
if (
|
|
38
|
-
if (!
|
|
39
|
-
if (!
|
|
40
|
-
|
|
41
|
-
arrow
|
|
42
|
-
} =
|
|
43
|
-
|
|
44
|
-
placement
|
|
45
|
-
} =
|
|
46
|
-
if (!
|
|
47
|
-
if (!
|
|
48
|
-
|
|
49
|
-
x:
|
|
50
|
-
y:
|
|
51
|
-
} =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Object.assign(
|
|
55
|
-
left:
|
|
56
|
-
top:
|
|
57
|
-
right:
|
|
58
|
-
bottom:
|
|
59
|
-
[
|
|
36
|
+
const attachArrow = modifier((element, _, named) => {
|
|
37
|
+
if (element === named.arrowElement.current) {
|
|
38
|
+
if (!named.data) return;
|
|
39
|
+
if (!named.data.middlewareData) return;
|
|
40
|
+
const {
|
|
41
|
+
arrow
|
|
42
|
+
} = named.data.middlewareData;
|
|
43
|
+
const {
|
|
44
|
+
placement
|
|
45
|
+
} = named.data;
|
|
46
|
+
if (!arrow) return;
|
|
47
|
+
if (!placement) return;
|
|
48
|
+
const {
|
|
49
|
+
x: arrowX,
|
|
50
|
+
y: arrowY
|
|
51
|
+
} = arrow;
|
|
52
|
+
const otherSide = placement.split("-")[0];
|
|
53
|
+
const staticSide = arrowSides[otherSide];
|
|
54
|
+
Object.assign(named.arrowElement.current.style, {
|
|
55
|
+
left: arrowX != null ? `${arrowX}px` : "",
|
|
56
|
+
top: arrowY != null ? `${arrowY}px` : "",
|
|
57
|
+
right: "",
|
|
58
|
+
bottom: "",
|
|
59
|
+
[staticSide]: "-4px"
|
|
60
60
|
});
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
(async () => {
|
|
63
|
+
void (async () => {
|
|
64
64
|
await Promise.resolve();
|
|
65
|
-
|
|
65
|
+
named.arrowElement.set(element);
|
|
66
66
|
})();
|
|
67
67
|
});
|
|
68
68
|
const ArrowElement = () => cell();
|
|
69
|
-
function maybeAddArrow(
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
element
|
|
69
|
+
function maybeAddArrow(middleware, element) {
|
|
70
|
+
const result = [...(middleware || [])];
|
|
71
|
+
if (element) {
|
|
72
|
+
result.push(arrow({
|
|
73
|
+
element
|
|
74
74
|
}));
|
|
75
75
|
}
|
|
76
|
-
return
|
|
76
|
+
return result;
|
|
77
77
|
}
|
|
78
|
-
function flipOptions(
|
|
78
|
+
function flipOptions(options) {
|
|
79
79
|
return {
|
|
80
|
-
elementContext:
|
|
81
|
-
...
|
|
80
|
+
elementContext: "reference",
|
|
81
|
+
...options
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
const Popover = setComponentTemplate(precompileTemplate("\n {{#let (ArrowElement) as |arrowElement|}}\n <FloatingUI @placement={{@placement}} @strategy={{@strategy}} @middleware={{maybeAddArrow @middleware arrowElement.current}} @flipOptions={{flipOptions @flipOptions}} @shiftOptions={{@shiftOptions}} @offsetOptions={{@offsetOptions}} as |reference floating extra|>\n {{yield (hash reference=reference setReference=extra.setReference Content=(component Content floating=floating inline=@inline) data=extra.data arrow=
|
|
84
|
+
const Popover = setComponentTemplate(precompileTemplate("\n {{#let (ArrowElement) as |arrowElement|}}\n <FloatingUI @placement={{@placement}} @strategy={{@strategy}} @middleware={{maybeAddArrow @middleware arrowElement.current}} @flipOptions={{flipOptions @flipOptions}} @shiftOptions={{@shiftOptions}} @offsetOptions={{@offsetOptions}} as |reference floating extra|>\n {{#let (modifier attachArrow arrowElement=arrowElement data=extra.data) as |arrow|}}\n {{yield (hash reference=reference setReference=extra.setReference Content=(component Content floating=floating inline=@inline) data=extra.data arrow=arrow)}}\n {{/let}}\n </FloatingUI>\n {{/let}}\n", {
|
|
85
85
|
strictMode: true,
|
|
86
86
|
scope: () => ({
|
|
87
87
|
ArrowElement,
|
|
88
88
|
FloatingUI,
|
|
89
89
|
maybeAddArrow,
|
|
90
90
|
flipOptions,
|
|
91
|
+
attachArrow,
|
|
91
92
|
hash,
|
|
92
|
-
Content
|
|
93
|
-
attachArrow
|
|
93
|
+
Content
|
|
94
94
|
})
|
|
95
95
|
}), templateOnly());
|
|
96
96
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"popover.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"popover.js","sources":["../../src/components/popover.gts"],"sourcesContent":["import { hash } from \"@ember/helper\";\n\nimport { arrow } from \"@floating-ui/dom\";\nimport { element } from \"ember-element-helper\";\nimport { modifier as eModifier } from \"ember-modifier\";\nimport { cell } from \"ember-resources\";\n\nimport { FloatingUI } from \"../floating-ui.ts\";\nimport { Portal } from \"./portal.gts\";\nimport { TARGETS } from \"./portal-targets.gts\";\n\nimport type { Signature as FloatingUiComponentSignature } from \"../floating-ui/component.ts\";\nimport type { Signature as HookSignature } from \"../floating-ui/modifier.ts\";\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { ElementContext, Middleware } from \"@floating-ui/dom\";\nimport type { ModifierLike, WithBoundArgs } from \"@glint/template\";\n\nexport interface Signature {\n Args: {\n /**\n * See the Floating UI's [flip docs](https://floating-ui.com/docs/flip) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n flipOptions?: HookSignature[\"Args\"][\"Named\"][\"flipOptions\"];\n /**\n * Array of one or more objects to add to Floating UI's list of [middleware](https://floating-ui.com/docs/middleware)\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n middleware?: HookSignature[\"Args\"][\"Named\"][\"middleware\"];\n /**\n * See the Floating UI's [offset docs](https://floating-ui.com/docs/offset) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n offsetOptions?: HookSignature[\"Args\"][\"Named\"][\"offsetOptions\"];\n /**\n * One of the possible [`placements`](https://floating-ui.com/docs/computeposition#placement). The default is 'bottom'.\n *\n * Possible values are\n * - top\n * - bottom\n * - right\n * - left\n *\n * And may optionally have `-start` or `-end` added to adjust position along the side.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n placement?: `${\"top\" | \"bottom\" | \"left\" | \"right\"}${\"\" | \"-start\" | \"-end\"}`;\n /**\n * See the Floating UI's [shift docs](https://floating-ui.com/docs/shift) for possible values.\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n shiftOptions?: HookSignature[\"Args\"][\"Named\"][\"shiftOptions\"];\n /**\n * CSS position property, either `fixed` or `absolute`.\n *\n * Pros and cons of each strategy are explained on [Floating UI's Docs](https://floating-ui.com/docs/computePosition#strategy)\n *\n * This argument is forwarded to the `<FloatingUI>` component.\n */\n strategy?: HookSignature[\"Args\"][\"Named\"][\"strategy\"];\n\n /**\n * By default, the popover is portaled.\n * If you don't control your CSS, and the positioning of the popover content\n * is misbehaving, you may pass \"@inline={{true}}\" to opt out of portalling.\n *\n * Inline may also be useful in nested menus, where you know exactly how the nesting occurs\n */\n inline?: boolean;\n };\n Blocks: {\n default: [\n {\n reference: FloatingUiComponentSignature[\"Blocks\"][\"default\"][0];\n setReference: FloatingUiComponentSignature[\"Blocks\"][\"default\"][2][\"setReference\"];\n Content: WithBoundArgs<typeof Content, \"floating\">;\n data: FloatingUiComponentSignature[\"Blocks\"][\"default\"][2][\"data\"];\n arrow: ModifierLike<{ Element: HTMLElement }>;\n },\n ];\n };\n}\n\nfunction getElementTag(tagName: undefined | string) {\n return tagName || \"div\";\n}\n\n/**\n * Allows lazy evaluation of the portal target (do nothing until rendered)\n * This is useful because the algorithm for finding the portal target isn't cheap.\n */\nconst Content: TOC<{\n Element: HTMLDivElement;\n Args: {\n floating: ModifierLike<{ Element: HTMLElement }>;\n inline?: boolean;\n /**\n * By default the popover content is wrapped in a div.\n * You may change this by supplying the name of an element here.\n *\n * For example:\n * ```gjs\n * <Popover as |p|>\n * <p.Content @as=\"dialog\">\n * this is now focus trapped\n * </p.Content>\n * </Popover>\n * ```\n */\n as?: string;\n };\n Blocks: { default: [] };\n}> = <template>\n {{#let (element (getElementTag @as)) as |El|}}\n {{#if @inline}}\n {{! @glint-ignore\n https://github.com/tildeio/ember-element-helper/issues/91\n https://github.com/typed-ember/glint/issues/610\n }}\n <El {{@floating}} ...attributes>\n {{yield}}\n </El>\n {{else}}\n <Portal @to={{TARGETS.popover}}>\n {{! @glint-ignore\n https://github.com/tildeio/ember-element-helper/issues/91\n https://github.com/typed-ember/glint/issues/610\n }}\n <El {{@floating}} ...attributes>\n {{yield}}\n </El>\n </Portal>\n {{/if}}\n {{/let}}\n</template>;\n\ninterface AttachArrowSignature {\n Element: HTMLElement;\n Args: {\n Named: {\n arrowElement: ReturnType<typeof ArrowElement>;\n data:\n | undefined\n | {\n placement: string;\n middlewareData?: {\n arrow?: { x?: number; y?: number };\n };\n };\n };\n };\n}\n\nconst arrowSides = {\n top: \"bottom\",\n right: \"left\",\n bottom: \"top\",\n left: \"right\",\n};\n\ntype Direction = \"top\" | \"bottom\" | \"left\" | \"right\";\ntype Placement = `${Direction}${\"\" | \"-start\" | \"-end\"}`;\n\nconst attachArrow: ModifierLike<AttachArrowSignature> = eModifier<AttachArrowSignature>(\n (element, _: [], named) => {\n if (element === named.arrowElement.current) {\n if (!named.data) return;\n if (!named.data.middlewareData) return;\n\n const { arrow } = named.data.middlewareData;\n const { placement } = named.data;\n\n if (!arrow) return;\n if (!placement) return;\n\n const { x: arrowX, y: arrowY } = arrow;\n const otherSide = (placement as Placement).split(\"-\")[0] as Direction;\n const staticSide = arrowSides[otherSide];\n\n Object.assign(named.arrowElement.current.style, {\n left: arrowX != null ? `${arrowX}px` : \"\",\n top: arrowY != null ? `${arrowY}px` : \"\",\n right: \"\",\n bottom: \"\",\n [staticSide]: \"-4px\",\n });\n\n return;\n }\n\n void (async () => {\n await Promise.resolve();\n named.arrowElement.set(element);\n })();\n },\n);\n\nconst ArrowElement: () => ReturnType<typeof cell<HTMLElement>> = () => cell<HTMLElement>();\n\nfunction maybeAddArrow(middleware: Middleware[] | undefined, element: Element | undefined) {\n const result = [...(middleware || [])];\n\n if (element) {\n result.push(arrow({ element }));\n }\n\n return result;\n}\n\nfunction flipOptions(options: HookSignature[\"Args\"][\"Named\"][\"flipOptions\"]) {\n return {\n elementContext: \"reference\" as ElementContext,\n ...options,\n };\n}\n\nexport const Popover: TOC<Signature> = <template>\n {{#let (ArrowElement) as |arrowElement|}}\n <FloatingUI\n @placement={{@placement}}\n @strategy={{@strategy}}\n @middleware={{maybeAddArrow @middleware arrowElement.current}}\n @flipOptions={{flipOptions @flipOptions}}\n @shiftOptions={{@shiftOptions}}\n @offsetOptions={{@offsetOptions}}\n as |reference floating extra|\n >\n {{#let (modifier attachArrow arrowElement=arrowElement data=extra.data) as |arrow|}}\n {{yield\n (hash\n reference=reference\n setReference=extra.setReference\n Content=(component Content floating=floating inline=@inline)\n data=extra.data\n arrow=arrow\n )\n }}\n {{/let}}\n </FloatingUI>\n {{/let}}\n</template>;\n\nexport default Popover;\n"],"names":["getElementTag","tagName","Content","setComponentTemplate","precompileTemplate","strictMode","scope","element","Portal","TARGETS","templateOnly","arrowSides","top","right","bottom","left","attachArrow","eModifier","_","named","arrowElement","current","data","middlewareData","arrow","placement","x","arrowX","y","arrowY","otherSide","split","staticSide","Object","assign","style","Promise","resolve","set","ArrowElement","cell","maybeAddArrow","middleware","result","push","flipOptions","options","elementContext","Popover","FloatingUI","hash"],"mappings":";;;;;;;;;;;;;AAwFA,SAASA,aAAcA,CAAAC,OAA2B,EAAA;EAChD,OAAOA,OAAW,IAAA,KAAA;AACpB;AAEA;;;AAGC;AACD,MAAMC,OAqBD,GAAAC,oBAAA,CAAAC,kBAAA,CAsBL,+pBAAA,EAAA;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;IAAAC,OAAA;IAAAP,aAAA;IAAAQ,MAAA;AAAAC,IAAAA;AAAA,GAAA;AAAU,CAAA,CAAA,EAAAC,YAAA,EAAA,CAAA;AAmBV,MAAMC,UAAa,GAAA;AACjBC,EAAAA,GAAK,EAAA,QAAA;AACLC,EAAAA,KAAO,EAAA,MAAA;AACPC,EAAAA,MAAQ,EAAA,KAAA;AACRC,EAAAA,IAAM,EAAA;AACR,CAAA;AAKA,MAAMC,WAA0B,GAAwBC,SACtD,CAACV,SAASW,CAAO,EAAAC,KAAA,KAAA;AACf,EAAA,IAAIZ,OAAY,KAAAY,KAAA,CAAMC,YAAY,CAACC,OAAO,EAAE;AAC1C,IAAA,IAAI,CAACF,KAAM,CAAAG,IAAI,EAAE;AACjB,IAAA,IAAI,CAACH,KAAA,CAAMG,IAAI,CAACC,cAAc,EAAE;IAEhC,MAAM;AAAEC,MAAAA;AAAK,KAAE,GAAGL,KAAM,CAAAG,IAAI,CAACC,cAAc;IAC3C,MAAM;AAAEE,MAAAA;KAAW,GAAGN,MAAMG,IAAI;IAEhC,IAAI,CAACE,KAAO,EAAA;IACZ,IAAI,CAACC,SAAW,EAAA;IAEhB,MAAM;AAAEC,MAAAA,CAAG,EAAAC,MAAM;AAAEC,MAAAA,CAAG,EAAAC;KAAQ,GAAGL,KAAA;IACjC,MAAMM,SAAA,GAAaL,SAAA,CAAwBM,KAAK,CAAC,GAAA,CAAI,CAAC,CAAA,CAAM;AAC5D,IAAA,MAAMC,UAAA,GAAarB,UAAU,CAACmB,SAAU,CAAA;IAExCG,MAAO,CAAAC,MAAM,CAACf,KAAM,CAAAC,YAAY,CAACC,OAAO,CAACc,KAAK,EAAE;MAC9CpB,IAAA,EAAMY,UAAU,IAAO,GAAA,CAAGA,EAAAA,MAAO,CAAG,EAAA,CAAA,GAAG,EAAA;MACvCf,GAAA,EAAKiB,UAAU,IAAO,GAAA,CAAGA,EAAAA,MAAO,CAAG,EAAA,CAAA,GAAG,EAAA;AACtChB,MAAAA,KAAO,EAAA,EAAA;AACPC,MAAAA,MAAQ,EAAA,EAAA;AACR,MAAA,CAACkB,aAAa;AAChB,KAAA,CAAA;AAEA,IAAA;AACF;AAEA,EAAA,KAAK,CAAC,YAAA;AACJ,IAAA,MAAMI,QAAQC,OAAO,EAAA;AACrBlB,IAAAA,KAAM,CAAAC,YAAY,CAACkB,GAAG,CAAC/B,OAAA,CAAA;AACzB,GAAC,GAAA;AACH,CAAA,CAAA;AAGF,MAAMgC,YAA2D,GAAAA,MAAMC,IAAK,EAAA;AAE5E,SAASC,cAAcC,UAAoC,EAAEnC,OAA4B,EAAA;EACvF,MAAMoC,MAAS,GAAA,CAAI,IAACD,cAAc,EAAE,CAAA,CAAE;AAEtC,EAAA,IAAInC,OAAS,EAAA;AACXoC,IAAAA,MAAO,CAAAC,IAAI,CAACpB,KAAM,CAAA;AAAEjB,MAAAA;AAAQ,KAAA,CAAA,CAAA;AAC9B;AAEA,EAAA,OAAOoC,MAAA;AACT;AAEA,SAASE,WAAYA,CAAAC,OAAsD,EAAA;EACzE,OAAO;AACLC,IAAAA,cAAA,EAAgB,WAAe;IAC/B,GAAGD;GACL;AACF;MAEaE,OAAa,GAAA7C,oBAAA,CAAaC,kBAAA,CAwBvC,gnBAAA,EAAA;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;IAAAiC,YAAA;IAAAU,UAAA;IAAAR,aAAA;IAAAI,WAAA;IAAA7B,WAAA;IAAAkC,IAAA;AAAAhD,IAAAA;AAAA,GAAA;AAAU,CAAE,CAAA,EAAAQ,YAAA,EAAA;;;;"}
|
|
@@ -5,26 +5,26 @@ import { setComponentTemplate } from '@ember/component';
|
|
|
5
5
|
import templateOnly from '@ember/component/template-only';
|
|
6
6
|
|
|
7
7
|
const TARGETS = Object.freeze({
|
|
8
|
-
popover:
|
|
9
|
-
tooltip:
|
|
10
|
-
modal:
|
|
8
|
+
popover: "ember-primitives__portal-targets__popover",
|
|
9
|
+
tooltip: "ember-primitives__portal-targets__tooltip",
|
|
10
|
+
modal: "ember-primitives__portal-targets__modal"
|
|
11
11
|
});
|
|
12
|
-
function findNearestTarget(
|
|
13
|
-
assert(`first argument to \`findNearestTarget\` must be an element`,
|
|
14
|
-
assert(`second argument to \`findNearestTarget\` must be a string`, typeof
|
|
15
|
-
let
|
|
16
|
-
let
|
|
17
|
-
while (!
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
|
|
12
|
+
function findNearestTarget(origin, name) {
|
|
13
|
+
assert(`first argument to \`findNearestTarget\` must be an element`, origin instanceof Element);
|
|
14
|
+
assert(`second argument to \`findNearestTarget\` must be a string`, typeof name === `string`);
|
|
15
|
+
let element = null;
|
|
16
|
+
let parent = origin.parentNode;
|
|
17
|
+
while (!element && parent) {
|
|
18
|
+
element = parent.querySelector(`[data-portal-name=${name}]`);
|
|
19
|
+
if (element) break;
|
|
20
|
+
parent = parent.parentNode;
|
|
21
21
|
}
|
|
22
22
|
if (macroCondition(isDevelopingApp())) {
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-
|
|
24
|
-
window.prime0 =
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
24
|
+
window.prime0 = origin;
|
|
25
25
|
}
|
|
26
|
-
assert(`Could not find element by the given name: \`${
|
|
27
|
-
return
|
|
26
|
+
assert(`Could not find element by the given name: \`${name}\`.` + ` The known names are ` + `${Object.values(TARGETS).join(", ")} ` + `-- but any name will work as long as it is set to the \`data-portal-name\` attribute. ` + `Double check that the element you're wanting to portal to is rendered. ` + `The element passed to \`findNearestTarget\` is stored on \`window.prime0\` ` + `You can debug in your browser's console via ` + `\`document.querySelector('[data-portal-name="${name}"]')\``, element);
|
|
27
|
+
return element;
|
|
28
28
|
}
|
|
29
29
|
const PortalTargets = setComponentTemplate(precompileTemplate("\n <div data-portal-name={{TARGETS.popover}}></div>\n <div data-portal-name={{TARGETS.tooltip}}></div>\n <div data-portal-name={{TARGETS.modal}}></div>\n", {
|
|
30
30
|
strictMode: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal-targets.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"portal-targets.js","sources":["../../src/components/portal-targets.gts"],"sourcesContent":["import { assert } from \"@ember/debug\";\nimport { isDevelopingApp, macroCondition } from \"@embroider/macros\";\n\nimport type { TOC } from \"@ember/component/template-only\";\n\nexport const TARGETS = Object.freeze({\n popover: \"ember-primitives__portal-targets__popover\",\n tooltip: \"ember-primitives__portal-targets__tooltip\",\n modal: \"ember-primitives__portal-targets__modal\",\n});\n\nexport function findNearestTarget(origin: Element, name: string) {\n assert(`first argument to \\`findNearestTarget\\` must be an element`, origin instanceof Element);\n assert(`second argument to \\`findNearestTarget\\` must be a string`, typeof name === `string`);\n\n let element: Element | null = null;\n\n let parent = origin.parentNode;\n\n while (!element && parent) {\n element = parent.querySelector(`[data-portal-name=${name}]`);\n if (element) break;\n parent = parent.parentNode;\n }\n\n if (macroCondition(isDevelopingApp())) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n (window as any).prime0 = origin;\n }\n\n assert(\n `Could not find element by the given name: \\`${name}\\`.` +\n ` The known names are ` +\n `${Object.values(TARGETS).join(\", \")} ` +\n `-- but any name will work as long as it is set to the \\`data-portal-name\\` attribute. ` +\n `Double check that the element you're wanting to portal to is rendered. ` +\n `The element passed to \\`findNearestTarget\\` is stored on \\`window.prime0\\` ` +\n `You can debug in your browser's console via ` +\n `\\`document.querySelector('[data-portal-name=\"${name}\"]')\\``,\n element,\n );\n\n return element;\n}\n\nexport interface Signature {\n Element: null;\n}\n\nexport const PortalTargets: TOC<Signature> = <template>\n <div data-portal-name={{TARGETS.popover}}></div>\n <div data-portal-name={{TARGETS.tooltip}}></div>\n <div data-portal-name={{TARGETS.modal}}></div>\n</template>;\n\nexport default PortalTargets;\n"],"names":["TARGETS","Object","freeze","popover","tooltip","modal","findNearestTarget","origin","name","assert","Element","element","parent","parentNode","querySelector","macroCondition","isDevelopingApp","window","prime0","values","join","PortalTargets","setComponentTemplate","precompileTemplate","strictMode","scope","templateOnly"],"mappings":";;;;;;MAKaA,OAAA,GAAUC,MAAO,CAAAC,MAAM,CAAC;AACnCC,EAAAA,OAAS,EAAA,2CAAA;AACTC,EAAAA,OAAS,EAAA,2CAAA;AACTC,EAAAA,KAAO,EAAA;AACT,CAAG;AAEI,SAASC,iBAAkBA,CAAAC,MAAe,EAAEC,IAAY,EAAA;AAC7DC,EAAAA,MAAA,CAAO,CAA4D,0DAAA,CAAA,EAAEF,MAAkB,YAAAG,OAAA,CAAA;AACvFD,EAAAA,MAAO,CAAA,CAA2D,yDAAA,CAAA,EAAE,OAAOD,IAAS,KAAA,QAAQ,CAAA;EAE5F,IAAIG,OAAuB,GAAG,IAAA;AAE9B,EAAA,IAAIC,MAAA,GAASL,OAAOM,UAAU;AAE9B,EAAA,OAAO,CAACF,WAAWC,MAAQ,EAAA;IACzBD,OAAU,GAAAC,MAAA,CAAOE,aAAa,CAAC,CAAqBN,kBAAAA,EAAAA,IAAA,GAAO,CAAA;AAC3D,IAAA,IAAIG,OAAS,EAAA;IACbC,MAAA,GAASA,OAAOC,UAAU;AAC5B;AAEA,EAAA,IAAIE,eAAeC,eAAoB,EAAA,CAAA,EAAA;AACrC;IACCC,MAAU,CAAKC,MAAM,GAAGX,MAAA;AAC3B;AAEAE,EAAAA,MACE,CAAA,CAAA,4CAAA,EAA+CD,IAAA,CAAA,GAAA,CAAS,GACtD,CAAuB,qBAAA,CAAA,GACvB,CAAGP,EAAAA,OAAOkB,MAAM,CAACnB,SAASoB,IAAI,CAAC,MAAQ,CAAA,CAAA,GACvC,CAAwF,sFAAA,CAAA,GACxF,yEAAyE,GACzE,CAAA,2EAAA,CAA6E,GAC7E,CAAA,4CAAA,CAA8C,GAC9C,CAAgDZ,6CAAAA,EAAAA,IAAA,CAAY,MAAA,CAAA,EAC9DG,OAAA,CAAA;AAGF,EAAA,OAAOA,OAAA;AACT;MAMaU,aAAmB,GAAAC,oBAAA,CAAaC,kBAAA,CAI7C,8JAAA,EAAA;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;AAAAzB,IAAAA;AAAA,GAAA;AAAU,CAAE,CAAA,EAAA0B,YAAA,EAAA;;;;"}
|
|
@@ -5,9 +5,11 @@ import { precompileTemplate } from '@ember/template-compilation';
|
|
|
5
5
|
import { setComponentTemplate } from '@ember/component';
|
|
6
6
|
import templateOnly from '@ember/component/template-only';
|
|
7
7
|
|
|
8
|
-
const anchor = modifier((
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const anchor = modifier((element, [to, update]) => {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
|
|
10
|
+
const found = findNearestTarget(element, to);
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
12
|
+
update(found);
|
|
11
13
|
});
|
|
12
14
|
const ElementValue = () => cell();
|
|
13
15
|
const Portal = setComponentTemplate(precompileTemplate("\n {{#let (ElementValue) as |target|}}\n {{!-- This div is always going to be empty,\n because it'll either find the portal and render content elsewhere,\n it it won't find the portal and won't render anything.\n --}}\n {{!-- template-lint-disable no-inline-styles --}}\n <div style=\"display:contents;\" {{anchor @to target.set}}>\n {{#if target.current}}\n {{#in-element target.current}}\n {{yield}}\n {{/in-element}}\n {{/if}}\n </div>\n {{/let}}\n", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"portal.js","sources":["../../src/components/portal.gts"],"sourcesContent":["import { modifier } from \"ember-modifier\";\nimport { cell } from \"ember-resources\";\n\nimport { findNearestTarget, type TARGETS } from \"./portal-targets.gts\";\n\nimport type { TOC } from \"@ember/component/template-only\";\n\ntype Targets = (typeof TARGETS)[keyof typeof TARGETS];\n\nexport interface Signature {\n Args: {\n /**\n * The name of the PortalTarget to render in to.\n * This is the value of the `data-portal-name` attribute\n * of the element you wish to render in to.\n */\n // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\n to: Targets | (string & {});\n };\n Blocks: {\n /**\n * The portaled content\n */\n default: [];\n };\n}\n\nconst anchor = modifier(\n (element: Element, [to, update]: [string, ReturnType<typeof ElementValue>[\"set\"]]) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call\n const found = findNearestTarget(element, to);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n update(found);\n },\n);\n\nconst ElementValue = () => cell<Element | ShadowRoot>();\n\nexport const Portal: TOC<Signature> = <template>\n {{#let (ElementValue) as |target|}}\n {{! This div is always going to be empty,\n because it'll either find the portal and render content elsewhere,\n it it won't find the portal and won't render anything.\n }}\n {{! template-lint-disable no-inline-styles }}\n <div style=\"display:contents;\" {{anchor @to target.set}}>\n {{#if target.current}}\n {{#in-element target.current}}\n {{yield}}\n {{/in-element}}\n {{/if}}\n </div>\n {{/let}}\n</template>;\n\nexport default Portal;\n"],"names":["anchor","modifier","element","to","update","found","findNearestTarget","ElementValue","cell","Portal","setComponentTemplate","precompileTemplate","strictMode","scope","templateOnly"],"mappings":";;;;;;;AA2BA,MAAMA,MAAS,GAAAC,QAAA,CACb,CAACC,OAAkB,EAAA,CAACC,EAAI,EAAAC,MAAA,CAAyD,KAAA;AAC/E;AACA,EAAA,MAAMC,KAAA,GAAQC,kBAAkBJ,OAAS,EAAAC,EAAA,CAAA;AAEzC;EACAC,MAAO,CAAAC,KAAA,CAAA;AACT,CAAA,CAAA;AAGF,MAAME,YAAA,GAAeA,MAAMC,IAAA,EAAe;MAE7BC,MAAY,GAAAC,oBAAA,CAAaC,kBAAA,CAetC,ygBAAA,EAAA;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;IAAAN,YAAA;AAAAP,IAAAA;AAAA,GAAA;AAAU,CAAE,CAAA,EAAAc,YAAA,EAAA;;;;"}
|
|
@@ -8,29 +8,29 @@ const DEFAULT_MAX = 100;
|
|
|
8
8
|
/**
|
|
9
9
|
* Non-negative, non-NaN, non-Infinite, positive, rational
|
|
10
10
|
*/
|
|
11
|
-
function isValidProgressNumber(
|
|
12
|
-
if (typeof
|
|
13
|
-
if (!Number.isFinite(
|
|
14
|
-
return
|
|
11
|
+
function isValidProgressNumber(value) {
|
|
12
|
+
if (typeof value !== "number") return false;
|
|
13
|
+
if (!Number.isFinite(value)) return false;
|
|
14
|
+
return value >= 0;
|
|
15
15
|
}
|
|
16
|
-
function progressState(
|
|
17
|
-
return
|
|
16
|
+
function progressState(value, maxValue) {
|
|
17
|
+
return value == null ? "indeterminate" : value === maxValue ? "complete" : "loading";
|
|
18
18
|
}
|
|
19
|
-
function getMax(
|
|
20
|
-
return isValidProgressNumber(
|
|
19
|
+
function getMax(userMax) {
|
|
20
|
+
return isValidProgressNumber(userMax) ? userMax : DEFAULT_MAX;
|
|
21
21
|
}
|
|
22
|
-
function getValue(
|
|
23
|
-
|
|
24
|
-
if (!isValidProgressNumber(
|
|
22
|
+
function getValue(userValue, maxValue) {
|
|
23
|
+
const max = getMax(maxValue);
|
|
24
|
+
if (!isValidProgressNumber(userValue)) {
|
|
25
25
|
return 0;
|
|
26
26
|
}
|
|
27
|
-
if (
|
|
28
|
-
return
|
|
27
|
+
if (userValue > max) {
|
|
28
|
+
return max;
|
|
29
29
|
}
|
|
30
|
-
return
|
|
30
|
+
return userValue;
|
|
31
31
|
}
|
|
32
|
-
function getValueLabel(
|
|
33
|
-
return `${Math.round(
|
|
32
|
+
function getValueLabel(value, max) {
|
|
33
|
+
return `${Math.round(value / max * 100)}%`;
|
|
34
34
|
}
|
|
35
35
|
const Indicator = setComponentTemplate(precompileTemplate("\n <div ...attributes data-max={{@max}} data-value={{@value}} data-state={{progressState @value @max}} data-percent={{@percent}}>\n {{yield}}\n </div>\n", {
|
|
36
36
|
strictMode: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"progress.js","sources":["../../src/components/progress.gts"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { hash } from \"@ember/helper\";\n\nimport type { TOC } from \"@ember/component/template-only\";\nimport type { WithBoundArgs } from \"@glint/template\";\n\nexport interface Signature {\n Element: HTMLDivElement;\n Args: {\n /**\n * The current progress\n * This may be less than 0 or more than `max`,\n * but the resolved value (managed internally, and yielded out)\n * does not exceed the range [0, max]\n */\n value: number;\n /**\n * The max value, defaults to 100\n */\n max?: number;\n };\n Blocks: {\n default: [\n {\n /**\n * The indicator element with some state applied.\n * This can be used to style the progress of bar.\n */\n Indicator: WithBoundArgs<typeof Indicator, \"value\" | \"max\" | \"percent\">;\n /**\n * The value as a percent of how far along the indicator should be\n * positioned, between 0 and 100.\n * Will be rounded to two decimal places.\n */\n percent: number;\n /**\n * The value as a percent of how far along the indicator should be positioned,\n * between 0 and 1\n */\n decimal: number;\n /**\n * The resolved value within the limits of the progress bar.\n */\n value: number;\n },\n ];\n };\n}\n\ntype ProgressState = \"indeterminate\" | \"complete\" | \"loading\";\n\nconst DEFAULT_MAX = 100;\n\n/**\n * Non-negative, non-NaN, non-Infinite, positive, rational\n */\nfunction isValidProgressNumber(value: number | undefined | null): value is number {\n if (typeof value !== \"number\") return false;\n if (!Number.isFinite(value)) return false;\n\n return value >= 0;\n}\n\nfunction progressState(value: number | undefined | null, maxValue: number): ProgressState {\n return value == null ? \"indeterminate\" : value === maxValue ? \"complete\" : \"loading\";\n}\n\nfunction getMax(userMax: number | undefined | null): number {\n return isValidProgressNumber(userMax) ? userMax : DEFAULT_MAX;\n}\n\nfunction getValue(userValue: number | undefined | null, maxValue: number): number {\n const max = getMax(maxValue);\n\n if (!isValidProgressNumber(userValue)) {\n return 0;\n }\n\n if (userValue > max) {\n return max;\n }\n\n return userValue;\n}\n\nfunction getValueLabel(value: number, max: number) {\n return `${Math.round((value / max) * 100)}%`;\n}\n\nconst Indicator: TOC<{\n Element: HTMLDivElement;\n Args: { max: number; value: number; percent: number };\n Blocks: { default: [] };\n}> = <template>\n <div\n ...attributes\n data-max={{@max}}\n data-value={{@value}}\n data-state={{progressState @value @max}}\n data-percent={{@percent}}\n >\n {{yield}}\n </div>\n</template>;\n\nexport class Progress extends Component<Signature> {\n get max() {\n return getMax(this.args.max);\n }\n\n get value() {\n return getValue(this.args.value, this.max);\n }\n\n get valueLabel() {\n return getValueLabel(this.value, this.max);\n }\n\n get decimal() {\n return this.value / this.max;\n }\n\n get percent() {\n return Math.round(this.decimal * 100 * 100) / 100;\n }\n\n <template>\n <div\n ...attributes\n aria-valuemax={{this.max}}\n aria-valuemin=\"0\"\n aria-valuenow={{this.value}}\n aria-valuetext={{this.valueLabel}}\n role=\"progressbar\"\n data-value={{this.value}}\n data-state={{progressState this.value this.max}}\n data-max={{this.max}}\n data-min=\"0\"\n data-percent={{this.percent}}\n >\n\n {{yield\n (hash\n Indicator=(component Indicator value=this.value max=this.max percent=this.percent)\n value=this.value\n percent=this.percent\n decimal=this.decimal\n )\n }}\n </div>\n </template>\n}\n\nexport default Progress;\n"],"names":["DEFAULT_MAX","isValidProgressNumber","value","Number","isFinite","progressState","maxValue","getMax","userMax","getValue","userValue","max","getValueLabel","Math","round","Indicator","setComponentTemplate","precompileTemplate","strictMode","scope","templateOnly","Progress","Component","args","valueLabel","decimal","percent","hash"],"mappings":";;;;;;AAmDA,MAAMA,WAAc,GAAA,GAAA;AAEpB;;;AAGA,SAASC,qBAAsBA,CAAAC,KAAgC,EAAkB;AAC/E,EAAA,IAAI,OAAOA,KAAU,KAAA,QAAA,EAAU,OAAO,KAAA;EACtC,IAAI,CAACC,MAAA,CAAOC,QAAQ,CAACF,QAAQ,OAAO,KAAA;EAEpC,OAAOA,KAAS,IAAA,CAAA;AAClB;AAEA,SAASG,aAAAA,CAAcH,KAAgC,EAAEI,QAAgB,EAAG;AAC1E,EAAA,OAAOJ,KAAS,IAAA,IAAA,GAAO,eAAkB,GAAAA,KAAA,KAAUI,WAAW,UAAa,GAAA,SAAA;AAC7E;AAEA,SAASC,MAAAA,CAAOC,OAAkC,EAAS;AACzD,EAAA,OAAOP,qBAAA,CAAsBO,WAAWA,OAAU,GAAAR,WAAA;AACpD;AAEA,SAASS,QAAAA,CAASC,SAAoC,EAAEJ,QAAgB,EAAS;AAC/E,EAAA,MAAMK,MAAMJ,MAAO,CAAAD,QAAA,CAAA;AAEnB,EAAA,IAAI,CAACL,sBAAsBS,SAAY,CAAA,EAAA;AACrC,IAAA,OAAO,CAAA;AACT;EAEA,IAAIA,YAAYC,GAAK,EAAA;AACnB,IAAA,OAAOA,GAAA;AACT;AAEA,EAAA,OAAOD,SAAA;AACT;AAEA,SAASE,aAAcA,CAAAV,KAAa,EAAES,GAAW,EAAA;EAC/C,OAAO,CAAA,EAAGE,IAAA,CAAKC,KAAK,CAAEZ,KAAA,GAAQS,GAAG,GAAI,GAAK,CAAA,CAAE,CAAA,CAAA;AAC9C;AAEA,MAAMI,SAID,GAAAC,oBAAA,CAAAC,kBAAA,CAUL,+JAAA,EAAA;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;AAAAd,IAAAA;AAAA,GAAA;AAAU,CAAA,CAAA,EAAAe,YAAA,EAAA,CAAA;AAEH,MAAMC,iBAAiBC,SAAU,CAAA;EACtC,IAAIX,GAAMA,GAAA;AACR,IAAA,OAAOJ,MAAO,CAAA,IAAI,CAACgB,IAAI,CAACZ,GAAG,CAAA;AAC7B;EAEA,IAAIT,KAAQA,GAAA;IACV,OAAOO,QAAA,CAAS,IAAI,CAACc,IAAI,CAACrB,KAAK,EAAE,IAAI,CAACS,GAAG,CAAA;AAC3C;EAEA,IAAIa,UAAaA,GAAA;IACf,OAAOZ,cAAc,IAAI,CAACV,KAAK,EAAE,IAAI,CAACS,GAAG,CAAA;AAC3C;EAEA,IAAIc,OAAUA,GAAA;AACZ,IAAA,OAAO,IAAI,CAACvB,KAAK,GAAG,IAAI,CAACS,GAAG;AAC9B;EAEA,IAAIe,OAAUA,GAAA;AACZ,IAAA,OAAOb,IAAA,CAAKC,KAAK,CAAC,IAAI,CAACW,OAAO,GAAG,MAAM,GAAO,CAAA,GAAA,GAAA;AAChD;AAEA,EAAA;IAAAT,oBAAA,CAAAC,kBAAA,CAwBA,qeAAA,EAAA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAd,aAAA;QAAAsB,IAAA;AAAAZ,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
|
|
@@ -7,8 +7,8 @@ import { setComponentTemplate } from '@ember/component';
|
|
|
7
7
|
|
|
8
8
|
class Scroller extends Component {
|
|
9
9
|
withinElement;
|
|
10
|
-
ref = modifier(
|
|
11
|
-
this.withinElement =
|
|
10
|
+
ref = modifier(el => {
|
|
11
|
+
this.withinElement = el;
|
|
12
12
|
});
|
|
13
13
|
#frame;
|
|
14
14
|
scrollToBottom = () => {
|
|
@@ -19,7 +19,7 @@ class Scroller extends Component {
|
|
|
19
19
|
if (isDestroyed(this) || isDestroying(this)) return;
|
|
20
20
|
this.withinElement.scrollTo({
|
|
21
21
|
top: this.withinElement.scrollHeight,
|
|
22
|
-
behavior:
|
|
22
|
+
behavior: "auto"
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
};
|
|
@@ -31,7 +31,7 @@ class Scroller extends Component {
|
|
|
31
31
|
if (isDestroyed(this) || isDestroying(this)) return;
|
|
32
32
|
this.withinElement.scrollTo({
|
|
33
33
|
top: 0,
|
|
34
|
-
behavior:
|
|
34
|
+
behavior: "auto"
|
|
35
35
|
});
|
|
36
36
|
});
|
|
37
37
|
};
|
|
@@ -43,7 +43,7 @@ class Scroller extends Component {
|
|
|
43
43
|
if (isDestroyed(this) || isDestroying(this)) return;
|
|
44
44
|
this.withinElement.scrollTo({
|
|
45
45
|
left: 0,
|
|
46
|
-
behavior:
|
|
46
|
+
behavior: "auto"
|
|
47
47
|
});
|
|
48
48
|
});
|
|
49
49
|
};
|
|
@@ -55,7 +55,7 @@ class Scroller extends Component {
|
|
|
55
55
|
if (isDestroyed(this) || isDestroying(this)) return;
|
|
56
56
|
this.withinElement.scrollTo({
|
|
57
57
|
left: this.withinElement.scrollWidth,
|
|
58
|
-
behavior:
|
|
58
|
+
behavior: "auto"
|
|
59
59
|
});
|
|
60
60
|
});
|
|
61
61
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scroller.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scroller.js","sources":["../../src/components/scroller.gts"],"sourcesContent":["import Component from \"@glimmer/component\";\nimport { isDestroyed, isDestroying } from \"@ember/destroyable\";\nimport { hash } from \"@ember/helper\";\n\nimport { modifier } from \"ember-modifier\";\n\n/**\n * Utility component for helping with scrolling in any direction within\n * any of the 4 directions: up, down, left, right.\n *\n * 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.\n */\nexport class Scroller extends Component<{\n /**\n * A containing element is required - in this case, a div.\n * It must be scrollable for this component to work, but can be customized.\n *\n * By default, this element will have some styling applied:\n * overflow: auto;\n *\n * By default, this element will have tabindex=\"0\" to support keyboard usage.\n *\n * The scroll-behavior is \"auto\", which can be controlled via CSS\n * https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior\n *\n */\n Element: HTMLDivElement;\n Blocks: {\n default: [\n {\n /**\n * Scroll the content to the bottom\n *\n * ```gjs\n * import { Scroller } from 'ember-primitives';\n *\n * <template>\n * <Scroller as |s|>\n * ...\n *\n * {{ (s.scrollToBottom) }}\n * </Scroller>\n * </template>\n * ```\n */\n scrollToBottom: () => void;\n /**\n * Scroll the content to the top\n *\n * ```gjs\n * import { Scroller } from 'ember-primitives';\n *\n * <template>\n * <Scroller as |s|>\n * ...\n *\n * {{ (s.scrollToTop) }}\n * </Scroller>\n * </template>\n * ```\n */\n scrollToTop: () => void;\n /**\n * Scroll the content to the left\n *\n * ```gjs\n * import { Scroller } from 'ember-primitives';\n *\n * <template>\n * <Scroller as |s|>\n * ...\n *\n * {{ (s.scrollToLeft) }}\n * </Scroller>\n * </template>\n * ```\n */\n scrollToLeft: () => void;\n /**\n * Scroll the content to the right\n *\n * ```gjs\n * import { Scroller } from 'ember-primitives';\n *\n * <template>\n * <Scroller as |s|>\n * ...\n *\n * {{ (s.scrollToRight) }}\n * </Scroller>\n * </template>\n * ```\n */\n scrollToRight: () => void;\n },\n ];\n };\n}> {\n declare withinElement: HTMLDivElement;\n\n ref = modifier((el: HTMLDivElement) => {\n this.withinElement = el;\n });\n\n #frame?: number;\n\n scrollToBottom = () => {\n if (this.#frame) {\n cancelAnimationFrame(this.#frame);\n }\n\n this.#frame = requestAnimationFrame(() => {\n if (isDestroyed(this) || isDestroying(this)) return;\n\n this.withinElement.scrollTo({\n top: this.withinElement.scrollHeight,\n behavior: \"auto\",\n });\n });\n };\n\n scrollToTop = () => {\n if (this.#frame) {\n cancelAnimationFrame(this.#frame);\n }\n\n this.#frame = requestAnimationFrame(() => {\n if (isDestroyed(this) || isDestroying(this)) return;\n\n this.withinElement.scrollTo({\n top: 0,\n behavior: \"auto\",\n });\n });\n };\n\n scrollToLeft = () => {\n if (this.#frame) {\n cancelAnimationFrame(this.#frame);\n }\n\n this.#frame = requestAnimationFrame(() => {\n if (isDestroyed(this) || isDestroying(this)) return;\n\n this.withinElement.scrollTo({\n left: 0,\n behavior: \"auto\",\n });\n });\n };\n\n scrollToRight = () => {\n if (this.#frame) {\n cancelAnimationFrame(this.#frame);\n }\n\n this.#frame = requestAnimationFrame(() => {\n if (isDestroyed(this) || isDestroying(this)) return;\n\n this.withinElement.scrollTo({\n left: this.withinElement.scrollWidth,\n behavior: \"auto\",\n });\n });\n };\n\n <template>\n <div tabindex=\"0\" ...attributes {{this.ref}}>\n {{yield\n (hash\n scrollToBottom=this.scrollToBottom\n scrollToTop=this.scrollToTop\n scrollToLeft=this.scrollToLeft\n scrollToRight=this.scrollToRight\n )\n }}\n </div>\n </template>\n}\n"],"names":["Scroller","Component","withinElement","ref","modifier","el","scrollToBottom","cancelAnimationFrame","requestAnimationFrame","isDestroyed","isDestroying","scrollTo","top","scrollHeight","behavior","scrollToTop","scrollToLeft","left","scrollToRight","scrollWidth","setComponentTemplate","precompileTemplate","strictMode","scope","hash"],"mappings":";;;;;;;AAYO,MAAMA,QAAiB,SAAAC,SAAA;EAsFpBC,aAAA;AAERC,EAAAA,GAAM,GAAAC,QAAA,CAAUC,EAAI,IAAA;IAClB,IAAI,CAACH,aAAa,GAAGG,EAAA;AACvB,GAAG,CAAA;AAEH,EAAA,MAAM;EAENC,cAAiB,GAAAA,MAAA;AACf,IAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACfC,MAAAA,oBAAqB,CAAA,IAAI,CAAC,MAAM,CAAA;AAClC;AAEA,IAAA,IAAI,CAAC,MAAM,GAAGC,qBAAsB,CAAA,MAAA;MAClC,IAAIC,WAAY,CAAA,IAAI,CAAK,IAAAC,YAAA,CAAa,IAAI,CAAG,EAAA;AAE7C,MAAA,IAAI,CAACR,aAAa,CAACS,QAAQ,CAAC;AAC1BC,QAAAA,GAAA,EAAK,IAAI,CAACV,aAAa,CAACW,YAAY;AACpCC,QAAAA,QAAU,EAAA;AACZ,OAAA,CAAA;AACF,KAAA,CAAA;GACA;EAEFC,WAAc,GAAAA,MAAA;AACZ,IAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACfR,MAAAA,oBAAqB,CAAA,IAAI,CAAC,MAAM,CAAA;AAClC;AAEA,IAAA,IAAI,CAAC,MAAM,GAAGC,qBAAsB,CAAA,MAAA;MAClC,IAAIC,WAAY,CAAA,IAAI,CAAK,IAAAC,YAAA,CAAa,IAAI,CAAG,EAAA;AAE7C,MAAA,IAAI,CAACR,aAAa,CAACS,QAAQ,CAAC;AAC1BC,QAAAA,GAAK,EAAA,CAAA;AACLE,QAAAA,QAAU,EAAA;AACZ,OAAA,CAAA;AACF,KAAA,CAAA;GACA;EAEFE,YAAe,GAAAA,MAAA;AACb,IAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACfT,MAAAA,oBAAqB,CAAA,IAAI,CAAC,MAAM,CAAA;AAClC;AAEA,IAAA,IAAI,CAAC,MAAM,GAAGC,qBAAsB,CAAA,MAAA;MAClC,IAAIC,WAAY,CAAA,IAAI,CAAK,IAAAC,YAAA,CAAa,IAAI,CAAG,EAAA;AAE7C,MAAA,IAAI,CAACR,aAAa,CAACS,QAAQ,CAAC;AAC1BM,QAAAA,IAAM,EAAA,CAAA;AACNH,QAAAA,QAAU,EAAA;AACZ,OAAA,CAAA;AACF,KAAA,CAAA;GACA;EAEFI,aAAgB,GAAAA,MAAA;AACd,IAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACfX,MAAAA,oBAAqB,CAAA,IAAI,CAAC,MAAM,CAAA;AAClC;AAEA,IAAA,IAAI,CAAC,MAAM,GAAGC,qBAAsB,CAAA,MAAA;MAClC,IAAIC,WAAY,CAAA,IAAI,CAAK,IAAAC,YAAA,CAAa,IAAI,CAAG,EAAA;AAE7C,MAAA,IAAI,CAACR,aAAa,CAACS,QAAQ,CAAC;AAC1BM,QAAAA,IAAA,EAAM,IAAI,CAACf,aAAa,CAACiB,WAAW;AACpCL,QAAAA,QAAU,EAAA;AACZ,OAAA,CAAA;AACF,KAAA,CAAA;GACA;AAEF,EAAA;IAAAM,oBAAA,CAAAC,kBAAA,CAWA,+NAAA,EAAA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
|
|
@@ -5,16 +5,16 @@ import { setComponentTemplate } from '@ember/component';
|
|
|
5
5
|
import templateOnly from '@ember/component/template-only';
|
|
6
6
|
|
|
7
7
|
const Shadow = () => {
|
|
8
|
-
|
|
8
|
+
const shadow = cell();
|
|
9
9
|
return {
|
|
10
10
|
get root() {
|
|
11
|
-
return
|
|
11
|
+
return shadow.current;
|
|
12
12
|
},
|
|
13
|
-
attach: modifier(
|
|
14
|
-
|
|
15
|
-
mode:
|
|
13
|
+
attach: modifier(element => {
|
|
14
|
+
const shadowRoot = element.attachShadow({
|
|
15
|
+
mode: "open"
|
|
16
16
|
});
|
|
17
|
-
|
|
17
|
+
const div = document.createElement("div");
|
|
18
18
|
// ember-source 5.6 broke the ability to in-element
|
|
19
19
|
// natively into a shadowroot.
|
|
20
20
|
//
|
|
@@ -22,15 +22,15 @@ const Shadow = () => {
|
|
|
22
22
|
// - https://github.com/emberjs/ember.js/issues/20643
|
|
23
23
|
// - https://github.com/emberjs/ember.js/issues/20642
|
|
24
24
|
// - https://github.com/emberjs/ember.js/issues/20641
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
shadowRoot.appendChild(div);
|
|
26
|
+
shadow.set(div);
|
|
27
27
|
})
|
|
28
28
|
};
|
|
29
29
|
};
|
|
30
30
|
// index.html has the production-fingerprinted references to these links
|
|
31
31
|
// Ideally, we'd have some pre-processor scan everything for references to
|
|
32
32
|
// assets in public, but idk how to set that up
|
|
33
|
-
const getStyles = () => [...document.querySelectorAll(
|
|
33
|
+
const getStyles = () => [...document.querySelectorAll("link")].map(link => link.href);
|
|
34
34
|
/**
|
|
35
35
|
* style + native @import
|
|
36
36
|
* is the only robust way to load styles in a shadowroot.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadowed.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shadowed.js","sources":["../../src/components/shadowed.gts"],"sourcesContent":["import { modifier } from \"ember-modifier\";\nimport { cell } from \"ember-resources\";\n\nimport type { TOC } from \"@ember/component/template-only\";\n\nconst Shadow = () => {\n const shadow = cell<Element>();\n\n return {\n get root() {\n return shadow.current;\n },\n attach: modifier((element: Element) => {\n const shadowRoot = element.attachShadow({ mode: \"open\" });\n const div = document.createElement(\"div\");\n\n // ember-source 5.6 broke the ability to in-element\n // natively into a shadowroot.\n //\n // See these ember-source bugs:\n // - https://github.com/emberjs/ember.js/issues/20643\n // - https://github.com/emberjs/ember.js/issues/20642\n // - https://github.com/emberjs/ember.js/issues/20641\n shadowRoot.appendChild(div);\n\n shadow.set(div);\n }),\n };\n};\n\n// index.html has the production-fingerprinted references to these links\n// Ideally, we'd have some pre-processor scan everything for references to\n// assets in public, but idk how to set that up\nconst getStyles = () => [...document.querySelectorAll(\"link\")].map((link) => link.href);\n\n/**\n * style + native @import\n * is the only robust way to load styles in a shadowroot.\n *\n * link is only valid in the head element.\n */\nconst Styles = <template>\n <style>\n {{#each (getStyles) as |styleHref|}}\n\n @import \"{{styleHref}}\";\n\n {{/each}}\n </style>\n</template>;\n\n/**\n * Render content in a shadow dom, attached to a div.\n *\n * Uses the [shadow DOM][mdn-shadow-dom] API.\n *\n * [mdn-shadow-dom]: https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM\n *\n * This is useful when you want to render content that escapes your app's styles.\n */\nexport const Shadowed: TOC<{\n /**\n * The shadow dom attaches to a div element.\n * You may specify any attribute, and it'll be applied to this host element.\n */\n Element: HTMLDivElement;\n Args: {\n /**\n * @public\n *\n * By default, shadow-dom does not include any styles.\n * Setting this to true will include all the `<style>` tags\n * that are present in the `<head>` element.\n */\n includeStyles?: boolean;\n };\n Blocks: {\n /**\n * Content to be placed within the ShadowDOM\n */\n default: [];\n };\n}> = <template>\n {{#let (Shadow) as |shadow|}}\n {{! TODO: We need a way in ember to render in to a shadow dom without an effect }}\n <div {{shadow.attach}} ...attributes></div>\n\n {{#if shadow.root}}\n {{#in-element shadow.root}}\n\n {{#if @includeStyles}}\n <Styles />\n {{/if}}\n\n {{yield}}\n\n {{/in-element}}\n {{/if}}\n {{/let}}\n</template>;\n\nexport default Shadowed;\n"],"names":["Shadow","shadow","cell","root","current","attach","modifier","element","shadowRoot","attachShadow","mode","div","document","createElement","appendChild","set","getStyles","querySelectorAll","map","link","href","Styles","setComponentTemplate","precompileTemplate","strictMode","scope","templateOnly","Shadowed"],"mappings":";;;;;;AAKA,MAAMA,MAAS,GAAAA,MAAA;AACb,EAAA,MAAMC,SAASC,IAAK,EAAA;EAEpB,OAAO;IACL,IAAIC,IAAOA,GAAA;MACT,OAAOF,OAAOG,OAAO;KACvB;AACAC,IAAAA,MAAQ,EAAAC,QAAA,CAAUC,OAAS,IAAA;AACzB,MAAA,MAAMC,UAAA,GAAaD,OAAQ,CAAAE,YAAY,CAAC;AAAEC,QAAAA,IAAM,EAAA;AAAO,OAAA,CAAA;AACvD,MAAA,MAAMC,GAAA,GAAMC,QAAS,CAAAC,aAAa,CAAC,KAAA,CAAA;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACAL,MAAAA,UAAA,CAAWM,WAAW,CAACH,GAAA,CAAA;AAEvBV,MAAAA,MAAA,CAAOc,GAAG,CAACJ,GAAA,CAAA;KACb;GACF;AACF,CAAA;AAEA;AACA;AACA;AACA,MAAMK,YAAYA,MAAM,CAAI,GAAAJ,QAAA,CAASK,gBAAgB,CAAC,MAAA,CAAA,CAAQ,CAACC,GAAG,CAAEC,IAAA,IAASA,KAAKC,IAAI,CAAA;AAEtF;;;;;;AAMA,MAAMC,MAAS,GAAAC,oBAAA,CAAAC,kBAAA,CAQf,0HAAA,EAAA;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;AAAAT,IAAAA;AAAA,GAAA;AAAU,CAAA,CAAA,EAAAU,YAAA,EAAA,CAAA;AAEV;;;;;;;;;MASaC,QAsBR,GAAAL,oBAAA,CAAAC,kBAAA,CAiBL,gYAAA,EAAA;EAAAC,UAAA,EAAA,IAAA;AAAAC,EAAAA,KAAA,EAAAA,OAAA;IAAAzB,MAAA;AAAAqB,IAAAA;AAAA,GAAA;AAAU,CAAE,CAAA,EAAAK,YAAA,EAAA;;;;"}
|