summaryrefslogtreecommitdiff
path: root/shared/components/src/stores
diff options
context:
space:
mode:
Diffstat (limited to 'shared/components/src/stores')
-rw-r--r--shared/components/src/stores/media-query.ts63
-rw-r--r--shared/components/src/stores/navigation-folders-open.ts21
-rw-r--r--shared/components/src/stores/prefers-reduced-motion.ts27
-rw-r--r--shared/components/src/stores/sidebar-hidden.ts12
4 files changed, 123 insertions, 0 deletions
diff --git a/shared/components/src/stores/media-query.ts b/shared/components/src/stores/media-query.ts
new file mode 100644
index 0000000..83cc055
--- /dev/null
+++ b/shared/components/src/stores/media-query.ts
@@ -0,0 +1,63 @@
+// Based on https://github.com/cibernox/svelte-media
+import { readable } from 'svelte/store';
+import { ArtworkConfig } from '@amp/web-app-components/config/components/artwork';
+import { getMediaConditions } from '@amp/web-app-components/src/utils/getMediaConditions';
+
+const { BREAKPOINTS } = ArtworkConfig.get();
+const mqConditions = getMediaConditions(BREAKPOINTS);
+
+const DEFAULT_SETTING = 'medium';
+
+/**
+ * Filters media query results and outputs the breakpoint name with a matching media query.
+ *
+ * @param {Object} mqls media query configurations (pulled from getMediaConditions())
+ * @returns {String|undefined} breakpoint string that matches current media query
+ */
+function calculateMediaQuery(mqls: Record<string, MediaQueryList>): string {
+ return Object.entries(mqls)
+ .filter(([_, query]) => query.matches)
+ .map(([name, _]) => name)[0];
+}
+
+/**
+ * This function allows to build a store that tracks which of the given media query conditions matches.
+ * @param initialValue The inital value for the store. It only bears importance in server side rendering
+ * as it will update immediately in the browser
+ * @param mediaQueryConditions The dictionary with the media query names and the MQ condition to match against.
+ * @returns Svelte.Store<string> The name of the matching media query
+ */
+export function buildMediaQueryStore(
+ initialValue: string,
+ mediaQueryConditions: Record<string, string> = mqConditions,
+) {
+ return readable(initialValue, (set) => {
+ if (
+ typeof window === 'undefined' ||
+ typeof matchMedia === 'undefined'
+ ) {
+ set(initialValue);
+ return;
+ }
+
+ let mqls = {};
+ let updateMediaQuery = () => set(calculateMediaQuery(mqls));
+
+ for (const key in mediaQueryConditions) {
+ mqls[key] = window.matchMedia(mediaQueryConditions[key]);
+ // `addListener` is deprecated but should still be used for compatibility with more browsers.
+ mqls[key].addListener(updateMediaQuery);
+ }
+
+ updateMediaQuery();
+
+ return function (): void {
+ for (let key in mqls) {
+ // `removeListener` is deprecated but should still be used for compatibility with more browsers.
+ mqls[key].removeListener(updateMediaQuery);
+ }
+ };
+ });
+}
+
+export const mediaQueries = buildMediaQueryStore(DEFAULT_SETTING, mqConditions);
diff --git a/shared/components/src/stores/navigation-folders-open.ts b/shared/components/src/stores/navigation-folders-open.ts
new file mode 100644
index 0000000..b761371
--- /dev/null
+++ b/shared/components/src/stores/navigation-folders-open.ts
@@ -0,0 +1,21 @@
+import { type Writable, writable } from 'svelte/store';
+
+type FolderState = Writable<boolean>;
+const folderStates = new Map<string, FolderState>();
+
+export function subscribeFolderOpenState(
+ id: string,
+ defaultState?: boolean,
+): FolderState {
+ let stateById = folderStates.get(id);
+ if (!stateById) {
+ folderStates.set(id, writable(defaultState ?? false));
+ stateById = folderStates.get(id);
+ }
+
+ return stateById;
+}
+
+export function resetFoldersOpenState() {
+ folderStates.clear();
+}
diff --git a/shared/components/src/stores/prefers-reduced-motion.ts b/shared/components/src/stores/prefers-reduced-motion.ts
new file mode 100644
index 0000000..03d9393
--- /dev/null
+++ b/shared/components/src/stores/prefers-reduced-motion.ts
@@ -0,0 +1,27 @@
+import { readable } from 'svelte/store';
+
+const DEFAULT_SETTING = false;
+
+export const prefersReducedMotion = readable(DEFAULT_SETTING, (set) => {
+ if (typeof window === 'undefined' || typeof matchMedia === 'undefined') {
+ set(DEFAULT_SETTING);
+ return;
+ }
+
+ const motionQuery = matchMedia('(prefers-reduced-motion)');
+
+ /* istanbul ignore next */
+ const motionQueryListener = (): void => {
+ set(motionQuery.matches);
+ };
+
+ // `addListener` is deprecated but should still be used for compatibility with more browsers.
+ motionQuery.addListener(motionQueryListener);
+
+ set(motionQuery.matches);
+
+ return function (): void {
+ // `removeListener` is deprecated but should still be used for compatibility with more browsers.
+ motionQuery.removeListener(motionQueryListener);
+ };
+});
diff --git a/shared/components/src/stores/sidebar-hidden.ts b/shared/components/src/stores/sidebar-hidden.ts
new file mode 100644
index 0000000..2de14d1
--- /dev/null
+++ b/shared/components/src/stores/sidebar-hidden.ts
@@ -0,0 +1,12 @@
+import { derived } from 'svelte/store';
+import { buildMediaQueryStore } from '@amp/web-app-components/src/stores/media-query';
+
+export const sidebarHiddenQuery = buildMediaQueryStore('visible', {
+ hidden: '(max-width: 483px)',
+ visible: '(min-width: 484px)',
+});
+
+export const sidebarIsHidden = derived(
+ sidebarHiddenQuery,
+ ($sidebarHiddenQuery) => $sidebarHiddenQuery === 'hidden',
+);