@resee-movies/nuxt-ux 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/Card.vue +4 -12
  3. package/dist/runtime/components/Card.vue.d.ts +1 -4
  4. package/dist/runtime/components/CardScroller.vue +189 -0
  5. package/dist/runtime/components/CardScroller.vue.d.ts +36 -0
  6. package/dist/runtime/components/GlobalHeader.vue +107 -0
  7. package/dist/runtime/components/GlobalHeader.vue.d.ts +15 -0
  8. package/dist/runtime/components/GlobalHeaderAnnouncement.vue +49 -0
  9. package/dist/runtime/components/GlobalHeaderAnnouncement.vue.d.ts +14 -0
  10. package/dist/runtime/components/Image.vue +37 -24
  11. package/dist/runtime/components/Image.vue.d.ts +1 -0
  12. package/dist/runtime/components/ImageBase.vue +6 -13
  13. package/dist/runtime/components/ImageBase.vue.d.ts +3 -4
  14. package/dist/runtime/components/LayoutPageRoot.vue +55 -0
  15. package/dist/runtime/components/LayoutPageRoot.vue.d.ts +21 -0
  16. package/dist/runtime/components/Message.vue +31 -11
  17. package/dist/runtime/components/Message.vue.d.ts +4 -3
  18. package/dist/runtime/components/NotificationContainer.vue +2 -2
  19. package/dist/runtime/components/ReseeWordLogo.vue +53 -0
  20. package/dist/runtime/components/ReseeWordLogo.vue.d.ts +12 -0
  21. package/dist/runtime/components/ScrollPinnedContainer.vue +33 -0
  22. package/dist/runtime/components/ScrollPinnedContainer.vue.d.ts +14 -0
  23. package/dist/runtime/components/SuccessSplash.vue +47 -0
  24. package/dist/runtime/components/SuccessSplash.vue.d.ts +6 -0
  25. package/dist/runtime/components/form/Form.vue +55 -21
  26. package/dist/runtime/components/form/Form.vue.d.ts +5 -0
  27. package/dist/runtime/composables/use-global-header-state.d.ts +10 -0
  28. package/dist/runtime/composables/use-global-header-state.js +20 -0
  29. package/dist/runtime/composables/use-load-image.d.ts +1 -1
  30. package/dist/runtime/composables/use-load-image.js +22 -51
  31. package/dist/runtime/composables/use-mutable-intersection-observer.d.ts +44 -0
  32. package/dist/runtime/composables/use-mutable-intersection-observer.js +68 -0
  33. package/dist/runtime/composables/use-resee-ux.d.ts +5 -0
  34. package/dist/runtime/composables/use-resee-ux.js +11 -1
  35. package/dist/runtime/composables/use-two-frame-ref-toggle.d.ts +25 -0
  36. package/dist/runtime/composables/use-two-frame-ref-toggle.js +24 -0
  37. package/dist/runtime/utils/validation.d.ts +2 -2
  38. package/dist/runtime/utils/validation.js +2 -2
  39. package/package.json +1 -1
@@ -1,14 +1,12 @@
1
- import { useNuxtApp } from "#imports";
2
1
  import { loadImage } from "@resee-movies/utilities/dom/load-image";
3
2
  import { getMediaAssetUrl } from "@resee-movies/utilities/resee/get-media-asset-url";
4
3
  import { isString } from "@resee-movies/utilities/strings/is-string";
5
4
  import { fromTmdbImageSize } from "@resee-movies/utilities/tmdb/from-tmdb-image-size";
6
5
  import { getTmdbImageCache } from "@resee-movies/utilities/tmdb/get-tmdb-image-cache";
7
6
  import { getTmdbImageUrl } from "@resee-movies/utilities/tmdb/get-tmdb-image-url";
8
- import { toTmdbImageSize } from "@resee-movies/utilities/tmdb/to-tmdb-image-size";
7
+ import { isPromiseLike } from "@resee-movies/utilities/objects/is-promise-like";
9
8
  import { ref, toRef, toValue, watch } from "vue";
10
9
  export function useLoadImage(source, config = {}) {
11
- const nuxtApp = useNuxtApp();
12
10
  const src = ref();
13
11
  const key = ref();
14
12
  const error = ref();
@@ -31,77 +29,51 @@ export function useLoadImage(source, config = {}) {
31
29
  src.value = toValue(config.errorSrc);
32
30
  key.value = src.value;
33
31
  error.value = new Error('"src" is not defined');
34
- config.onLoading?.(false);
35
32
  config.onError?.(error.value);
36
33
  return;
37
34
  }
38
35
  const loadType = toValue(config.type);
39
36
  const imgWidth = toValue(config.width);
40
- if (import.meta.server || nuxtApp.isHydrating) {
41
- switch (loadType) {
42
- case "tmdb": {
43
- src.value = getTmdbImageUrl(sourceUnwrapped, imgWidth ? toTmdbImageSize(imgWidth) : void 0);
44
- break;
45
- }
46
- case "resee": {
47
- const filename = toValue(config.friendlyName);
48
- const reseeConfig = toValue(config.reseeConfig);
49
- const width = fromTmdbImageSize(imgWidth, { originalIsUndefined: true });
50
- src.value = getMediaAssetUrl(sourceUnwrapped, filename, { width, format: "webp", ...reseeConfig });
51
- break;
52
- }
53
- default: {
54
- src.value = sourceUnwrapped;
55
- }
56
- }
57
- loading.value = true;
58
- key.value = sourceUnwrapped;
59
- config.onLoading?.(true);
60
- config.onLoad?.(src.value);
61
- return;
62
- }
63
37
  if (toValue(config.deferLoad)) {
64
38
  return;
65
39
  }
66
- let pendingSource;
67
- let pendingLoader;
40
+ let targetSrc;
41
+ let tempSrc;
42
+ let promise;
68
43
  if (loadType === "tmdb") {
69
- const result = getTmdbImageCache().getImage(sourceUnwrapped, imgWidth);
70
- pendingSource = isString(result) ? void 0 : result.placeholderUrl;
71
- pendingLoader = result;
44
+ targetSrc = getTmdbImageUrl(sourceUnwrapped, imgWidth);
45
+ const result = getTmdbImageCache().getImage(targetSrc);
46
+ if (isPromiseLike(result)) {
47
+ tempSrc = result.placeholderUrl;
48
+ promise = result;
49
+ }
72
50
  } else if (loadType === "resee") {
73
51
  const filename = toValue(config.friendlyName);
74
52
  const reseeConfig = toValue(config.reseeConfig);
75
53
  const width = fromTmdbImageSize(imgWidth, { originalIsUndefined: true });
76
- pendingLoader = loadImage(
77
- getMediaAssetUrl(sourceUnwrapped, filename, { width, format: "webp", ...reseeConfig })
78
- );
54
+ targetSrc = getMediaAssetUrl(sourceUnwrapped, filename, { width, format: "webp", ...reseeConfig });
55
+ promise = loadImage(targetSrc);
79
56
  } else {
80
- pendingLoader = loadImage(sourceUnwrapped);
57
+ targetSrc = sourceUnwrapped;
58
+ promise = loadImage(targetSrc);
81
59
  }
82
- if (isString(pendingLoader)) {
83
- src.value = pendingLoader;
84
- key.value = sourceUnwrapped;
85
- loading.value = false;
86
- bgLoading.value = false;
87
- error.value = void 0;
88
- config.onLoading?.(false);
60
+ src.value = tempSrc ?? targetSrc;
61
+ key.value = tempSrc ?? sourceUnwrapped;
62
+ loading.value = !!promise && !tempSrc;
63
+ bgLoading.value = !!promise && !!tempSrc;
64
+ error.value = void 0;
65
+ if (!promise) {
89
66
  config.onLoad?.(src.value);
90
67
  return;
91
68
  }
92
- src.value = pendingSource ?? toValue(config.placeholderSrc) ?? src.value;
93
- key.value = pendingSource ? sourceUnwrapped : void 0;
94
- loading.value = !pendingSource;
95
- bgLoading.value = !!pendingSource;
96
- config.onLoading?.(true);
97
- pendingLoader.then(
69
+ config.onLoading?.();
70
+ promise.then(
98
71
  (loadedSrc) => {
99
72
  src.value = loadedSrc;
100
73
  key.value = sourceUnwrapped;
101
74
  loading.value = false;
102
75
  bgLoading.value = false;
103
76
  error.value = void 0;
104
- config.onLoading?.(false);
105
77
  config.onLoad?.(src.value);
106
78
  },
107
79
  (e) => {
@@ -110,7 +82,6 @@ export function useLoadImage(source, config = {}) {
110
82
  loading.value = false;
111
83
  bgLoading.value = false;
112
84
  error.value = e;
113
- config.onLoading?.(false);
114
85
  config.onError?.(e);
115
86
  }
116
87
  );
@@ -0,0 +1,44 @@
1
+ import { type ConfigurableWindow, type MaybeComputedElementRef, type MaybeElement, type Pausable } from '@vueuse/core';
2
+ import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
3
+ /**
4
+ * @module
5
+ *
6
+ * This exists solely to support a dynamic `rootMargin` property. The capability will
7
+ * land natively in Vueuse soon, at which time this can be dropped.
8
+ */
9
+ export interface UseMutableIntersectionObserverOptions extends ConfigurableWindow {
10
+ /**
11
+ * Start the IntersectionObserver immediately on creation
12
+ *
13
+ * @default true
14
+ */
15
+ immediate?: boolean;
16
+ /**
17
+ * The Element or Document whose bounds are used as the bounding box
18
+ * when testing for intersection.
19
+ */
20
+ root?: MaybeComputedElementRef | Document;
21
+ /**
22
+ * A string which specifies a set of offsets to add to the root's
23
+ * bounding_box when calculating intersections.
24
+ */
25
+ rootMargin?: MaybeRefOrGetter<string>;
26
+ /**
27
+ * Either a single number or an array of numbers between 0.0 and 1.
28
+ * @default 0
29
+ */
30
+ threshold?: number | number[];
31
+ }
32
+ export interface UseMutableIntersectionObserverReturn extends Pausable {
33
+ isSupported: ComputedRef<boolean>;
34
+ stop: () => void;
35
+ }
36
+ /**
37
+ * Detects that a target element's visibility.
38
+ *
39
+ * @see https://vueuse.org/useIntersectionObserver
40
+ * @param target
41
+ * @param callback
42
+ * @param options
43
+ */
44
+ export declare function useMutableIntersectionObserver(target: MaybeComputedElementRef | MaybeRefOrGetter<MaybeElement[]> | MaybeComputedElementRef[], callback: IntersectionObserverCallback, options?: UseMutableIntersectionObserverOptions): UseMutableIntersectionObserverReturn;
@@ -0,0 +1,68 @@
1
+ import {
2
+ defaultWindow,
3
+ noop,
4
+ notNullish,
5
+ toArray,
6
+ tryOnScopeDispose,
7
+ unrefElement,
8
+ useSupported
9
+ } from "@vueuse/core";
10
+ import { computed, shallowRef, toValue, watch } from "vue";
11
+ export function useMutableIntersectionObserver(target, callback, options = {}) {
12
+ const {
13
+ root,
14
+ rootMargin,
15
+ threshold = 0,
16
+ window = defaultWindow,
17
+ immediate = true
18
+ } = options;
19
+ const isSupported = useSupported(() => window && "IntersectionObserver" in window);
20
+ const targets = computed(() => {
21
+ const _target = toValue(target);
22
+ return toArray(_target).map(unrefElement).filter(notNullish);
23
+ });
24
+ let cleanup = noop;
25
+ const isActive = shallowRef(immediate);
26
+ const stopWatch = isSupported.value ? watch(
27
+ () => [targets.value, unrefElement(root), toValue(rootMargin), isActive.value],
28
+ ([targets2, root2, rootMargin2]) => {
29
+ cleanup();
30
+ if (!isActive.value)
31
+ return;
32
+ if (!targets2.length)
33
+ return;
34
+ const observer = new IntersectionObserver(
35
+ callback,
36
+ {
37
+ root: unrefElement(root2),
38
+ rootMargin: rootMargin2,
39
+ threshold
40
+ }
41
+ );
42
+ targets2.forEach((el) => el && observer.observe(el));
43
+ cleanup = () => {
44
+ observer.disconnect();
45
+ cleanup = noop;
46
+ };
47
+ },
48
+ { immediate, flush: "post" }
49
+ ) : noop;
50
+ const stop = () => {
51
+ cleanup();
52
+ stopWatch();
53
+ isActive.value = false;
54
+ };
55
+ tryOnScopeDispose(stop);
56
+ return {
57
+ isSupported,
58
+ isActive,
59
+ pause() {
60
+ cleanup();
61
+ isActive.value = false;
62
+ },
63
+ resume() {
64
+ isActive.value = true;
65
+ },
66
+ stop
67
+ };
68
+ }
@@ -1,3 +1,6 @@
1
+ type AppPreferences = {
2
+ dismissNotification?: string;
3
+ };
1
4
  /**
2
5
  * Provides the ability to manage runtime configuration of the ReSee UX
3
6
  * module.
@@ -22,4 +25,6 @@ export declare function useReseeUx(): {
22
25
  numOptionsSelected: string;
23
26
  };
24
27
  };
28
+ preferences: import("#app").CookieRef<AppPreferences>;
25
29
  };
30
+ export {};
@@ -1,3 +1,4 @@
1
+ import { useCookie } from "#imports";
1
2
  import { reactive } from "vue";
2
3
  const Localization = reactive({
3
4
  validation: {
@@ -18,6 +19,15 @@ const Localization = reactive({
18
19
  numOptionsSelected: "{count} Item(s) Selected"
19
20
  }
20
21
  });
22
+ const msInYear = 31536e6;
21
23
  export function useReseeUx() {
22
- return { locale: Localization };
24
+ const preferences = useCookie("resee-app-preferences", {
25
+ default: () => ({}),
26
+ secure: true,
27
+ httpOnly: false,
28
+ sameSite: "strict",
29
+ maxAge: msInYear / 1e3,
30
+ expires: new Date(Date.now() + msInYear)
31
+ });
32
+ return { locale: Localization, preferences };
23
33
  }
@@ -0,0 +1,25 @@
1
+ import { type Ref } from 'vue';
2
+ /**
3
+ * Configuration for the {@link useTwoFrameRefToggle} composable.
4
+ *
5
+ * - `direction` (default: "one-way"): The order in which the refs will be updated,
6
+ * depending on whether they are being toggled true or false.
7
+ * - `"one-way"`: refA -> refB, always.
8
+ * - `"reverse"`: refA -> refB, if true. refB -> refA, if false.
9
+ * - `defaultValue` (default: false): The initial value to set the refs as.
10
+ */
11
+ type UseTwoFrameRefToggleOptions = {
12
+ direction?: 'one-way' | 'reverse';
13
+ defaultValue?: boolean;
14
+ };
15
+ /**
16
+ * Returns two boolean refs and an `update` method that accepts a boolean that the
17
+ * refs will be updated to. The setting of the ref values is staggered, with one
18
+ * happening one re-paint after the first.
19
+ *
20
+ * Why? This sort of one-two-step can be very useful in certain scenarios - like
21
+ * when CSS properties need to be applied in a specific order (e.x. initial state,
22
+ * and then some kind of transition).
23
+ */
24
+ export declare function useTwoFrameRefToggle(options?: UseTwoFrameRefToggleOptions): [refA: Ref<boolean>, refB: Ref<boolean>, updateMethod: (value: boolean) => void];
25
+ export {};
@@ -0,0 +1,24 @@
1
+ import { ref } from "vue";
2
+ export function useTwoFrameRefToggle(options) {
3
+ const refA = ref(options?.defaultValue ?? false);
4
+ const refB = ref(options?.defaultValue ?? false);
5
+ const update = (value) => {
6
+ if (refA.value === value) {
7
+ return;
8
+ }
9
+ const refOrder = !value && options?.direction === "reverse" ? [refB, refA] : [refA, refB];
10
+ refOrder[0].value = value;
11
+ let frameId;
12
+ if (frameId) {
13
+ cancelAnimationFrame(frameId);
14
+ frameId = void 0;
15
+ }
16
+ frameId = requestAnimationFrame(() => {
17
+ frameId = requestAnimationFrame(() => {
18
+ refOrder[1].value = value;
19
+ frameId = void 0;
20
+ });
21
+ });
22
+ };
23
+ return [refA, refB, update];
24
+ }
@@ -12,10 +12,10 @@ export type TextInputRequirements = {
12
12
  minLength?: string | number;
13
13
  maxLength?: string | number;
14
14
  };
15
- export declare function createTextValidator(requirements: TextInputRequirements): z.ZodMiniString<string> | z.ZodMiniUnion<readonly [z.ZodMiniNull, z.ZodMiniString<string>]>;
15
+ export declare function createTextValidator(requirements: TextInputRequirements): z.ZodMiniString<string> | z.ZodMiniUnion<readonly [z.ZodMiniUndefined, z.ZodMiniNull, z.ZodMiniString<string>]>;
16
16
  export type ListInputRequirements = {
17
17
  required?: boolean;
18
18
  minRequired?: string | number;
19
19
  maxRequired?: string | number;
20
20
  };
21
- export declare function createListValidator(requirements: ListInputRequirements): z.ZodMiniArray<z.ZodMiniUnknown> | z.ZodMiniUnion<readonly [z.ZodMiniNull, z.ZodMiniArray<z.ZodMiniUnknown>]>;
21
+ export declare function createListValidator(requirements: ListInputRequirements): z.ZodMiniArray<z.ZodMiniUnknown> | z.ZodMiniUnion<readonly [z.ZodMiniUndefined, z.ZodMiniNull, z.ZodMiniArray<z.ZodMiniUnknown>]>;
@@ -34,7 +34,7 @@ export function createTextValidator(requirements) {
34
34
  }
35
35
  }
36
36
  const stringSchema = z.string(toValidationError(locale.validation.required)).check(z.trim(), ...checkFns);
37
- return requirements.required ? stringSchema : z.union([z.null(), stringSchema]);
37
+ return requirements.required ? stringSchema : z.union([z.undefined(), z.null(), stringSchema]);
38
38
  }
39
39
  export function createListValidator(requirements) {
40
40
  const { locale } = useReseeUx();
@@ -52,5 +52,5 @@ export function createListValidator(requirements) {
52
52
  );
53
53
  }
54
54
  const arraySchema = z.array(z.unknown(), toValidationError(locale.validation.required)).check(...checkFns);
55
- return requirements.required ? arraySchema : z.union([z.null(), arraySchema]);
55
+ return requirements.required ? arraySchema : z.union([z.undefined(), z.null(), arraySchema]);
56
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resee-movies/nuxt-ux",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "The next-gen user experience library for ReSee Movies - currently in development. ",
5
5
  "repository": {
6
6
  "url": "https://github.com/ReSee-Movies/nuxt-ux.git"