@hywax/cms 3.5.0 → 3.6.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.
@@ -1,5 +1,15 @@
1
1
  export default {
2
2
  "slots": {
3
- "base": "rounded-lg"
3
+ "base": "rounded-lg",
4
+ "overlay": "fixed inset-0 bg-default/75 backdrop-blur-sm will-change-opacity",
5
+ "content": "fixed inset-0 flex items-center justify-center cursor-zoom-out focus:outline-none",
6
+ "zoomedImage": "w-full h-auto max-w-[95vw] max-h-[95vh] object-contain rounded-md"
7
+ },
8
+ "variants": {
9
+ "open": {
10
+ "false": {
11
+ "base": "cursor-zoom-in"
12
+ }
13
+ }
4
14
  }
5
15
  }
package/.nuxt/cms/toc.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  export default {
2
2
  "slots": {
3
3
  "root": "",
4
- "list": "min-w-0",
4
+ "header": "flex items-center gap-2 text-sm font-medium mb-2",
5
+ "icon": "size-4",
6
+ "title": "text-sm font-medium",
7
+ "list": "min-w-0 border-default border-l-2 pl-4 ml-0.5",
5
8
  "listWithChildren": "ms-3",
6
9
  "item": "min-w-0",
7
10
  "itemWithChildren": "",
8
- "link": "group relative text-sm flex items-center focus-visible:outline-primary py-1 text-muted hover:text-default transition-colors",
11
+ "link": "group relative text-sm flex items-center focus-visible:outline-primary py-1 font-medium text-muted hover:text-default transition-colors",
9
12
  "linkText": "truncate"
10
13
  },
11
14
  "variants": {
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hywax/cms",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "configKey": "cms",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
package/dist/module.mjs CHANGED
@@ -7,7 +7,7 @@ import { pascalCase, kebabCase, camelCase } from 'scule';
7
7
  import { globSync } from 'tinyglobby';
8
8
 
9
9
  const name = "@hywax/cms";
10
- const version = "3.5.0";
10
+ const version = "3.6.0";
11
11
 
12
12
  function createContext(options, nuxt) {
13
13
  const { resolve } = createResolver(import.meta.url);
@@ -182,7 +182,8 @@ const icons = {
182
182
  code: "i-lucide-code",
183
183
  image: "i-lucide-image",
184
184
  editLine: "i-lucide-pen-line",
185
- save: "i-lucide-cloud-check"
185
+ save: "i-lucide-cloud-check",
186
+ toc: "i-lucide-text-align-start"
186
187
  };
187
188
 
188
189
  async function buildComponentDependencyGraph(componentDir, componentPattern) {
@@ -539,11 +540,14 @@ const tableSearchInput = {
539
540
  const toc = {
540
541
  slots: {
541
542
  root: "",
542
- list: "min-w-0",
543
+ header: "flex items-center gap-2 text-sm font-medium mb-2",
544
+ icon: "size-4",
545
+ title: "text-sm font-medium",
546
+ list: "min-w-0 border-default border-l-2 pl-4 ml-0.5",
543
547
  listWithChildren: "ms-3",
544
548
  item: "min-w-0",
545
549
  itemWithChildren: "",
546
- link: "group relative text-sm flex items-center focus-visible:outline-primary py-1 text-muted hover:text-default transition-colors",
550
+ link: "group relative text-sm flex items-center focus-visible:outline-primary py-1 font-medium text-muted hover:text-default transition-colors",
547
551
  linkText: "truncate"
548
552
  },
549
553
  variants: {
@@ -583,7 +587,17 @@ const theme = {
583
587
 
584
588
  const uploraImage = {
585
589
  slots: {
586
- base: "rounded-lg"
590
+ base: "rounded-lg",
591
+ overlay: "fixed inset-0 bg-default/75 backdrop-blur-sm will-change-opacity",
592
+ content: "fixed inset-0 flex items-center justify-center cursor-zoom-out focus:outline-none",
593
+ zoomedImage: "w-full h-auto max-w-[95vw] max-h-[95vh] object-contain rounded-md"
594
+ },
595
+ variants: {
596
+ open: {
597
+ false: {
598
+ base: "cursor-zoom-in"
599
+ }
600
+ }
587
601
  }
588
602
  };
589
603
 
@@ -4,8 +4,9 @@ import type { ComponentConfig } from '../types';
4
4
  import theme from '#build/cms/toc';
5
5
  type Toc = ComponentConfig<typeof theme, AppConfig, 'toc'>;
6
6
  export interface TocProps {
7
+ title?: string;
7
8
  links: TocLink[];
8
- containerRef?: HTMLElement | null;
9
+ container?: string;
9
10
  class?: any;
10
11
  ui?: Toc['slots'];
11
12
  }
@@ -17,7 +18,7 @@ declare const __VLS_export: import("vue").DefineComponent<TocProps, {}, {}, {},
17
18
  }, string, import("vue").PublicProps, Readonly<TocProps> & Readonly<{
18
19
  onMove?: ((args_0: string) => any) | undefined;
19
20
  }>, {
20
- containerRef: HTMLElement | null;
21
+ title: string;
21
22
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
23
  declare const _default: typeof __VLS_export;
23
24
  export default _default;
@@ -21,6 +21,14 @@
21
21
  </DefineListTemplate>
22
22
 
23
23
  <div :class="ui.root({ class: [props.ui?.root, props.class] })">
24
+ <div :class="ui.header({ class: props.ui?.header })">
25
+ <UIcon :class="ui.icon({ class: props.ui?.icon })" :name="appConfig.ui.icons.toc" />
26
+
27
+ <span :class="ui.title({ class: props.ui?.title })">
28
+ {{ title }}
29
+ </span>
30
+ </div>
31
+
24
32
  <ReuseListTemplate :links="props.links" :level="0" />
25
33
  </div>
26
34
  </template>
@@ -32,8 +40,9 @@ import { createReusableTemplate } from "@vueuse/core";
32
40
  import { computed } from "vue";
33
41
  import { tv } from "../tv";
34
42
  const props = defineProps({
43
+ title: { type: String, required: false, default: "\u0421\u043E\u0434\u0435\u0440\u0436\u0430\u043D\u0438\u0435" },
35
44
  links: { type: Array, required: true },
36
- containerRef: { type: null, required: false, default: null },
45
+ container: { type: String, required: false },
37
46
  class: { type: null, required: false },
38
47
  ui: { type: null, required: false }
39
48
  });
@@ -55,11 +64,11 @@ function scrollToHeading(id) {
55
64
  const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.toc || {} })());
56
65
  const nuxtApp = useNuxtApp();
57
66
  nuxtApp.hooks.hook("page:loading:end", () => {
58
- const headings = Array.from((props.containerRef || document)?.querySelectorAll("h2, h3") ?? []);
67
+ const headings = Array.from(document.querySelector(props.container ?? "body")?.querySelectorAll("h2, h3") ?? []);
59
68
  updateHeadings(headings);
60
69
  });
61
70
  nuxtApp.hooks.hook("page:transition:finish", () => {
62
- const headings = Array.from((props.containerRef || document)?.querySelectorAll("h2, h3") ?? []);
71
+ const headings = Array.from(document.querySelector(props.container ?? "body")?.querySelectorAll("h2, h3") ?? []);
63
72
  updateHeadings(headings);
64
73
  });
65
74
  </script>
@@ -4,8 +4,9 @@ import type { ComponentConfig } from '../types';
4
4
  import theme from '#build/cms/toc';
5
5
  type Toc = ComponentConfig<typeof theme, AppConfig, 'toc'>;
6
6
  export interface TocProps {
7
+ title?: string;
7
8
  links: TocLink[];
8
- containerRef?: HTMLElement | null;
9
+ container?: string;
9
10
  class?: any;
10
11
  ui?: Toc['slots'];
11
12
  }
@@ -17,7 +18,7 @@ declare const __VLS_export: import("vue").DefineComponent<TocProps, {}, {}, {},
17
18
  }, string, import("vue").PublicProps, Readonly<TocProps> & Readonly<{
18
19
  onMove?: ((args_0: string) => any) | undefined;
19
20
  }>, {
20
- containerRef: HTMLElement | null;
21
+ title: string;
21
22
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
23
  declare const _default: typeof __VLS_export;
23
24
  export default _default;
@@ -1,10 +1,11 @@
1
1
  import type { AppConfig } from '@nuxt/schema';
2
2
  import type { ImgHTMLAttributes } from 'vue';
3
3
  import type { ComponentConfig, ImageFormat, ImageSize } from '../types';
4
+ import type { BuildUploraImage } from '../utils';
4
5
  import theme from '#build/cms/uplora-image';
5
6
  export type UploraImage = ComponentConfig<typeof theme, AppConfig, 'uploraImage'>;
6
7
  export interface UploraImageProps {
7
- image: string;
8
+ image: string | BuildUploraImage;
8
9
  alt?: string;
9
10
  formats?: ImageFormat[];
10
11
  sizes?: ImageSize[];
@@ -41,7 +41,7 @@ import { buildUploraImage } from "../utils";
41
41
 
42
42
  <script setup>
43
43
  const props = defineProps({
44
- image: { type: String, required: true },
44
+ image: { type: [String, Object], required: true },
45
45
  alt: { type: String, required: false },
46
46
  formats: { type: Array, required: false },
47
47
  sizes: { type: Array, required: false },
@@ -59,11 +59,11 @@ const appConfig = useAppConfig();
59
59
  const cmsConfig = getCmsConfig();
60
60
  const nuxtApp = useNuxtApp();
61
61
  const initialLoad = nuxtApp.isHydrating;
62
- const image = computed(() => buildUploraImage({
62
+ const image = computed(() => typeof props.image === "string" ? buildUploraImage({
63
63
  id: props.image,
64
64
  formats: props.formats ?? cmsConfig?.components?.uploraImage?.formats,
65
65
  sizes: props.sizes
66
- }));
66
+ }) : props.image);
67
67
  const sources = computed(() => {
68
68
  if (image.value.sources.length > 1) {
69
69
  return image.value.sources.slice(1);
@@ -1,10 +1,11 @@
1
1
  import type { AppConfig } from '@nuxt/schema';
2
2
  import type { ImgHTMLAttributes } from 'vue';
3
3
  import type { ComponentConfig, ImageFormat, ImageSize } from '../types';
4
+ import type { BuildUploraImage } from '../utils';
4
5
  import theme from '#build/cms/uplora-image';
5
6
  export type UploraImage = ComponentConfig<typeof theme, AppConfig, 'uploraImage'>;
6
7
  export interface UploraImageProps {
7
- image: string;
8
+ image: string | BuildUploraImage;
8
9
  alt?: string;
9
10
  formats?: ImageFormat[];
10
11
  sizes?: ImageSize[];
@@ -1,9 +1,10 @@
1
1
  import type { AppConfig } from '@nuxt/schema';
2
- import type { ComponentConfig, ImageSize } from '../../types';
2
+ import type { ComponentConfig, ImageFormat, ImageSize } from '../../types';
3
3
  import theme from '#build/cms/prose/uplora-image';
4
4
  export type ProseUploraImage = ComponentConfig<typeof theme, AppConfig, 'uploraImage', 'cms.prose'>;
5
5
  export interface ProseUploraImageProps {
6
- image?: string;
6
+ image: string;
7
+ formats?: ImageFormat[];
7
8
  alt?: string;
8
9
  color?: string;
9
10
  sizes?: ImageSize[];
@@ -1,25 +1,61 @@
1
1
  <template>
2
- <BaseUploraImage
3
- v-if="image"
4
- :image="image"
5
- :class="ui.base({ class: [props.ui?.base, props.class] })"
6
- :alt="alt"
7
- :color="color"
8
- :sizes="sizes"
9
- />
2
+ <DialogRoot
3
+ v-slot="{ close }"
4
+ v-model:open="open"
5
+ :modal="false"
6
+ >
7
+ <DialogTrigger as-child>
8
+ <Motion :layout-id="layoutId" as-child :transition="{ type: 'spring', bounce: 0.15, duration: 0.5, ease: 'easeInOut' }">
9
+ <BaseUploraImage
10
+ v-if="image"
11
+ :image="image"
12
+ :class="ui.base({ class: [props.ui?.base, props.class] })"
13
+ :alt="alt"
14
+ :color="color"
15
+ />
16
+ </Motion>
17
+ </DialogTrigger>
18
+
19
+ <DialogPortal>
20
+ <AnimatePresence>
21
+ <Motion
22
+ v-if="open"
23
+ :initial="{ opacity: 0 }"
24
+ :animate="{ opacity: 1 }"
25
+ :exit="{ opacity: 0 }"
26
+ :class="ui.overlay({ class: [props.ui?.overlay] })"
27
+ />
28
+
29
+ <div v-if="open" :class="ui.content({ class: [props.ui?.content] })" @click="close">
30
+ <Motion as-child :layout-id="layoutId" :transition="{ type: 'spring', bounce: 0.15, duration: 0.5, ease: 'easeInOut' }">
31
+ <img
32
+ :src="image.original"
33
+ :alt="alt"
34
+ :class="ui.zoomedImage({ class: props.ui?.zoomedImage })"
35
+ >
36
+ </Motion>
37
+ </div>
38
+ </AnimatePresence>
39
+ </DialogPortal>
40
+ </DialogRoot>
10
41
  </template>
11
42
 
12
43
  <script>
13
44
  import theme from "#build/cms/prose/uplora-image";
14
45
  import { getCmsConfig, useAppConfig } from "#imports";
15
- import { computed } from "vue";
46
+ import { useEventListener } from "@vueuse/core";
47
+ import { AnimatePresence, Motion } from "motion-v";
48
+ import { DialogPortal, DialogRoot, DialogTrigger } from "reka-ui";
49
+ import { computed, onMounted, ref, useId } from "vue";
16
50
  import { tv } from "../../tv";
51
+ import { buildUploraImage } from "../../utils";
17
52
  import BaseUploraImage from "../UploraImage.vue";
18
53
  </script>
19
54
 
20
55
  <script setup>
21
56
  const props = defineProps({
22
- image: { type: String, required: false },
57
+ image: { type: String, required: true },
58
+ formats: { type: Array, required: false },
23
59
  alt: { type: String, required: false },
24
60
  color: { type: String, required: false },
25
61
  sizes: { type: Array, required: false },
@@ -28,6 +64,19 @@ const props = defineProps({
28
64
  });
29
65
  const appConfig = useAppConfig();
30
66
  const cmsConfig = getCmsConfig();
31
- const sizes = props.sizes ?? cmsConfig?.components?.prose?.uploraImage?.sizes;
32
- const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.prose?.uploraImage || {} })());
67
+ const layoutId = computed(() => `${props.image}::${useId()}`);
68
+ const open = ref(false);
69
+ const image = computed(() => buildUploraImage({
70
+ id: props.image,
71
+ formats: props.formats ?? cmsConfig?.components?.uploraImage?.formats,
72
+ sizes: props.sizes ?? cmsConfig?.components?.prose?.uploraImage?.sizes
73
+ }));
74
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.prose?.uploraImage || {} })({
75
+ open: open.value
76
+ }));
77
+ onMounted(() => {
78
+ useEventListener(window, "scroll", () => {
79
+ open.value = false;
80
+ });
81
+ });
33
82
  </script>
@@ -1,9 +1,10 @@
1
1
  import type { AppConfig } from '@nuxt/schema';
2
- import type { ComponentConfig, ImageSize } from '../../types';
2
+ import type { ComponentConfig, ImageFormat, ImageSize } from '../../types';
3
3
  import theme from '#build/cms/prose/uplora-image';
4
4
  export type ProseUploraImage = ComponentConfig<typeof theme, AppConfig, 'uploraImage', 'cms.prose'>;
5
5
  export interface ProseUploraImageProps {
6
- image?: string;
6
+ image: string;
7
+ formats?: ImageFormat[];
7
8
  alt?: string;
8
9
  color?: string;
9
10
  sizes?: ImageSize[];
@@ -9,8 +9,9 @@ export interface BuildUploraImageOptions {
9
9
  id: string;
10
10
  formats?: ImageFormat[];
11
11
  sizes?: ImageSize[];
12
+ originalWithFormat?: boolean;
12
13
  }
13
- export interface BuildUploraImageReturn {
14
+ export interface BuildUploraImage {
14
15
  img: string;
15
16
  original: string;
16
17
  sources: ImageSource[];
@@ -19,7 +20,7 @@ export interface BuildUploraImageReturn {
19
20
  /**
20
21
  * Создает URL изображения из Uplora
21
22
  */
22
- export declare function buildUploraImage(options: BuildUploraImageOptions): BuildUploraImageReturn;
23
+ export declare function buildUploraImage(options: BuildUploraImageOptions): BuildUploraImage;
23
24
  export interface GenerateImageSizesOptions {
24
25
  width: number;
25
26
  minWidth?: number;
@@ -14,7 +14,7 @@ export function createUploraImageResolver() {
14
14
  }
15
15
  export function buildUploraImage(options) {
16
16
  const resolver = createUploraImageResolver();
17
- const original = resolver(options.id);
17
+ let original = resolver(options.id);
18
18
  const sizes = options.sizes ?? [{ descriptor: "1x" }];
19
19
  const sources = [];
20
20
  function makeSrcset(format) {
@@ -23,7 +23,7 @@ export function buildUploraImage(options) {
23
23
  }
24
24
  return null;
25
25
  }
26
- if (options.formats) {
26
+ if (options.formats?.length) {
27
27
  for (const format of options.formats) {
28
28
  const srcset = makeSrcset(format);
29
29
  sources.push({
@@ -38,6 +38,9 @@ export function buildUploraImage(options) {
38
38
  type: imagesExtensionsToMimeTypes[format]
39
39
  });
40
40
  }
41
+ if (options.originalWithFormat) {
42
+ original = resolver(options.id, { format: options.formats[0] });
43
+ }
41
44
  } else {
42
45
  const srcset = makeSrcset();
43
46
  sources.push({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hywax/cms",
3
3
  "type": "module",
4
- "version": "3.5.0",
4
+ "version": "3.6.0",
5
5
  "description": "Hywax CMS. ⚠️ This package is intended for internal use only.",
6
6
  "imports": {
7
7
  "#build/cms/*": "./.nuxt/cms/*.ts",