@signal24/vue-foundation 4.2.2 → 4.3.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@signal24/vue-foundation",
3
3
  "type": "module",
4
- "version": "4.2.2",
4
+ "version": "4.3.0",
5
5
  "description": "Common components, directives, and helpers for Vue 3 apps",
6
6
  "module": "./dist/vue-foundation.es.js",
7
7
  "bin": {
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <Modal class="vf-alert" :class="classes">
3
- <template v-if="!isBare" v-slot:header>
3
+ <template v-if="!isBare && title" v-slot:header>
4
4
  <h1>{{ title }}</h1>
5
5
  </template>
6
6
 
@@ -1,9 +1,23 @@
1
1
  import type { DirectiveBinding, ObjectDirective } from 'vue';
2
2
 
3
- import { type IInfiniteScrollOptions, useInfiniteScroll } from '../hooks/infinite-scroll';
3
+ import { InfiniteScrollHandler } from '../hooks/infinite-scroll';
4
4
 
5
- export const vInfiniteScroll: ObjectDirective<Element, IInfiniteScrollOptions> = {
6
- created(_el: Element, binding: DirectiveBinding<IInfiniteScrollOptions>) {
7
- useInfiniteScroll(binding.value, binding.instance!.$);
5
+ const HandlerSymbol = Symbol('InfiniteScrollHandler');
6
+ type InfiniteScrollElement = Element & { [HandlerSymbol]?: InfiniteScrollHandler };
7
+ type InfiniteScrollBindingValue = () => void;
8
+
9
+ export const vInfiniteScroll: ObjectDirective<Element, InfiniteScrollBindingValue> = {
10
+ mounted(el: InfiniteScrollElement, binding: DirectiveBinding<InfiniteScrollBindingValue>) {
11
+ el[HandlerSymbol] = new InfiniteScrollHandler(el, binding.value);
12
+ },
13
+
14
+ updated(el: InfiniteScrollElement, binding: DirectiveBinding<InfiniteScrollBindingValue>) {
15
+ el[HandlerSymbol]?.uninstall();
16
+ el[HandlerSymbol] = new InfiniteScrollHandler(el, binding.value);
17
+ },
18
+
19
+ unmounted(el: InfiniteScrollElement) {
20
+ el[HandlerSymbol]?.uninstall();
21
+ delete el[HandlerSymbol];
8
22
  }
9
23
  };
@@ -27,22 +27,19 @@ export function installScrollHook(cmp: InfiniteScrollComponent, options: IInfini
27
27
 
28
28
  if (options.elScrolledToBottom) {
29
29
  hookState.el = new InfiniteScrollHandler(cmp.vnode.el as Element, options.elScrolledToBottom);
30
- hookState.el.install();
31
30
  }
32
31
 
33
32
  if (options.ancestorScrolledToBottom) {
34
33
  const scrollableAncestorEl = discoverScrollableAncestorEl(cmp.vnode.el as Element);
35
34
  if (scrollableAncestorEl) {
36
35
  hookState.ancestor = new InfiniteScrollHandler(scrollableAncestorEl, options.ancestorScrolledToBottom);
37
- hookState.ancestor.install();
38
36
  } else {
39
- console.warn('no scollable ancestor found for component:', cmp);
37
+ console.warn('[VueFoundation] No scollable ancestor found for component:', cmp);
40
38
  }
41
39
  }
42
40
 
43
41
  if (options.windowScrolledToBottom) {
44
42
  hookState.window = new InfiniteScrollHandler(window as unknown as Element, options.windowScrolledToBottom);
45
- hookState.window.install();
46
43
  }
47
44
 
48
45
  cmp[HookState] = hookState;
@@ -83,7 +80,9 @@ function discoverScrollableAncestorEl(el: Element): Element | null {
83
80
  export class InfiniteScrollHandler {
84
81
  isTripped = false;
85
82
 
86
- constructor(private el: Element, private handler: (e: Event) => void) {}
83
+ constructor(private el: Element, private handler: (e: Event) => void) {
84
+ this.install();
85
+ }
87
86
 
88
87
  install() {
89
88
  this.el.addEventListener('scroll', this.onScrollWithContext);
@@ -2,15 +2,18 @@
2
2
 
3
3
  import { existsSync } from 'fs';
4
4
 
5
- import { generateOpenapiClient, loadOpenapiOverrides } from './vite-openapi-plugin.js';
5
+ import { generateConfiguredOpenapiClients, generateOpenapiClient } from './vite-openapi-plugin.js';
6
6
 
7
- if (!process.argv[2]) {
8
- throw new Error('Usage: vf-generate-openapi-client <openapi-yaml-path> [<openapi-output-path>]');
9
- }
7
+ if (process.argv[2]) {
8
+ if (process.argv[2] === '--help') {
9
+ throw new Error('Usage: vf-generate-openapi-client [<openapi-yaml-path> [<openapi-output-path>]]');
10
+ }
10
11
 
11
- if (!existsSync(process.argv[2])) {
12
- throw new Error(`OpenAPI YAML file not found: ${process.argv[2]}`);
13
- }
12
+ if (!existsSync(process.argv[2])) {
13
+ throw new Error(`OpenAPI YAML file not found: ${process.argv[2]}`);
14
+ }
14
15
 
15
- loadOpenapiOverrides();
16
- await generateOpenapiClient(process.argv[2], process.argv[3]);
16
+ await generateOpenapiClient(process.argv[2], process.argv[3]);
17
+ } else {
18
+ generateConfiguredOpenapiClients();
19
+ }
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { existsSync, readFileSync, watch } from 'node:fs';
2
+ import { copyFileSync, existsSync, readFileSync, watch } from 'node:fs';
3
3
  import { rm } from 'node:fs/promises';
4
4
 
5
5
  import * as OpenAPI from 'openapi-typescript-codegen';
@@ -7,31 +7,50 @@ import * as OpenAPI from 'openapi-typescript-codegen';
7
7
  const DEFAULT_OUT_PATH = './src/openapi-client-generated';
8
8
 
9
9
  let generatedHash: string | null = null;
10
+ let generatorMap: Record<string, string> = {};
10
11
  let overridesMap: Record<string, string> | null = null;
12
+ let overridesInverseMap: Record<string, string> | null = null;
11
13
 
12
- export function loadOpenapiOverrides() {
13
- if (!existsSync('./openapi-specs.overrides.json')) {
14
+ export function loadOpenapiConfig() {
15
+ loadGeneratorMap();
16
+ loadOverridesMap();
17
+ }
18
+
19
+ function loadGeneratorMap() {
20
+ if (!existsSync('./openapi-specs.json')) {
21
+ console.error('openapi-specs.json not found. Cannot generate OpenAPI client.');
14
22
  return;
15
23
  }
16
24
 
17
25
  try {
18
- const overridesContent = readFileSync('./openapi-specs.overrides.json', 'utf8');
26
+ const specsContent = readFileSync('./openapi-specs.json', 'utf8');
27
+ generatorMap = JSON.parse(specsContent);
28
+ } catch (e) {
29
+ console.error('Failed to load openapi-specs.json:', e);
30
+ }
31
+ }
32
+
33
+ function loadOverridesMap() {
34
+ if (!existsSync('./openapi-specs.dev.json')) {
35
+ return;
36
+ }
37
+
38
+ try {
39
+ const overridesContent = readFileSync('./openapi-specs.dev.json', 'utf8');
19
40
  overridesMap = JSON.parse(overridesContent);
41
+ overridesInverseMap = Object.fromEntries(Object.entries(overridesMap!).map(([k, v]) => [v, k]));
20
42
  } catch (e) {
21
- console.error('Failed to load openapi-specs.overrides.json:', e);
43
+ console.error('Failed to load openapi-specs.dev.json:', e);
22
44
  }
23
45
  }
24
46
 
25
- export function openapiClientGeneratorPlugin(
26
- openapiYamlPath: string,
27
- outPath: string = DEFAULT_OUT_PATH
28
- ): {
47
+ export function openapiClientGeneratorPlugin(): {
29
48
  name: string;
30
49
  apply: 'serve';
31
50
  buildStart(): void;
32
51
  closeBundle(): void;
33
52
  } {
34
- let generator: ReturnType<typeof getGenerator> = null;
53
+ let generators: ReturnType<typeof createWatchfulGenerators> | null = null;
35
54
 
36
55
  return {
37
56
  name: 'openapi-types-generator',
@@ -40,18 +59,26 @@ export function openapiClientGeneratorPlugin(
40
59
  buildStart() {
41
60
  // apply a slight delay so any output doesn't get pushed off screen
42
61
  setTimeout(() => {
43
- loadOpenapiOverrides();
44
- generator = getGenerator(openapiYamlPath, outPath);
62
+ generators = createWatchfulGenerators();
45
63
  }, 250);
46
64
  },
47
65
 
48
66
  closeBundle() {
49
- generator?.close();
67
+ if (generators) {
68
+ for (const generator of generators) {
69
+ generator?.close();
70
+ }
71
+ }
50
72
  }
51
73
  };
52
74
  }
53
75
 
54
- function getGenerator(openapiYamlPath: string, outPath: string) {
76
+ function createWatchfulGenerators() {
77
+ loadOpenapiConfig();
78
+ return Object.entries(generatorMap).map(([openapiYamlPath, outPath]) => createWatchfulGenerator(openapiYamlPath, outPath));
79
+ }
80
+
81
+ function createWatchfulGenerator(openapiYamlPath: string, outPath: string) {
55
82
  const resolvedPath = overridesMap?.[openapiYamlPath] ?? openapiYamlPath;
56
83
 
57
84
  if (!existsSync(resolvedPath)) {
@@ -74,7 +101,15 @@ function getGenerator(openapiYamlPath: string, outPath: string) {
74
101
  };
75
102
  }
76
103
 
77
- async function generateOpenapiClientInternal(openapiYamlPath: string, outPath: string = DEFAULT_OUT_PATH) {
104
+ export async function generateConfiguredOpenapiClients() {
105
+ loadOpenapiConfig();
106
+ for (const [openapiYamlPath, outPath] of Object.entries(generatorMap)) {
107
+ const resolvedPath = overridesMap?.[openapiYamlPath] ?? openapiYamlPath;
108
+ await generateOpenapiClient(resolvedPath, outPath);
109
+ }
110
+ }
111
+
112
+ export async function generateOpenapiClient(openapiYamlPath: string, outPath: string = DEFAULT_OUT_PATH) {
78
113
  const yaml = readFileSync(openapiYamlPath, 'utf8');
79
114
  const hash = createHash('sha256').update(yaml).digest('hex');
80
115
 
@@ -99,13 +134,12 @@ async function generateOpenapiClientInternal(openapiYamlPath: string, outPath: s
99
134
  useUnionTypes: true
100
135
  });
101
136
 
137
+ if (overridesInverseMap?.[openapiYamlPath]) {
138
+ copyFileSync(openapiYamlPath, overridesInverseMap[openapiYamlPath]);
139
+ }
140
+
102
141
  console.log(`[${new Date().toISOString()}] Generated client from ${openapiYamlPath} to ${outPath}/`);
103
142
  } catch (err) {
104
143
  console.error(`[${new Date().toISOString()}] Error generating client from ${openapiYamlPath}:`, err);
105
144
  }
106
145
  }
107
-
108
- export async function generateOpenapiClient(openapiYamlPath: string, outPath: string = DEFAULT_OUT_PATH) {
109
- const resolvedPath = overridesMap?.[openapiYamlPath] ?? openapiYamlPath;
110
- return generateOpenapiClientInternal(resolvedPath, outPath);
111
- }