@simple-reporting/base 1.0.28 → 1.0.30

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 (29) hide show
  1. package/dev/package.json +1 -1
  2. package/livingdocs/020.Text/050.quote/quote.html +6 -6
  3. package/livingdocs/020.Text/060.quote-with-portrait/quote-with-portrait.html +6 -6
  4. package/livingdocs/020.Text/060.quote-with-portrait/scss/pdf.scss +5 -0
  5. package/livingdocs/040.Media/010.table/scss/general.scss +3 -3
  6. package/livingdocs/040.Media/010.table/table.html +1 -1
  7. package/livingdocs/110.PDF/050.pdf-chapter-navigation-container/scss/editor.scss +1 -0
  8. package/package.json +1 -1
  9. package/plugins/viteSrlPlugin.js +7 -1
  10. package/scripts/build.js +71 -24
  11. package/scss/grid/root.scss +19 -5
  12. package/srl/.srl/components/Srl/Menu/Item/Content/Image/After.vue +2 -2
  13. package/srl/.srl/components/Srl/Menu/Item/Content/Image/Before.vue +2 -2
  14. package/srl/.srl/components/Srl/Menu/Item/Content/Image.vue +2 -2
  15. package/srl/.srl/components/Srl/Menu/Item/Content/Svg/After.vue +79 -0
  16. package/srl/.srl/components/Srl/Menu/Item/Content/Svg/Before.vue +79 -0
  17. package/srl/.srl/components/Srl/Menu/Item/Content/Svg.vue +71 -0
  18. package/srl/.srl/components/Srl/Menu/Item/Content.vue +18 -0
  19. package/srl/.srl/components/Srl/Menu/Item.vue +90 -15
  20. package/srl/.srl/components/Srl/Menu.vue +81 -33
  21. package/srl/.srl/composables/article.ts +16 -16
  22. package/srl/.srl/composables/articles.ts +7 -1
  23. package/srl/.srl/composables/index.ts +5 -0
  24. package/srl/.srl/composables/instance.ts +13 -0
  25. package/srl/.srl/composables/languageSwitch.ts +0 -3
  26. package/srl/.srl/plugins/initProject.ts +12 -1
  27. package/srl/.srl/utils/index.ts +3 -2
  28. package/srl/.srl/utils/uri.ts +38 -26
  29. /package/srl/.srl/utils/{camelCase.ts → string.ts} +0 -0
package/dev/package.json CHANGED
@@ -24,7 +24,7 @@
24
24
  "postinstall": "srl prepare"
25
25
  },
26
26
  "dependencies": {
27
- "@simple-reporting/base": "^1.0.28",
27
+ "@simple-reporting/base": "^1.0.30",
28
28
  "axios": "^1.12.2",
29
29
  "chalk": "^5.6.2",
30
30
  "exceljs": "^4.4.0",
@@ -3,13 +3,13 @@
3
3
  <p class="srl-quote__quote srl-linkable" doc-editable="quote-text">
4
4
  Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse
5
5
  </p>
6
- <cite class="srl-quote__cite">
7
- <p class="srl-quote__name" doc-editable="quote-name" doc-optional>
6
+ <p class="srl-quote__cite">
7
+ <cite class="srl-quote__name" doc-editable="quote-name" doc-optional>
8
8
  Max Mustermann
9
- </p>
10
- <p class="srl-quote__position" doc-editable="quote-position" doc-optional>
9
+ </cite>
10
+ <span class="srl-quote__position" doc-editable="quote-position" doc-optional>
11
11
  CEO
12
- </p>
13
- </cite>
12
+ </span>
13
+ </p>
14
14
  </div>
15
15
  </blockquote>
@@ -6,13 +6,13 @@
6
6
  <p class="srl-quote__quote srl-linkable" doc-editable="quote-text">
7
7
  Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse
8
8
  </p>
9
- <cite class="srl-quote__cite">
10
- <p class="srl-quote__name" doc-editable="quote-name" doc-optional>
9
+ <p class="srl-quote__cite">
10
+ <cite class="srl-quote__name" doc-editable="quote-name" doc-optional>
11
11
  Max Mustermann
12
- </p>
13
- <p class="srl-quote__position" doc-editable="quote-position" doc-optional>
12
+ </cite>
13
+ <span class="srl-quote__position" doc-editable="quote-position" doc-optional>
14
14
  CEO
15
- </p>
16
- </cite>
15
+ </span>
16
+ </p>
17
17
  </div>
18
18
  </blockquote>
@@ -1,6 +1,11 @@
1
1
  @use 'srl';
2
2
 
3
+ .srl-quote {
4
+ align-items: flex-end;
5
+ }
6
+
3
7
  .srl-quote--with-image {
8
+ align-items: center;
4
9
  flex-direction: row;
5
10
  }
6
11
 
@@ -270,15 +270,15 @@ td {
270
270
  text-align: right;
271
271
  }
272
272
 
273
- &[class*="vertical-top"] {
273
+ &[class*="vatop"] {
274
274
  vertical-align: top;
275
275
  }
276
276
 
277
- &[class*="vertical-middle"] {
277
+ &[class*="vamiddle"] {
278
278
  vertical-align: middle;
279
279
  }
280
280
 
281
- &[class*="vertical-bottom"] {
281
+ &[class*="vabottom"] {
282
282
  vertical-align: bottom;
283
283
  }
284
284
 
@@ -1,5 +1,5 @@
1
1
  <div class="srl-table">
2
- <srl-ld-table data-remove-from-xhtml="transient" data-remove-from-pdf="transient">
2
+ <srl-ld-table data-replace-tag="div" data-remove-from-xhtml="transient" data-remove-from-pdf="transient">
3
3
  <div ref="wrapper" class="srl-table__container" doc-include="nswow-table">
4
4
  <p class="srl-grid srl-paragraph">
5
5
  <span class="srl-grid__inner srl-paragraph__text">
@@ -1,4 +1,5 @@
1
1
  @use "web";
2
+ @use "assets/scss/placeholders";
2
3
 
3
4
  .srl-chapter-navigation__list {
4
5
  @extend %srl-regular-width;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simple-reporting/base",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "Manage srl templates, build and publish",
5
5
  "repository": {
6
6
  "url": "https://github.com/mmssolutionsio/simple-reporting-library"
@@ -1,5 +1,5 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
- import { join, relative } from 'node:path/posix';
2
+ import { join } from 'node:path/posix';
3
3
  import { execSync } from 'node:child_process';
4
4
  import folders from '../scripts/folders.js';
5
5
  import { beaver } from '../scripts/beaver.js';
@@ -77,11 +77,17 @@ function checkForUpdates() {
77
77
  try {
78
78
  if (!data.version) return;
79
79
 
80
+ /*
80
81
  const tag = `v${data.version.split('.')[0]}-lts`;
81
82
 
82
83
  const latest = execSync(`npm view ${packageName}@${tag} version`)
83
84
  .toString()
84
85
  .trim();
86
+ */
87
+
88
+ const latest = execSync(`npm view ${packageName} version`)
89
+ .toString()
90
+ .trim();
85
91
 
86
92
  if (isVersionGreater(latest, data.version)) {
87
93
  printPromptsMessage([
package/scripts/build.js CHANGED
@@ -124,7 +124,7 @@ async function buildApp() {
124
124
  return false;
125
125
  } else {
126
126
  src === join(folders.srlPublic, '/') ||
127
- console.log(`Copy ${src} to ${outputPath}/app`);
127
+ console.log(`Copy ${src} to ${outputPath}/app`);
128
128
  return true;
129
129
  }
130
130
  },
@@ -204,13 +204,12 @@ async function buildDDev() {
204
204
  buildVariables.system['size-unit'] = 'rem';
205
205
 
206
206
  await checkFolders();
207
- const build = await viteBuild({
207
+ return await viteBuild({
208
208
  build: {
209
209
  outDir: './.output/ddev',
210
210
  },
211
211
  publicDir: true,
212
212
  });
213
- return build;
214
213
  }
215
214
 
216
215
  /**
@@ -527,7 +526,7 @@ async function buildPdfCustomer(customer) {
527
526
  console.error(`Customer ${customer} does not exist in: ` + customerDir);
528
527
  }
529
528
 
530
- console.log(`\nBuild PDF for customer ${customer}`);
529
+ console.log(`\nBuild customer ${customer}`);
531
530
 
532
531
  const lddPdfDir = join(folders.srlOutput, 'ldd', 'pdf');
533
532
  const lddJson = await readLivingDocsJson();
@@ -542,9 +541,10 @@ async function buildPdfCustomer(customer) {
542
541
  console.error(e)
543
542
  }
544
543
 
544
+ const lddXbrlDir = join(folders.srlOutput, 'ldd', 'xbrl');
545
+
545
546
  try {
546
547
  const xbrlDir = join(folders.srlOutput, 'xbrl');
547
- const lddXbrlDir = join(folders.srlOutput, 'ldd', 'xbrl');
548
548
  statSync(xbrlDir);
549
549
  await cpSync(xbrlDir, lddXbrlDir, { recursive: true });
550
550
  console.log(`Xbrl folder has been copied to ${relative(folders.root, lddXbrlDir)}`);
@@ -552,6 +552,49 @@ async function buildPdfCustomer(customer) {
552
552
  console.error(e)
553
553
  }
554
554
 
555
+ try {
556
+ const customerXbrlScssPath = join(customerDir, 'custom.scss');
557
+ const customerXbrlTarget = join(lddXbrlDir, customerName);
558
+ const baseXbrlScssPath = join(folders.srlImports, 'xbrl.scss');
559
+
560
+ const entries = [
561
+ baseXbrlScssPath,
562
+ customerXbrlScssPath,
563
+ ];
564
+
565
+ buildVariables.system.environment = 'production';
566
+ buildVariables.system.build = 'xbrl';
567
+ buildVariables.system['size-unit'] = 'rem';
568
+
569
+ const config = {
570
+ css: {
571
+ preprocessorOptions: {
572
+ scss: {
573
+ api: 'modern-compiler',
574
+ },
575
+ },
576
+ },
577
+ base: './',
578
+ build: {
579
+ outDir: customerXbrlTarget,
580
+ lib: {
581
+ fileName: 'xbrl',
582
+ entry: entries,
583
+ formats: ['es'],
584
+ },
585
+ },
586
+ publicDir: false,
587
+ };
588
+
589
+ try {
590
+ statSync(customerXbrlScssPath)
591
+ await viteBuild(config);
592
+ } catch (e) {
593
+ console.error(e);
594
+ }
595
+
596
+ } catch (e) {}
597
+
555
598
  try {
556
599
  const pdfDir = join(folders.srlOutput, 'pdf');
557
600
  statSync(pdfDir);
@@ -574,6 +617,10 @@ async function buildPdfCustomer(customer) {
574
617
  const tsPath = join(customerDir, 'custom.ts');
575
618
  statSync(tsPath);
576
619
 
620
+ buildVariables.system.environment = 'production';
621
+ buildVariables.system.build = 'pdf';
622
+ buildVariables.system['size-unit'] = 'rem';
623
+
577
624
  const config = {
578
625
  css: {
579
626
  preprocessorOptions: {
@@ -761,16 +808,16 @@ async function build(version, options = {}) {
761
808
  const has = (name) => targets.includes(name);
762
809
 
763
810
  if (has('ldd')) {
764
- if (!version) {
765
- const prompt = new Input({
766
- message: 'Livingdocs version',
767
- initial: packageJson.version,
768
- });
769
- version = await prompt.run();
770
- }
811
+ if (!version) {
812
+ const prompt = new Input({
813
+ message: 'Livingdocs version',
814
+ initial: packageJson.version,
815
+ });
816
+ version = await prompt.run();
817
+ }
771
818
 
772
- packageJson.version = version;
773
- await writePackageJson();
819
+ packageJson.version = version;
820
+ await writePackageJson();
774
821
  }
775
822
 
776
823
 
@@ -1015,33 +1062,33 @@ async function mapScss() {
1015
1062
  await writeFileSync(
1016
1063
  join(folders.srlImports, 'app.scss'),
1017
1064
  `@use ` +
1018
- output.app.join(';\n@use ') +
1019
- `;\n@use "@simple-reporting/base/scss/core-styles.scss" as *;\n`,
1065
+ output.app.join(';\n@use ') +
1066
+ `;\n@use "@simple-reporting/base/scss/core-styles.scss" as *;\n`,
1020
1067
  );
1021
1068
  await writeFileSync(
1022
1069
  join(folders.srlImports, 'ldd.scss'),
1023
1070
  `@use ` +
1024
- output.ldd.join(';\n@use ') +
1025
- `;\n@use "@simple-reporting/base/scss/core-styles.scss" as *;\n`,
1071
+ output.ldd.join(';\n@use ') +
1072
+ `;\n@use "@simple-reporting/base/scss/core-styles.scss" as *;\n`,
1026
1073
  );
1027
1074
  await writeFileSync(
1028
1075
  join(folders.srlImports, 'pdf.scss'),
1029
1076
  `@use ` +
1030
- output.pdf.join(';\n@use ') +
1031
- `;\n@use "@simple-reporting/base/scss/core-styles.scss" as *;\n`,
1077
+ output.pdf.join(';\n@use ') +
1078
+ `;\n@use "@simple-reporting/base/scss/core-styles.scss" as *;\n`,
1032
1079
  );
1033
1080
  await writeFileSync(
1034
1081
  join(folders.srlImports, 'word.scss'),
1035
1082
  `@use ` +
1036
- output.word.join(';\n@use ') +
1037
- `;\n@use "@simple-reporting/base/scss/core-styles.scss" as *;\n`,
1083
+ output.word.join(';\n@use ') +
1084
+ `;\n@use "@simple-reporting/base/scss/core-styles.scss" as *;\n`,
1038
1085
  );
1039
1086
 
1040
1087
  await writeFileSync(
1041
1088
  join(folders.srlImports, 'xbrl.scss'),
1042
1089
  `@use ` +
1043
- output.xbrl.join(`;\n@use `) +
1044
- `;\n@use "@simple-reporting/base/scss/xbrl-core-styles.scss" as *;\n`,
1090
+ output.xbrl.join(`;\n@use `) +
1091
+ `;\n@use "@simple-reporting/base/scss/xbrl-core-styles.scss" as *;\n`,
1045
1092
  );
1046
1093
 
1047
1094
  return true;
@@ -2,7 +2,21 @@
2
2
  @use "../system";
3
3
  @use "sass:map";
4
4
 
5
+ $smallest-breakpoint: null;
6
+
7
+ @each $breakpoint, $min-width in variables.$breakpoints {
8
+ @if $min-width == 0 {
9
+ $smallest-breakpoint: $breakpoint;
10
+ }
11
+ }
12
+
5
13
  @each $breakpoint, $min-width in variables.$breakpoints {
14
+ $breakpoint-map: $breakpoint;
15
+
16
+ @if $breakpoint == $smallest-breakpoint {
17
+ $breakpoint-map: false;
18
+ }
19
+
6
20
  $size-unit: false;
7
21
  @if $breakpoint == print {
8
22
  $size-unit: in;
@@ -23,7 +37,7 @@
23
37
  @include system.add-root-style(
24
38
  #{variables.$variable-prefix}container-max-width,
25
39
  system.size-unit($container-max-width, $size-unit),
26
- $breakpoint
40
+ $breakpoint-map
27
41
  );
28
42
 
29
43
  $container-padding: map.get($container, padding);
@@ -34,7 +48,7 @@
34
48
  @include system.add-root-style(
35
49
  #{variables.$variable-prefix}container-padding,
36
50
  system.size-unit($container-padding, $size-unit),
37
- $breakpoint
51
+ $breakpoint-map
38
52
  );
39
53
 
40
54
  $gutter: map.get(variables.$gutter, $breakpoint);
@@ -64,17 +78,17 @@
64
78
  @include system.add-root-style(
65
79
  #{variables.$variable-prefix}gutter-columns,
66
80
  $columns,
67
- $breakpoint
81
+ $breakpoint-map
68
82
  );
69
83
  @include system.add-root-style(
70
84
  #{variables.$variable-prefix}gutter-row-gap,
71
85
  system.size-unit($row-gap, $size-unit),
72
- $breakpoint
86
+ $breakpoint-map
73
87
  );
74
88
 
75
89
  @include system.add-root-style(
76
90
  #{variables.$variable-prefix}gutter-column-gap,
77
91
  system.size-unit($column-gap, $size-unit),
78
- $breakpoint
92
+ $breakpoint-map
79
93
  );
80
94
  }
@@ -26,7 +26,7 @@ const classListText = computed(() => {
26
26
  <span :class="classListText" v-text="props.item.label"/>
27
27
  <img
28
28
  :class="classListIcon"
29
- :src="props.item.imgAfter.src"
30
- :alt="props.item.imgAfter.alt ?? props.item.label"
29
+ :src="props.item.imgAfter?.src"
30
+ :alt="props.item.imgAfter?.alt ?? props.item.label"
31
31
  />
32
32
  </template>
@@ -25,8 +25,8 @@ const classListText = computed(() => {
25
25
  <template>
26
26
  <img
27
27
  :class="classListIcon"
28
- :src="props.item.imgBefore.src"
29
- :alt="props.item.imgBefore.alt ?? props.item.label"
28
+ :src="props.item.imgBefore?.src"
29
+ :alt="props.item.imgBefore?.alt ?? props.item.label"
30
30
  />
31
31
  <span :class="classListText" v-text="props.item.label"/>
32
32
  </template>
@@ -18,7 +18,7 @@ const classListIcon = computed(() => {
18
18
  <template>
19
19
  <img
20
20
  :class="classListIcon"
21
- :src="props.item.img.src"
22
- :alt="props.item.img.alt ?? props.item.label"
21
+ :src="props.item.img?.src"
22
+ :alt="props.item.img?.alt ?? props.item.label"
23
23
  />
24
24
  </template>
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, computed, h, type VNode } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ item: NsWowNavigationItem
6
+ depth: number
7
+ disableClasses: boolean
8
+ }>();
9
+
10
+ const classListIcon = computed(() => {
11
+ return props.disableClasses ? [] : [
12
+ 'srl-menu__link-svg-after',
13
+ `srl-menu__link-svg-after--level-${props.depth}`
14
+ ]
15
+ })
16
+
17
+ const svgVNode = ref<VNode | null>(null)
18
+
19
+ watch(
20
+ props,
21
+ async (to) => {
22
+ if (typeof to.item.svgAfter === 'string') {
23
+ let icon
24
+ if (to.item.svgAfter.startsWith('data:image/svg+xml,')) {
25
+ icon = decodeURIComponent(to.item.svgAfter.replace('data:image/svg+xml,', ''))
26
+ } else if (!to.item.svgAfter.startsWith('<svg')) {
27
+ try {
28
+ const res = await fetch(to.item.svgAfter)
29
+ if (!res.ok) {
30
+ svgVNode.value = null
31
+ return
32
+ }
33
+ icon = await res.text()
34
+ } catch (e) {
35
+ svgVNode.value = null
36
+ return
37
+ }
38
+ } else {
39
+ icon = to.item.svgAfter
40
+ }
41
+
42
+ if (!icon) {
43
+ svgVNode.value = null
44
+ return
45
+ }
46
+ const parser = new DOMParser()
47
+ const doc = parser.parseFromString(icon, 'image/svg+xml')
48
+ const svg = doc.querySelector('svg')
49
+ if (!svg) {
50
+ svgVNode.value = null
51
+ return
52
+ }
53
+
54
+ const attrs = Object.fromEntries([...svg.attributes].map(attr => [attr.name, attr.value]))
55
+
56
+ svgVNode.value = h('svg', {
57
+ innerHTML: svg.innerHTML,
58
+ ...attrs
59
+ })
60
+ } else {
61
+ svgVNode.value = null
62
+ }
63
+ },
64
+ { immediate: true }
65
+ )
66
+
67
+ const classListText = computed(() => {
68
+ return props.disableClasses ? [] : [
69
+ 'srl-menu__link-text',
70
+ `srl-menu__link-text--level-${props.depth}`
71
+ ]
72
+ })
73
+ </script>
74
+
75
+ <template>
76
+ <span :class="classListText" v-text="props.item.label"/>
77
+ <component :is="svgVNode" v-if="svgVNode" :class="classListIcon" />
78
+ <component :is="props.item.svgAfter" v-if="typeof props.item.svgAfter === 'object'" :class="classListIcon" />
79
+ </template>
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, computed, h, type VNode } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ item: NsWowNavigationItem
6
+ depth: number
7
+ disableClasses: boolean
8
+ }>();
9
+
10
+ const classListIcon = computed(() => {
11
+ return props.disableClasses ? [] : [
12
+ 'srl-menu__link-svg-before',
13
+ `srl-menu__link-svg-before--level-${props.depth}`
14
+ ]
15
+ })
16
+
17
+ const svgVNode = ref<VNode | null>(null)
18
+
19
+ watch(
20
+ props,
21
+ async (to) => {
22
+ if (typeof to.item.svgBefore === 'string') {
23
+ let icon
24
+ if (to.item.svgBefore.startsWith('data:image/svg+xml,')) {
25
+ icon = decodeURIComponent(to.item.svgBefore.replace('data:image/svg+xml,', ''))
26
+ } else if (!to.item.svgBefore.startsWith('<svg')) {
27
+ try {
28
+ const res = await fetch(to.item.svgBefore)
29
+ if (!res.ok) {
30
+ svgVNode.value = null
31
+ return
32
+ }
33
+ icon = await res.text()
34
+ } catch (e) {
35
+ svgVNode.value = null
36
+ return
37
+ }
38
+ } else {
39
+ icon = to.item.svgBefore
40
+ }
41
+
42
+ if (!icon) {
43
+ svgVNode.value = null
44
+ return
45
+ }
46
+ const parser = new DOMParser()
47
+ const doc = parser.parseFromString(icon, 'image/svg+xml')
48
+ const svg = doc.querySelector('svg')
49
+ if (!svg) {
50
+ svgVNode.value = null
51
+ return
52
+ }
53
+
54
+ const attrs = Object.fromEntries([...svg.attributes].map(attr => [attr.name, attr.value]))
55
+
56
+ svgVNode.value = h('svg', {
57
+ innerHTML: svg.innerHTML,
58
+ ...attrs
59
+ })
60
+ } else {
61
+ svgVNode.value = null
62
+ }
63
+ },
64
+ { immediate: true }
65
+ )
66
+
67
+ const classListText = computed(() => {
68
+ return props.disableClasses ? [] : [
69
+ 'srl-menu__link-text',
70
+ `srl-menu__link-text--level-${props.depth}`
71
+ ]
72
+ })
73
+ </script>
74
+
75
+ <template>
76
+ <component :is="svgVNode" v-if="svgVNode" :class="classListIcon" />
77
+ <component :is="props.item.svgBefore" v-else-if="typeof props.item.svgBefore === 'object'" :class="classListIcon" />
78
+ <span :class="classListText" v-text="props.item.label"/>
79
+ </template>
@@ -0,0 +1,71 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch, computed, h, type VNode } from 'vue'
3
+
4
+ const props = defineProps<{
5
+ item: NsWowNavigationItem
6
+ depth: number
7
+ disableClasses: boolean
8
+ }>();
9
+
10
+ const classListIcon = computed(() => {
11
+ return props.disableClasses ? [] : [
12
+ 'srl-menu__link-svg',
13
+ `srl-menu__link-svg--level-${props.depth}`
14
+ ]
15
+ })
16
+
17
+ const svgVNode = ref<VNode | null>(null)
18
+
19
+ watch(
20
+ props,
21
+ async (to) => {
22
+ if (typeof to.item.svg === 'string') {
23
+ let icon
24
+ if (to.item.svg.startsWith('data:image/svg+xml,')) {
25
+ icon = decodeURIComponent(to.item.svg.replace('data:image/svg+xml,', ''))
26
+ } else if (!to.item.svg.startsWith('<svg')) {
27
+ try {
28
+ const res = await fetch(icon)
29
+ if (!res.ok) {
30
+ svgVNode.value = null
31
+ return
32
+ }
33
+ icon = await res.text()
34
+ } catch (e) {
35
+ svgVNode.value = null
36
+ return
37
+ }
38
+ } else {
39
+ icon = to.item.svg
40
+ }
41
+
42
+ if (!icon) {
43
+ svgVNode.value = null
44
+ return
45
+ }
46
+ const parser = new DOMParser()
47
+ const doc = parser.parseFromString(icon, 'image/svg+xml')
48
+ const svg = doc.querySelector('svg')
49
+ if (!svg) {
50
+ svgVNode.value = null
51
+ return
52
+ }
53
+
54
+ const attrs = Object.fromEntries([...svg.attributes].map(attr => [attr.name, attr.value]))
55
+
56
+ svgVNode.value = h('svg', {
57
+ innerHTML: svg.innerHTML,
58
+ ...attrs
59
+ })
60
+ } else {
61
+ svgVNode.value = null
62
+ }
63
+ },
64
+ { immediate: true }
65
+ )
66
+ </script>
67
+
68
+ <template>
69
+ <component :is="svgVNode" v-if="svgVNode" :class="classListIcon" />
70
+ <component :is="props.item.svg" v-else-if="typeof props.item.svg === 'object'" :class="classListIcon" />
71
+ </template>
@@ -44,6 +44,24 @@ const props = defineProps<{
44
44
  :depth="props.depth"
45
45
  :disableClasses="props.disableClasses"
46
46
  />
47
+ <SrlMenuItemContentSvg
48
+ v-else-if="props.item.svg"
49
+ :item="props.item"
50
+ :depth="props.depth"
51
+ :disableClasses="props.disableClasses"
52
+ />
53
+ <SrlMenuItemContentSvgBefore
54
+ v-else-if="props.item.svgBefore"
55
+ :item="props.item"
56
+ :depth="props.depth"
57
+ :disableClasses="props.disableClasses"
58
+ />
59
+ <SrlMenuItemContentSvgAfter
60
+ v-else-if="props.item.svgAfter"
61
+ :item="props.item"
62
+ :depth="props.depth"
63
+ :disableClasses="props.disableClasses"
64
+ />
47
65
  <SrlMenuItemContentText
48
66
  v-else
49
67
  :item="props.item"
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { computed, nextTick, ref, useId } from 'vue'
2
+ import { computed, nextTick, ref, useId, VNode } from 'vue'
3
+ import type { Ref } from 'vue'
3
4
  import type { RouterLink } from 'vue-router'
4
5
  import { isExternalPath } from '#utils/uri'
5
6
 
@@ -17,6 +18,12 @@ type BackButtonItem = {
17
18
  src: string
18
19
  alt?: string
19
20
  }
21
+ icon?: string
22
+ iconBefore?: string
23
+ iconAfter?: string
24
+ svg?: VNode | string;
25
+ svgBefore?: VNode | string;
26
+ svgAfter?: VNode | string;
20
27
  attributes?: {
21
28
  [key: string]: string
22
29
  }
@@ -31,12 +38,19 @@ const props = defineProps<{
31
38
  initOpen: number
32
39
  depth: number
33
40
  disableClasses: boolean
34
- backButonEnabled?: boolean
35
- backButtonLabel?: string
36
- backButtonItem: BackButtonItem
41
+ backButtonEnabled: boolean
42
+ backButtonLabel: (backPath: NsWowNavigationItem[]) => string
43
+ backButtonItem?: BackButtonItem
44
+ backPath: NsWowNavigationItem[]
45
+ path: NsWowNavigationItem[]
37
46
  }>()
38
47
 
48
+ const breadcrumb = defineModel<NsWowNavigationItem[]>('breadcrumb', {
49
+ required: true,
50
+ }) as Ref<NsWowNavigationItem[]>
51
+
39
52
  const emit = defineEmits([
53
+ 'toggle',
40
54
  'open',
41
55
  'close',
42
56
  'link',
@@ -51,15 +65,43 @@ if (props.item.children) {
51
65
  id.value = useId()
52
66
  }
53
67
 
68
+ const currentBackPath = computed<NsWowNavigationItem[]>(() => {
69
+ if (!props.item?.attributes?.class?.includes('srl-menu__link--back')) {
70
+ const i: NsWowNavigationItem = {
71
+ label: props.item.label
72
+ }
73
+ props.item.title ? i.title = props.item.title : null
74
+ props.item.img ? i.img = props.item.img : null
75
+ props.item.imgBefore ? i.imgBefore = props.item.imgBefore : null
76
+ props.item.imgAfter ? i.imgAfter = props.item.imgAfter : null
77
+ props.item.icon ? i.icon = props.item.icon : null
78
+ props.item.iconBefore ? i.iconBefore = props.item.iconBefore : null
79
+ props.item.iconAfter ? i.iconAfter = props.item.iconAfter : null
80
+ props.item.svg ? i.svg = props.item.svg : null
81
+ props.item.svgBefore ? i.svgBefore = props.item.svgBefore : null
82
+ props.item.svgAfter ? i.svgAfter = props.item.svgAfter : null
83
+ props.item.attributes ? i.attributes = props.item.attributes : null
84
+ return [i, ...props.backPath]
85
+ }
86
+ return props.backPath
87
+ })
88
+
89
+ const currentPath = computed<NsWowNavigationItem[]>(() => {
90
+ return [...props.path, props.item]
91
+ })
92
+
54
93
  const external = ref(props.item.href && isExternalPath(props.item.href))
55
94
 
56
95
  const menu = ref()
57
- const $el = ref<HTMLButtonElement | HTMLAnchorElement | typeof RouterLink>()
96
+ const $el = ref<HTMLElement | null>(null)
58
97
  const opened = ref(false)
59
98
 
60
- function toggle() {
99
+
100
+
101
+ function toggleAction() {
61
102
  opened.value = !opened.value
62
103
  if (opened.value) {
104
+ breadcrumb.value = currentPath.value
63
105
  emit('open', { index: props.index })
64
106
  nextTick(() => {
65
107
  menu.value.$el.focus()
@@ -67,13 +109,22 @@ function toggle() {
67
109
  } else {
68
110
  menu.value.closeAll()
69
111
  }
112
+ toggle()
70
113
  }
71
114
  function close() {
115
+ breadcrumb.value = props.path
72
116
  emit('close', { index: props.index })
73
117
  }
74
118
 
75
119
  function closeSub() {
76
- $el.value?.focus()
120
+ breadcrumb.value = props.path
121
+ if ($el.value instanceof HTMLElement) {
122
+ $el.value.focus()
123
+ }
124
+ }
125
+
126
+ function toggle() {
127
+ emit('toggle')
77
128
  }
78
129
 
79
130
  function next() {
@@ -99,6 +150,7 @@ function back(event: KeyboardEvent) {
99
150
  event.preventDefault()
100
151
  }
101
152
  emit('back')
153
+ toggle()
102
154
  }
103
155
 
104
156
  function link() {
@@ -113,23 +165,41 @@ function routerChange() {
113
165
  }
114
166
 
115
167
  function closeItem() {
168
+ !opened.value || toggle()
116
169
  opened.value = false
117
170
  menu.value?.closeAll()
118
171
  }
119
172
 
173
+ function findPath(): NsWowNavigationItem[] | null {
174
+ if (opened.value) {
175
+ if (props.item?.children?.length) {
176
+ let res = [...props.path, props.item]
177
+ menu.value.items.forEach((item) => {
178
+ item.opened && (res = item.findPath() || res)
179
+ })
180
+ return res
181
+ } else {
182
+ return currentPath.value
183
+ }
184
+ }
185
+ return null
186
+ }
187
+
120
188
  defineExpose({
189
+ opened,
121
190
  closeItem,
191
+ findPath,
122
192
  $el,
123
193
  menu,
124
194
  })
125
195
 
126
- function internalLinkClick(event: Event) {
127
- !props.item.callback || props.item.callback(event)
196
+ function internalLinkClick() {
197
+ !props.item.callback || props.item.callback()
128
198
  routerChange()
129
199
  }
130
200
 
131
- function externalLinkClick(event: Event) {
132
- !props.item.callback || props.item.callback(event)
201
+ function externalLinkClick() {
202
+ !props.item.callback || props.item.callback()
133
203
  link()
134
204
  }
135
205
 
@@ -258,7 +328,7 @@ const classListItem = computed(() => {
258
328
  :title="props.item.title ?? props.item.label"
259
329
  :aria-label="props.item.icon ? props.item.title ?? props.item.label : undefined"
260
330
  v-bind="dynamicAttributes"
261
- @click.stop="toggle"
331
+ @click.stop="toggleAction"
262
332
  @keydown.left.stop.prevent="prev"
263
333
  @keydown.up.stop.prevent="prev"
264
334
  @keydown.down.stop.prevent="next"
@@ -276,6 +346,9 @@ const classListItem = computed(() => {
276
346
  <SrlMenu
277
347
  v-if="props.item.children"
278
348
  ref="menu"
349
+ :key="id"
350
+ v-model:opened="opened"
351
+ v-model:breadcrumb="breadcrumb"
279
352
  :id="`${props.name}-${id}`"
280
353
  :name="props.name"
281
354
  :menu="props.item.children"
@@ -283,14 +356,16 @@ const classListItem = computed(() => {
283
356
  :initOpen="props.initOpen"
284
357
  :depth="props.depth + 1"
285
358
  :disableClasses="props.disableClasses"
286
- :backButonEnabled="props.backButonEnabled"
287
- :backButtonLabel="props.backButonEnabled ? props.item.label : undefined"
359
+ :backButtonEnabled="props.backButtonEnabled"
360
+ :backButtonLabel="props.backButtonLabel"
288
361
  :backButtonItem="props.backButtonItem"
289
- v-model:opened="opened"
362
+ :path="currentPath"
363
+ :backPath="currentBackPath"
290
364
  @link="link"
291
365
  @routerChange="emit('routerChange')"
292
366
  @tab="tab"
293
367
  @closeSub="closeSub"
368
+ @toggle="toggle"
294
369
  />
295
370
  </li>
296
371
  </template>
@@ -50,7 +50,7 @@
50
50
  * @link="handleNavigation"
51
51
  * />
52
52
  */
53
- import { computed, ref } from 'vue'
53
+ import { computed, ref, VNode } from 'vue'
54
54
 
55
55
  type BackButtonItem = {
56
56
  title?: string
@@ -66,6 +66,12 @@ type BackButtonItem = {
66
66
  src: string
67
67
  alt?: string
68
68
  }
69
+ icon?: string
70
+ iconBefore?: string
71
+ iconAfter?: string
72
+ svg?: VNode | string;
73
+ svgBefore?: VNode | string;
74
+ svgAfter?: VNode | string;
69
75
  attributes?: {
70
76
  [key: string]: string
71
77
  }
@@ -82,9 +88,11 @@ const props = withDefaults(
82
88
  singleOpen?: boolean
83
89
  depth?: number
84
90
  disableClasses?: boolean
85
- backButonEnabled?: boolean
91
+ backButtonEnabled?: boolean
92
+ backButtonLabel?: (backPath: NsWowNavigationItem[]) => string
86
93
  backButtonItem?: BackButtonItem
87
- backButtonLabel?: string
94
+ path?: NsWowNavigationItem[]
95
+ backPath?: NsWowNavigationItem[]
88
96
  }>(),
89
97
  {
90
98
  disableTab: false,
@@ -93,11 +101,15 @@ const props = withDefaults(
93
101
  singleOpen: true,
94
102
  depth: 0,
95
103
  disableClasses: false,
96
- backButtonItem: {}
104
+ backButtonLabel: function(backPath: NsWowNavigationItem[]) {
105
+ return backPath[0]?
106
+ backPath[0]?.label ?? 'Back' : 'Back'
107
+ },
97
108
  },
98
109
  )
99
110
 
100
111
  const emit = defineEmits([
112
+ 'toggle',
101
113
  'close',
102
114
  'closeSub',
103
115
  'link',
@@ -110,6 +122,10 @@ const emit = defineEmits([
110
122
  const items = ref<SrlMenuItem[]>([])
111
123
 
112
124
  const opened = defineModel('opened', { type: Boolean, default: true })
125
+ const breadcrumb = defineModel('breadcrumb', {
126
+ type: Array,
127
+ default: [],
128
+ })
113
129
 
114
130
  function open(event: { index: number }) {
115
131
  if (props.singleOpen) {
@@ -124,6 +140,7 @@ function close() {
124
140
  } else {
125
141
  emit('close')
126
142
  }
143
+ toggle()
127
144
  }
128
145
 
129
146
  function next(event: { index: number }) {
@@ -166,10 +183,27 @@ function routerChange() {
166
183
  emit('routerChange')
167
184
  }
168
185
 
186
+ function toggle() {
187
+ emit('toggle')
188
+ }
189
+
169
190
  function closeAll(keep?: number | string) {
191
+ let findCurrentPath = null
170
192
  items.value.forEach((item: SrlMenuItem, index: number) => {
171
- if (keep !== index) item.closeItem()
193
+ if (keep !== index) {
194
+ item.closeItem()
195
+ }
196
+ const currentPath = item.findPath()
197
+ if (currentPath) {
198
+ findCurrentPath = currentPath
199
+ }
172
200
  })
201
+ if (findCurrentPath) {
202
+ breadcrumb.value = findCurrentPath
203
+ } else {
204
+ breadcrumb.value = props.path && props.path.length ?
205
+ props.path.slice(0, -1) : []
206
+ }
173
207
  }
174
208
 
175
209
  const $el = ref<HTMLUListElement>()
@@ -186,36 +220,44 @@ function focusClickable(index: number) {
186
220
 
187
221
  const menuItems = computed<NsWowNavigationItem[]>(() => {
188
222
  const classes: string[] = []
189
- if (props.backButtonItem?.attributes?.class) {
190
- props.backButtonItem.attributes.class.split(' ').forEach(c => {
191
- classes.push(c)
223
+ const backButtonAttributes = props.backButtonItem?.attributes
224
+ if (backButtonAttributes && backButtonAttributes.class.length > 0) {
225
+ backButtonAttributes.class.split(' ').forEach(c => {
226
+ if (c) classes.push(c)
192
227
  })
193
228
  }
194
229
  classes.push('srl-menu__link--back')
195
- return props.backButonEnabled && props.depth ? [
196
- {
197
- label: props.backButtonLabel,
198
- title: props.backButtonItem.title,
199
- img: props.backButtonItem.img,
200
- imgBefore: props.backButtonItem.imgBefore,
201
- imgAfter: props.backButtonItem.imgAfter,
202
- attributes: props.backButtonItem.attributes ?
203
- Object.assign(props.backButtonItem.attributes,
204
- {
205
- 'class': classes.join(' '),
206
- 'aria-controls': props.id,
207
- 'aria-expanded': opened.value
208
- }
209
- ):
210
- {
211
- 'class': classes.join(' '),
212
- 'aria-controls': props.id,
213
- 'aria-expanded': opened.value
214
- },
215
- callback: close,
216
- },
217
- ...props.menu,
218
- ] : props.menu
230
+
231
+ let backLabel: string = props.backButtonLabel(props.backPath ?? [])
232
+
233
+ if
234
+ (props.backButtonEnabled &&
235
+ props.depth &&
236
+ props.backButtonItem
237
+ ) {
238
+ const backItem: NsWowNavigationItem = {
239
+ label: backLabel,
240
+ }
241
+ props.backButtonItem.title ? backItem.title = props.backButtonItem.title : null
242
+ props.backButtonItem.img ? backItem.img = props.backButtonItem.img : null
243
+ props.backButtonItem.imgBefore ? backItem.imgBefore = props.backButtonItem.imgBefore : null
244
+ props.backButtonItem.imgAfter ? backItem.imgAfter = props.backButtonItem.imgAfter : null
245
+ props.backButtonItem.icon ? backItem.icon = props.backButtonItem.icon : null
246
+ props.backButtonItem.iconBefore ? backItem.iconBefore = props.backButtonItem.iconBefore : null
247
+ props.backButtonItem.iconAfter ? backItem.iconAfter = props.backButtonItem.iconAfter : null
248
+ props.backButtonItem.svg ? backItem.svg = props.backButtonItem.svg : null
249
+ props.backButtonItem.svgBefore ? backItem.svgBefore = props.backButtonItem.svgBefore : null
250
+ props.backButtonItem.svgAfter ? backItem.svgAfter = props.backButtonItem.svgAfter : null
251
+ backItem.attributes = {
252
+ ...(props.backButtonItem.attributes ?? {}),
253
+ class: classes.join(' '),
254
+ 'aria-controls': props.id ?? '',
255
+ 'aria-expanded': String(opened.value)
256
+ }
257
+ backItem.callback = close
258
+ return [backItem, ...props.menu]
259
+ }
260
+ return props.menu
219
261
  })
220
262
 
221
263
  const classList = computed(() => {
@@ -229,7 +271,9 @@ defineExpose({
229
271
  closeAll,
230
272
  $el,
231
273
  items,
274
+ breadcrumb,
232
275
  })
276
+
233
277
  </script>
234
278
 
235
279
  <template>
@@ -249,6 +293,7 @@ defineExpose({
249
293
  <template v-for="(item, index) in menuItems" :key="index">
250
294
  <SrlMenuItem
251
295
  ref="items"
296
+ v-model:breadcrumb="breadcrumb"
252
297
  :item="item"
253
298
  :name="props.name"
254
299
  :index="index"
@@ -257,9 +302,11 @@ defineExpose({
257
302
  :initOpen="props.initOpen"
258
303
  :depth="props.depth"
259
304
  :disableClasses="props.disableClasses"
260
- :backButonEnabled="props.backButonEnabled"
305
+ :backButtonEnabled="props.backButtonEnabled"
261
306
  :backButtonLabel="props.backButtonLabel"
262
307
  :backButtonItem="props.backButtonItem"
308
+ :path="props.path ?? []"
309
+ :backPath="props.backPath ?? []"
263
310
  @open="open"
264
311
  @close="close"
265
312
  @next="next"
@@ -268,6 +315,7 @@ defineExpose({
268
315
  @back="back"
269
316
  @link="link"
270
317
  @routerChange="routerChange"
318
+ @toggle="toggle"
271
319
  />
272
320
  </template>
273
321
  </ul>
@@ -1,24 +1,24 @@
1
1
  import { computed, type ComputedRef } from 'vue';
2
- import { useRoute } from 'vue-router';
2
+ import { useInstance } from '#composables/instance.ts'
3
3
  import useArticles from './articles';
4
4
 
5
5
  const articles = useArticles();
6
+ const instance = useInstance();
6
7
 
7
- export default function useArticle(): ComputedRef<NsWowArticle | undefined> {
8
- const route = useRoute();
9
- return computed<NsWowArticle | undefined>(() => {
10
- const slug = route.params.slug
11
- ? (route.params.slug[0] as string)
12
- : undefined;
13
- const article = !slug
14
- ? articles.value.find((article) => article.index)
15
- : articles.value.find((article) => article.slug === slug);
8
+ const slug = computed(() => {
9
+ return instance.value?.config.globalProperties.$route?.params?.slug
10
+ ? (instance.value?.config.globalProperties.$route?.params?.slug[0] as string)
11
+ : undefined;
12
+ });
16
13
 
17
- if (!article) {
18
- console.error(`Article not found for slug: ${route.path}`);
19
- return undefined;
20
- }
14
+ const article = computed<NsWowArticle | undefined>(() => {
15
+ const slugValue = slug.value;
16
+ const a = articles.value
17
+ return !slugValue
18
+ ? a.find((article) => article.index)
19
+ : a.find((article) => article.slug === slugValue);
20
+ });
21
21
 
22
- return article;
23
- });
22
+ export default function useArticle(): ComputedRef<NsWowArticle | undefined> {
23
+ return article;
24
24
  }
@@ -29,11 +29,17 @@
29
29
  */
30
30
  import { computed, type ComputedRef } from 'vue';
31
31
  import useConfig from './config.ts';
32
+ import { useInstance } from '#composables/instance.ts';
32
33
 
33
34
  const config = useConfig();
35
+ const instance = useInstance();
36
+ const locale = computed(() => instance.value?.config.globalProperties.$route.params.locale);
34
37
 
35
38
  const articles = computed<NsWowArticle[]>(
36
- () => config.value?.articles[config.value.locale],
39
+ () => {
40
+ const lang = locale.value || config.value.locale;
41
+ return config.value?.articles[lang] ?? [];
42
+ },
37
43
  );
38
44
 
39
45
  export default function useArticles(): ComputedRef<NsWowArticle[]> {
@@ -1,3 +1,4 @@
1
+ import _instance from './instance';
1
2
  import _useArticle from './article';
2
3
  import _useArticles from './articles';
3
4
  import _useConfig from './config';
@@ -10,6 +11,8 @@ import _useViewPort from './viewPort';
10
11
  import _useLanguageSwitch from './languageSwitch';
11
12
  import * as cssStyles from './cssStyles.ts'
12
13
 
14
+ export const setInstance = _instance.setInstance;
15
+ export const useInstance = _instance.useInstance;
13
16
  export const useArticle = _useArticle;
14
17
  export const useArticles = _useArticles;
15
18
  export const useConfig = _useConfig;
@@ -24,6 +27,8 @@ export const addCssStyles = cssStyles.addCssStyles;
24
27
  export const useCssStyles = cssStyles.useCssStyles;
25
28
 
26
29
  export default {
30
+ setInstance,
31
+ useInstance,
27
32
  useArticle,
28
33
  useArticles,
29
34
  useConfig,
@@ -0,0 +1,13 @@
1
+ import { ref } from 'vue';
2
+ const app = ref();
3
+
4
+ export function setInstance(instance) {
5
+ app.value = instance;
6
+ }
7
+ export function useInstance() {
8
+ return app;
9
+ }
10
+ export default {
11
+ setInstance,
12
+ useInstance,
13
+ }
@@ -23,9 +23,6 @@ const languageSwitch = computed<NsWowLanguageSwitch>(() => {
23
23
  res.items.push({
24
24
  label: locale,
25
25
  href: `/${locale}`,
26
- callback: () => {
27
- Tr.switchLanguage(locale)
28
- }
29
26
  })
30
27
  }
31
28
  }
@@ -1,8 +1,10 @@
1
1
  import { setConfig } from '#composables/config';
2
+ import { setInstance } from '#composables/instance.ts'
2
3
  import { createApp } from 'vue';
3
4
  import { initI18n } from '@/i18n';
4
5
  import srlVuePlugin from '#plugins/vueSrlPlugin';
5
6
  import { clearPageState } from '#utils'
7
+ import Translate from '@/i18n/translation.ts'
6
8
 
7
9
  import '#imports/app.scss';
8
10
 
@@ -10,15 +12,24 @@ import SrlPageApp from '../App.vue';
10
12
  import router from '@/router';
11
13
 
12
14
  export default async function initProject() {
15
+ const config = await setConfig();
13
16
  router.beforeEach((to, from, next) => {
14
17
  clearPageState();
15
18
  next();
16
19
  });
17
- await setConfig();
20
+ router.afterEach(() => {
21
+ if (
22
+ router.currentRoute.value.params.locale
23
+ && config.value.locale !== router.currentRoute.value.params.locale
24
+ ) {
25
+ Translate.switchLanguage(router.currentRoute.value.params.locale as string);
26
+ }
27
+ })
18
28
  const i18n = initI18n();
19
29
  const app = (window.app = createApp(SrlPageApp));
20
30
  app.use(i18n);
21
31
  app.use(router);
22
32
  app.use(srlVuePlugin);
33
+ setInstance(app);
23
34
  return app;
24
35
  }
@@ -1,5 +1,5 @@
1
- import { isRouterPath, isExternalPath } from './uri';
2
- import { camelCase } from './camelCase';
1
+ import { isFilePath, isRouterPath, isExternalPath } from './uri';
2
+ import { camelCase } from './string';
3
3
  import { prepareHtmlContent } from './html';
4
4
  import {
5
5
  usePageState,
@@ -15,6 +15,7 @@ import {
15
15
  } from './pageState.ts';
16
16
 
17
17
  export {
18
+ isFilePath,
18
19
  isRouterPath,
19
20
  isExternalPath,
20
21
  camelCase,
@@ -1,36 +1,47 @@
1
+ export function isFilePath(path: string): boolean {
2
+ return (
3
+ path.endsWith('.pdf') ||
4
+ path.endsWith('.doc') ||
5
+ path.endsWith('.docx') ||
6
+ path.endsWith('.xls') ||
7
+ path.endsWith('.xlsx') ||
8
+ path.endsWith('.ppt') ||
9
+ path.endsWith('.pptx') ||
10
+ path.endsWith('.zip') ||
11
+ path.endsWith('.html') ||
12
+ path.endsWith('.htm') ||
13
+ path.endsWith('.php') ||
14
+ path.endsWith('.asp') ||
15
+ path.endsWith('.aspx') ||
16
+ path.endsWith('.jsp') ||
17
+ path.endsWith('.xml') ||
18
+ path.endsWith('.json') ||
19
+ path.endsWith('.txt') ||
20
+ path.endsWith('.svg') ||
21
+ path.endsWith('.png') ||
22
+ path.endsWith('.jpg') ||
23
+ path.endsWith('.jpeg') ||
24
+ path.endsWith('.gif') ||
25
+ path.endsWith('.webp')
26
+ );
27
+ }
28
+
1
29
  export function isRouterPath(path: string): boolean {
2
30
  return (
3
- (path.startsWith('/') || path.startsWith('./')) &&
4
- !path.startsWith('//') &&
5
- !path.startsWith('javascript:') &&
6
- !path.endsWith('.html') &&
7
- !path.endsWith('.htm') &&
8
- !path.endsWith('.php') &&
9
- !path.endsWith('.asp') &&
10
- !path.endsWith('.aspx') &&
11
- !path.endsWith('.jsp') &&
12
- !path.endsWith('.xml') &&
13
- !path.endsWith('.json') &&
14
- !path.endsWith('.txt') &&
15
- !path.endsWith('.svg') &&
16
- !path.endsWith('.png') &&
17
- !path.endsWith('.jpg') &&
18
- !path.endsWith('.jpeg') &&
19
- !path.endsWith('.gif') &&
20
- !path.endsWith('.webp') &&
21
- !path.endsWith('.pdf') &&
22
- !path.endsWith('.doc') &&
23
- !path.endsWith('.docx') &&
24
- !path.endsWith('.xls') &&
25
- !path.endsWith('.xlsx') &&
26
- !path.endsWith('.ppt') &&
27
- !path.endsWith('.pptx') &&
28
- !path.endsWith('.zip')
31
+ (
32
+ (
33
+ path.startsWith('/') &&
34
+ !path.startsWith('//')
35
+ ) ||
36
+ path.startsWith('./')
37
+ ) &&
38
+ !isFilePath(path)
29
39
  );
30
40
  }
31
41
 
32
42
  export function isExternalPath(path: string): boolean {
33
43
  return (
44
+ isFilePath(path) ||
34
45
  path.startsWith('http') ||
35
46
  path.startsWith('//') ||
36
47
  path.startsWith('mailto') ||
@@ -49,6 +60,7 @@ export function isExternalPath(path: string): boolean {
49
60
  }
50
61
 
51
62
  export default {
63
+ isFilePath,
52
64
  isRouterPath,
53
65
  isExternalPath,
54
66
  };
File without changes