@hywax/cms 3.4.2 → 3.5.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.
@@ -3,11 +3,14 @@ const cmsConfig = {
3
3
  "formats": {
4
4
  "serverDateTime": "YYYY-MM-DD HH:mm:ss"
5
5
  },
6
- "prose": {
7
- "enabled": true,
6
+ "components": {
8
7
  "uploraImage": {
9
- "formats": [],
10
- "sizes": []
8
+ "formats": []
9
+ },
10
+ "prose": {
11
+ "uploraImage": {
12
+ "sizes": []
13
+ }
11
14
  }
12
15
  }
13
16
  }
@@ -9,5 +9,6 @@ export { default as formUploraImage } from './form-uplora-image'
9
9
  export { default as modalConfirm } from './modal-confirm'
10
10
  export { default as tablePanel } from './table-panel'
11
11
  export { default as tableSearchInput } from './table-search-input'
12
+ export { default as toc } from './toc'
12
13
  export { default as uploraImage } from './uplora-image'
13
14
  export * as prose from './prose'
@@ -0,0 +1,18 @@
1
+ export default {
2
+ "slots": {
3
+ "root": "",
4
+ "list": "min-w-0",
5
+ "listWithChildren": "ms-3",
6
+ "item": "min-w-0",
7
+ "itemWithChildren": "",
8
+ "link": "group relative text-sm flex items-center focus-visible:outline-primary py-1 text-muted hover:text-default transition-colors",
9
+ "linkText": "truncate"
10
+ },
11
+ "variants": {
12
+ "active": {
13
+ "true": {
14
+ "link": "text-primary"
15
+ }
16
+ }
17
+ }
18
+ }
package/.nuxt/cms.css CHANGED
@@ -1,4 +1,5 @@
1
1
  @source "./cms/prose";
2
+ @source "./cms/toc.ts";
2
3
  @source "./cms/form-panel.ts";
3
4
  @source "./cms/form-panel-section.ts";
4
5
  @source "./cms/form-slug.ts";
package/dist/module.d.mts CHANGED
@@ -50,14 +50,20 @@ interface CMSCoreOptions {
50
50
  formats?: {
51
51
  serverDateTime?: string;
52
52
  };
53
- /**
54
- * Prose configuration
55
- */
56
- prose?: {
57
- enabled?: boolean;
53
+ components?: {
54
+ /**
55
+ * Uplora image configuration
56
+ */
58
57
  uploraImage?: {
59
58
  formats?: ImageFormat[];
60
- sizes?: ImageSize[];
59
+ };
60
+ /**
61
+ * Prose configuration
62
+ */
63
+ prose?: {
64
+ uploraImage?: {
65
+ sizes?: ImageSize[];
66
+ };
61
67
  };
62
68
  };
63
69
  }
package/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hywax/cms",
3
- "version": "3.4.2",
3
+ "version": "3.5.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.4.2";
10
+ const version = "3.5.0";
11
11
 
12
12
  function createContext(options, nuxt) {
13
13
  const { resolve } = createResolver(import.meta.url);
@@ -31,6 +31,19 @@ const defaultModuleOptions = {
31
31
  envPrefix: "APP_",
32
32
  unovis: false,
33
33
  componentDetection: true,
34
+ components: {
35
+ uploraImage: {
36
+ formats: []
37
+ },
38
+ prose: {
39
+ uploraImage: {
40
+ sizes: []
41
+ }
42
+ }
43
+ },
44
+ formats: {
45
+ serverDateTime: "YYYY-MM-DD HH:mm:ss"
46
+ },
34
47
  httpStatuses: {
35
48
  badRequest: {
36
49
  code: 400,
@@ -80,16 +93,6 @@ const defaultModuleOptions = {
80
93
  code: 503,
81
94
  message: "\u0421\u0435\u0440\u0432\u0438\u0441 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D"
82
95
  }
83
- },
84
- formats: {
85
- serverDateTime: "YYYY-MM-DD HH:mm:ss"
86
- },
87
- prose: {
88
- enabled: true,
89
- uploraImage: {
90
- formats: [],
91
- sizes: []
92
- }
93
96
  }
94
97
  };
95
98
 
@@ -533,6 +536,25 @@ const tableSearchInput = {
533
536
  }
534
537
  };
535
538
 
539
+ const toc = {
540
+ slots: {
541
+ root: "",
542
+ list: "min-w-0",
543
+ listWithChildren: "ms-3",
544
+ item: "min-w-0",
545
+ itemWithChildren: "",
546
+ link: "group relative text-sm flex items-center focus-visible:outline-primary py-1 text-muted hover:text-default transition-colors",
547
+ linkText: "truncate"
548
+ },
549
+ variants: {
550
+ active: {
551
+ true: {
552
+ link: "text-primary"
553
+ }
554
+ }
555
+ }
556
+ };
557
+
536
558
  const uploraImage$1 = {
537
559
  slots: {
538
560
  root: "relative grid grid-cols-[100%] grid-rows-[100%] overflow-hidden",
@@ -555,6 +577,7 @@ const theme = {
555
577
  modalConfirm: modalConfirm,
556
578
  tablePanel: tablePanel,
557
579
  tableSearchInput: tableSearchInput,
580
+ toc: toc,
558
581
  uploraImage: uploraImage$1
559
582
  };
560
583
 
@@ -607,7 +630,7 @@ async function getAppTemplates({ options, resolve, nuxt, detectedComponents: con
607
630
  }
608
631
  }
609
632
  async function generateSources() {
610
- const sources2 = [];
633
+ const sources = [];
611
634
  const layers = getLayerDirectories(nuxt).map((layer) => layer.app);
612
635
  if (options.componentDetection) {
613
636
  const detectedComponents = await detectUsedComponents({
@@ -628,14 +651,12 @@ async function getAppTemplates({ options, resolve, nuxt, detectedComponents: con
628
651
  logger.success(`CMS detected ${detectedComponents.size} components in use (including dependencies)`);
629
652
  }
630
653
  previousDetectedComponents = detectedComponents;
631
- if (options.prose?.enabled) {
632
- sources2.push('@source "./cms/prose";');
633
- }
654
+ sources.push('@source "./cms/prose";');
634
655
  for (const component of detectedComponents) {
635
656
  const kebabComponent = kebabCase(component);
636
657
  const camelComponent = camelCase(component);
637
658
  if (theme[camelComponent]) {
638
- sources2.push(`@source "./cms/${kebabComponent}.ts";`);
659
+ sources.push(`@source "./cms/${kebabComponent}.ts";`);
639
660
  }
640
661
  contextDetectedComponents.add(component);
641
662
  }
@@ -644,27 +665,26 @@ async function getAppTemplates({ options, resolve, nuxt, detectedComponents: con
644
665
  logger.info("CMS detected no components in use, including all components");
645
666
  }
646
667
  previousDetectedComponents = /* @__PURE__ */ new Set();
647
- sources2.push('@source "./cms";');
668
+ sources.push('@source "./cms";');
648
669
  }
649
670
  } else {
650
- sources2.push('@source "./cms";');
671
+ sources.push('@source "./cms";');
651
672
  }
652
- return sources2.join("\n");
653
- }
654
- if (options.prose?.enabled) {
655
- templates.push({
656
- filename: `cms/prose/index.ts`,
657
- write: true,
658
- getContents: () => Object.keys(themeProse).map((component) => `export { default as ${component} } from './${kebabCase(component)}'`).join("\n")
659
- });
660
- writeThemeTemplate(themeProse, "prose");
673
+ return sources.join("\n");
661
674
  }
675
+ templates.push({
676
+ filename: `cms/prose/index.ts`,
677
+ write: true,
678
+ getContents: () => Object.keys(themeProse).map((component) => `export { default as ${component} } from './${kebabCase(component)}'`).join("\n")
679
+ });
680
+ writeThemeTemplate(themeProse, "prose");
662
681
  writeThemeTemplate(theme);
663
- const sources = await generateSources();
682
+ await generateSources();
664
683
  templates.push({
665
684
  filename: "cms.css",
666
685
  write: true,
667
686
  getContents: async () => {
687
+ const sources = await generateSources();
668
688
  return `${sources}
669
689
  ${options.unovis ? `
670
690
  @layer components {
@@ -704,7 +724,7 @@ ${options.unovis ? `
704
724
  write: true,
705
725
  getContents: () => [
706
726
  ...Object.keys(theme).map((component) => `export { default as ${component} } from './${kebabCase(component)}'`),
707
- ...options.prose?.enabled ? [`export * as prose from './prose'`] : []
727
+ `export * as prose from './prose'`
708
728
  ].join("\n")
709
729
  });
710
730
  templates.push({
@@ -744,7 +764,7 @@ export {}
744
764
  const config = {
745
765
  name: options.name,
746
766
  formats: options.formats,
747
- prose: options.prose
767
+ components: options.components
748
768
  };
749
769
  return `const cmsConfig = ${JSON.stringify(config, null, 2)}
750
770
 
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="relative flex items-center gap-2">
3
- <div v-if="image" class="overflow-hidden rounded-lg shrink-0">
3
+ <div v-if="image" class="overflow-hidden rounded-lg shrink-0 size-8">
4
4
  <CUploraImage
5
5
  v-bind="image"
6
6
  :sizes="[{ width: 32, height: 32, descriptor: '1x' }]"
@@ -0,0 +1,23 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { TocLink } from '@nuxtjs/mdc';
3
+ import type { ComponentConfig } from '../types';
4
+ import theme from '#build/cms/toc';
5
+ type Toc = ComponentConfig<typeof theme, AppConfig, 'toc'>;
6
+ export interface TocProps {
7
+ links: TocLink[];
8
+ containerRef?: HTMLElement | null;
9
+ class?: any;
10
+ ui?: Toc['slots'];
11
+ }
12
+ export interface TocEmits {
13
+ move: [string];
14
+ }
15
+ declare const __VLS_export: import("vue").DefineComponent<TocProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
+ move: (args_0: string) => any;
17
+ }, string, import("vue").PublicProps, Readonly<TocProps> & Readonly<{
18
+ onMove?: ((args_0: string) => any) | undefined;
19
+ }>, {
20
+ containerRef: HTMLElement | null;
21
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const _default: typeof __VLS_export;
23
+ export default _default;
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <!-- eslint-disable-next-line vue/no-template-shadow -->
3
+ <DefineListTemplate v-slot="{ links, level }">
4
+ <ul :class="level > 0 ? ui.listWithChildren({ class: props.ui?.listWithChildren }) : ui.list({ class: props.ui?.list })">
5
+ <li v-for="(link, index) in links" :key="index" :class="link.children && link.children.length > 0 ? ui.itemWithChildren({ class: props.ui?.itemWithChildren }) : ui.item({ class: props.ui?.item })">
6
+ <a
7
+ :href="`#${link.id}`"
8
+ :class="ui.link({ class: props.ui?.link, active: activeHeadings.includes(link.id) })"
9
+ @click="scrollToHeading(link.id)"
10
+ >
11
+ {{ link.text }}
12
+ </a>
13
+
14
+ <ReuseListTemplate
15
+ v-if="link.children?.length"
16
+ :links="link.children"
17
+ :level="level + 1"
18
+ />
19
+ </li>
20
+ </ul>
21
+ </DefineListTemplate>
22
+
23
+ <div :class="ui.root({ class: [props.ui?.root, props.class] })">
24
+ <ReuseListTemplate :links="props.links" :level="0" />
25
+ </div>
26
+ </template>
27
+
28
+ <script setup>
29
+ import theme from "#build/cms/toc";
30
+ import { useAppConfig, useNuxtApp, useRouter, useScrollspy } from "#imports";
31
+ import { createReusableTemplate } from "@vueuse/core";
32
+ import { computed } from "vue";
33
+ import { tv } from "../tv";
34
+ const props = defineProps({
35
+ links: { type: Array, required: true },
36
+ containerRef: { type: null, required: false, default: null },
37
+ class: { type: null, required: false },
38
+ ui: { type: null, required: false }
39
+ });
40
+ const emits = defineEmits(["move"]);
41
+ const router = useRouter();
42
+ const appConfig = useAppConfig();
43
+ const { activeHeadings, updateHeadings } = useScrollspy();
44
+ const [DefineListTemplate, ReuseListTemplate] = createReusableTemplate({
45
+ props: {
46
+ links: Array,
47
+ level: Number
48
+ }
49
+ });
50
+ function scrollToHeading(id) {
51
+ const encodedId = encodeURIComponent(id);
52
+ router.push(`#${encodedId}`);
53
+ emits("move", id);
54
+ }
55
+ const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.toc || {} })());
56
+ const nuxtApp = useNuxtApp();
57
+ nuxtApp.hooks.hook("page:loading:end", () => {
58
+ const headings = Array.from((props.containerRef || document)?.querySelectorAll("h2, h3") ?? []);
59
+ updateHeadings(headings);
60
+ });
61
+ nuxtApp.hooks.hook("page:transition:finish", () => {
62
+ const headings = Array.from((props.containerRef || document)?.querySelectorAll("h2, h3") ?? []);
63
+ updateHeadings(headings);
64
+ });
65
+ </script>
@@ -0,0 +1,23 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { TocLink } from '@nuxtjs/mdc';
3
+ import type { ComponentConfig } from '../types';
4
+ import theme from '#build/cms/toc';
5
+ type Toc = ComponentConfig<typeof theme, AppConfig, 'toc'>;
6
+ export interface TocProps {
7
+ links: TocLink[];
8
+ containerRef?: HTMLElement | null;
9
+ class?: any;
10
+ ui?: Toc['slots'];
11
+ }
12
+ export interface TocEmits {
13
+ move: [string];
14
+ }
15
+ declare const __VLS_export: import("vue").DefineComponent<TocProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
+ move: (args_0: string) => any;
17
+ }, string, import("vue").PublicProps, Readonly<TocProps> & Readonly<{
18
+ onMove?: ((args_0: string) => any) | undefined;
19
+ }>, {
20
+ containerRef: HTMLElement | null;
21
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
22
+ declare const _default: typeof __VLS_export;
23
+ export default _default;
@@ -33,7 +33,7 @@
33
33
 
34
34
  <script>
35
35
  import theme from "#build/cms/uplora-image";
36
- import { useAppConfig, useHead, useNuxtApp } from "#imports";
36
+ import { getCmsConfig, useAppConfig, useHead, useNuxtApp } from "#imports";
37
37
  import { computed, onMounted, useTemplateRef } from "vue";
38
38
  import { tv } from "../tv";
39
39
  import { buildUploraImage } from "../utils";
@@ -55,11 +55,13 @@ const props = defineProps({
55
55
  ui: { type: null, required: false }
56
56
  });
57
57
  const emit = defineEmits(["load", "error"]);
58
+ const appConfig = useAppConfig();
59
+ const cmsConfig = getCmsConfig();
58
60
  const nuxtApp = useNuxtApp();
59
61
  const initialLoad = nuxtApp.isHydrating;
60
62
  const image = computed(() => buildUploraImage({
61
63
  id: props.image,
62
- formats: props.formats,
64
+ formats: props.formats ?? cmsConfig?.components?.uploraImage?.formats,
63
65
  sizes: props.sizes
64
66
  }));
65
67
  const sources = computed(() => {
@@ -106,6 +108,5 @@ onMounted(() => {
106
108
  emit("error", event);
107
109
  };
108
110
  });
109
- const appConfig = useAppConfig();
110
111
  const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.uploraImage || {} })());
111
112
  </script>
@@ -1,11 +1,12 @@
1
1
  import type { AppConfig } from '@nuxt/schema';
2
- import type { ComponentConfig } from '../../types';
2
+ import type { ComponentConfig, 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
6
  image?: string;
7
7
  alt?: string;
8
8
  color?: string;
9
+ sizes?: ImageSize[];
9
10
  class?: any;
10
11
  ui?: ProseUploraImage['slots'];
11
12
  }
@@ -5,7 +5,6 @@
5
5
  :class="ui.base({ class: [props.ui?.base, props.class] })"
6
6
  :alt="alt"
7
7
  :color="color"
8
- :formats="formats"
9
8
  :sizes="sizes"
10
9
  />
11
10
  </template>
@@ -23,12 +22,12 @@ const props = defineProps({
23
22
  image: { type: String, required: false },
24
23
  alt: { type: String, required: false },
25
24
  color: { type: String, required: false },
25
+ sizes: { type: Array, required: false },
26
26
  class: { type: null, required: false },
27
27
  ui: { type: null, required: false }
28
28
  });
29
29
  const appConfig = useAppConfig();
30
30
  const cmsConfig = getCmsConfig();
31
- const formats = cmsConfig?.prose?.uploraImage?.formats.length > 0 ? cmsConfig?.prose?.uploraImage?.formats : void 0;
32
- const sizes = cmsConfig?.prose?.uploraImage?.sizes.length > 0 ? cmsConfig?.prose?.uploraImage?.sizes : void 0;
31
+ const sizes = props.sizes ?? cmsConfig?.components?.prose?.uploraImage?.sizes;
33
32
  const ui = computed(() => tv({ extend: tv(theme), ...appConfig.cms?.prose?.uploraImage || {} })());
34
33
  </script>
@@ -1,11 +1,12 @@
1
1
  import type { AppConfig } from '@nuxt/schema';
2
- import type { ComponentConfig } from '../../types';
2
+ import type { ComponentConfig, 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
6
  image?: string;
7
7
  alt?: string;
8
8
  color?: string;
9
+ sizes?: ImageSize[];
9
10
  class?: any;
10
11
  ui?: ProseUploraImage['slots'];
11
12
  }
@@ -18,6 +18,7 @@ export * from '../components/TableFilters.vue';
18
18
  export * from '../components/TablePanel.vue';
19
19
  export * from '../components/TablePreviewSeo.vue';
20
20
  export * from '../components/TableSearchInput.vue';
21
+ export * from '../components/Toc.vue';
21
22
  export * from '../components/UploraImage.vue';
22
23
  export * from '../composables/useAdmin';
23
24
  export * from '../composables/useApi';
@@ -18,6 +18,7 @@ export * from "../components/TableFilters.vue";
18
18
  export * from "../components/TablePanel.vue";
19
19
  export * from "../components/TablePreviewSeo.vue";
20
20
  export * from "../components/TableSearchInput.vue";
21
+ export * from "../components/Toc.vue";
21
22
  export * from "../components/UploraImage.vue";
22
23
  export * from "../composables/useAdmin.js";
23
24
  export * from "../composables/useApi.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hywax/cms",
3
3
  "type": "module",
4
- "version": "3.4.2",
4
+ "version": "3.5.0",
5
5
  "description": "Hywax CMS. ⚠️ This package is intended for internal use only.",
6
6
  "imports": {
7
7
  "#build/cms/*": "./.nuxt/cms/*.ts",