@dative-gpi/foundation-shared-components 1.0.180-utils-composable → 1.0.181-fix-sandbox-connectivity

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.
@@ -0,0 +1,60 @@
1
+ <template>
2
+ <FSRow
3
+ align="bottom-left"
4
+ :wrap="false"
5
+ gap="16px"
6
+ >
7
+ <FSRow
8
+ align="bottom-center"
9
+ >
10
+ <FSNumberField
11
+ :label="$tr('entity.widget.width', 'Width')"
12
+ :modelValue="widgetWidth"
13
+ @update:modelValue="$emit('update:widgetWidth', $event)"
14
+ />
15
+ <FSNumberField
16
+ :label="$tr('entity.widget.height', 'Height')"
17
+ :modelValue="widgetHeight"
18
+ @update:modelValue="$emit('update:widgetHeight', $event)"
19
+ />
20
+ <FSSwitch
21
+ class="dialog-configure-widget-hide-borders"
22
+ :label="$tr('entity.widget.hide-borders', 'Hide borders')"
23
+ :modelValue="hideBorders"
24
+ @update:modelValue="$emit('update:hideBorders', $event)"
25
+ />
26
+ </FSRow>
27
+ </FSRow>
28
+ </template>
29
+
30
+ <script lang="ts">
31
+ import { defineComponent } from "vue";
32
+
33
+ import FSRow from "./FSRow.vue";
34
+ import FSNumberField from "./fields/FSNumberField.vue";
35
+ import FSSwitch from "./FSSwitch.vue";
36
+
37
+ export default defineComponent({
38
+ name: "WidgetStandardOptions",
39
+ components: {
40
+ FSRow,
41
+ FSNumberField,
42
+ FSSwitch
43
+ },
44
+ props: {
45
+ widgetWidth: {
46
+ type: Number,
47
+ required: true
48
+ },
49
+ widgetHeight: {
50
+ type: Number,
51
+ required: true
52
+ },
53
+ hideBorders: {
54
+ type: Boolean,
55
+ required: true
56
+ }
57
+ },
58
+ emits: ["update:widgetWidth", "update:widgetHeight", "update:hideBorders"]
59
+ });
60
+ </script>
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <FSCard
3
+ height="100px"
4
+ width="100%"
5
+ >
6
+ <FSCol
7
+ align="center-center"
8
+ >
9
+ <FSIcon
10
+ v-if="icon"
11
+ size="32px"
12
+ >
13
+ {{ $props.icon }}
14
+ </FSIcon>
15
+ <FSText
16
+ font="text-overline"
17
+ >
18
+ {{ $props.label }}
19
+ </FSText>
20
+ </FSCol>
21
+ </FSCard>
22
+ </template>
23
+
24
+ <script lang="ts">
25
+ import { defineComponent } from "vue";
26
+
27
+ import FSCard from "./FSCard.vue";
28
+ import FSCol from "./FSCol.vue";
29
+ import FSIcon from "./FSIcon.vue";
30
+ import FSText from "./FSText.vue";
31
+
32
+ export default defineComponent({
33
+ name: "FSWidgetTemplateCardUI",
34
+ components: {
35
+ FSCard,
36
+ FSCol,
37
+ FSIcon,
38
+ FSText
39
+ },
40
+ props: {
41
+ icon: {
42
+ type: String,
43
+ required: false,
44
+ default: "mdi-chart-bar"
45
+ },
46
+ label: {
47
+ type: String,
48
+ required: true
49
+ }
50
+ }
51
+ });
52
+ </script>
@@ -5,7 +5,7 @@ export * from "./useBreakpoints";
5
5
  export * from "./useColors";
6
6
  export * from "./useCountUp";
7
7
  export * from "./useDebounce";
8
- export * from "./useElementVisibility"
8
+ export * from "./useElementVisibility";
9
9
  export * from "./useMapLayers";
10
10
  export * from "./useRules";
11
11
  export * from "./useSlots";
@@ -1,7 +1,32 @@
1
+ import { ref, onMounted, onBeforeUnmount } from 'vue';
2
+
3
+ const PREFERS_REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
4
+
1
5
  export function useAccessibilityPreferences() {
2
- const prefersReducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches || false;
6
+ const prefersReducedMotion = ref(false);
7
+ let mediaQuery: MediaQueryList | null = null;
8
+
9
+ const handleChange = (e: MediaQueryListEvent) => {
10
+ prefersReducedMotion.value = e.matches;
11
+ };
3
12
 
13
+ onMounted(() => {
14
+
15
+ mediaQuery = window.matchMedia?.(PREFERS_REDUCED_MOTION_QUERY) || null;
16
+ prefersReducedMotion.value = mediaQuery?.matches || false;
17
+
18
+ if (mediaQuery?.addEventListener) {
19
+ mediaQuery.addEventListener('change', handleChange);
20
+ }
21
+ });
22
+
23
+ onBeforeUnmount(() => {
24
+ if (mediaQuery?.removeEventListener) {
25
+ mediaQuery.removeEventListener('change', handleChange);
26
+ }
27
+ });
28
+
4
29
  return {
5
30
  prefersReducedMotion
6
31
  };
7
- }
32
+ }
@@ -0,0 +1,41 @@
1
+ import { onBeforeUnmount, computed } from 'vue';
2
+
3
+ export const useAnimationFrame = (callback: () => void) => {
4
+ let animationId: number | null = null;
5
+ let isActive = false;
6
+
7
+ const start = () => {
8
+ if (isActive) {
9
+ return;
10
+ }
11
+ isActive = true;
12
+
13
+ const animate = () => {
14
+ if (!isActive) {
15
+ return;
16
+ }
17
+
18
+ callback();
19
+ animationId = requestAnimationFrame(animate);
20
+ };
21
+ animationId = requestAnimationFrame(animate);
22
+ };
23
+
24
+ const stop = () => {
25
+ isActive = false;
26
+ if (animationId !== null) {
27
+ cancelAnimationFrame(animationId);
28
+ animationId = null;
29
+ }
30
+ };
31
+
32
+ onBeforeUnmount(() => {
33
+ stop();
34
+ });
35
+
36
+ return {
37
+ start,
38
+ stop,
39
+ isActive: computed(() => isActive)
40
+ };
41
+ };
@@ -1,101 +1,74 @@
1
- import { ref, computed, onBeforeUnmount, watch } from 'vue';
1
+ import { ref, computed, watch, type Ref, type ComponentPublicInstance } from 'vue';
2
2
  import { useAccessibilityPreferences } from './useAccessibilityPreferences';
3
+ import { useAnimationFrame } from './useAnimationFrame';
4
+ import { useElementVisibility } from './useElementVisibility';
3
5
 
4
- export function useCountUp(options: {
5
- value: number | string,
6
- duration?: number,
7
- countUp?: boolean,
8
- pad?: number,
9
- startOnVisible?: boolean,
10
- easing?: (t: number) => number
11
- }) {
12
- const {
13
- value,
14
- duration = 800,
15
- countUp = true,
16
- pad = 2,
17
- startOnVisible = true,
18
- easing = easeOutCubic
19
- } = options;
20
-
6
+ export function useCountUp(
7
+ targetValue: Ref<number>,
8
+ duration = ref<number>(800),
9
+ pad = ref<number>(2),
10
+ startOnVisible = ref<boolean>(false),
11
+ elementRef: Ref<ComponentPublicInstance | null> = ref(null),
12
+ easing = (t: number) => 1 - Math.pow(1 - t, 3)
13
+ ) {
21
14
  const { prefersReducedMotion } = useAccessibilityPreferences();
22
- const current = ref(0);
23
- const rafId = ref(0);
24
- const hasAnimated = ref(false);
25
-
26
- // Easing function
27
- function easeOutCubic(t: number) {
28
- return 1 - Math.pow(1 - t, 3);
29
- }
30
15
 
31
- // Target value computation
32
- const target = computed(() => {
33
- const n = Number(value);
34
- return Number.isFinite(n) ? Math.trunc(n) : 0;
35
- });
16
+ const { onVisible } = useElementVisibility(
17
+ elementRef
18
+ );
36
19
 
37
- // Formatted display value
20
+ const current = ref<number>(0);
21
+ const animating = ref<boolean>(false);
22
+ const startTime = ref<number>(0);
23
+ const from = ref<number>(0);
24
+ const to = ref<number>(0);
25
+
38
26
  const displayText = computed(() => {
39
- const s = String(countUp ? current.value : target.value);
40
- return pad > 0 ? s.padStart(pad, "0") : s;
27
+ const text = String(current.value);
28
+ return pad.value > 0 ? text.padStart(pad.value, "0") : text;
41
29
  });
42
30
 
43
- // Animation function
44
- function animate(from: number, to: number, animDuration: number) {
45
- cancelAnimationFrame(rafId.value);
46
- const start = performance.now();
47
-
48
- const step = (now: number) => {
49
- const t = Math.min(1, (now - start) / animDuration);
50
- const v = from + (to - from) * easing(t);
51
- current.value = Math.round(v);
52
- if (t < 1) {
53
- rafId.value = requestAnimationFrame(step);
54
- }
55
- };
31
+ const { start: afStart, stop: afStop } = useAnimationFrame(() => {
32
+ const elapsed = performance.now() - startTime.value;
33
+ const progress = Math.min(1, elapsed / duration.value);
56
34
 
57
- rafId.value = requestAnimationFrame(step);
58
- }
59
-
60
- // Start animation
61
- function start() {
62
- if (!countUp) {
63
- current.value = target.value;
35
+ if (progress >= 1) {
36
+ current.value = to.value;
37
+ animating.value = false;
38
+ afStop();
64
39
  return;
65
40
  }
66
41
 
67
- if (prefersReducedMotion) {
68
- current.value = target.value;
42
+ const easedProgress = easing(progress);
43
+ current.value = Math.round(from.value + (to.value - from.value) * easedProgress);
44
+ });
45
+
46
+ const start = () => {
47
+ if (prefersReducedMotion.value) {
48
+ current.value = targetValue.value;
49
+ animating.value = false;
69
50
  return;
70
51
  }
71
52
 
72
- animate(current.value, target.value, duration);
73
- hasAnimated.value = true;
53
+ from.value = current.value;
54
+ to.value = targetValue.value;
55
+ startTime.value = performance.now();
56
+ animating.value = true;
57
+ afStart();
74
58
  }
75
59
 
76
- // Clean up
77
- onBeforeUnmount(() => {
78
- cancelAnimationFrame(rafId.value);
79
- });
80
-
81
- // The restart method can be useful when the value changes.
82
- function restart() {
83
- if (hasAnimated.value || !startOnVisible) {
60
+ watch(targetValue, () => {
61
+ start();
62
+ }, { immediate: true });
63
+
64
+ onVisible.value = () => {
65
+ if (startOnVisible.value && !animating.value) {
66
+ current.value = from.value;
84
67
  start();
85
68
  }
86
- }
87
-
88
- // Monitor changes in value
89
- watch(() => value, () => {
90
- restart();
91
- });
69
+ };
92
70
 
93
71
  return {
94
- current,
95
- target,
96
72
  displayText,
97
- start,
98
- restart,
99
- hasAnimated
100
73
  };
101
74
  }
@@ -1,39 +1,41 @@
1
- import { ref, onMounted, onBeforeUnmount } from 'vue';
2
-
3
- export function useElementVisibility(element: HTMLElement | null, options: {
4
- threshold?: number,
5
- onVisible?: () => void
6
- }) {
7
- const {
8
- threshold = 0.3,
9
- onVisible
10
- } = options;
11
-
12
- const isVisible = ref(false);
13
- const observer = ref<IntersectionObserver | null>(null);
1
+ import { type Ref, ref, onBeforeUnmount, watch, type ComponentPublicInstance } from 'vue';
2
+
3
+
4
+ export function useElementVisibility(
5
+ target: Ref<ComponentPublicInstance | null>,
6
+ threshold = 0.3
7
+ ) {
8
+ const onVisible: Ref<(() => void) | null> = ref(null);
9
+ let observer: IntersectionObserver | null = null;
14
10
 
15
- onMounted(() => {
16
- if (!element) {
17
- return;
18
- }
19
-
20
- observer.value = new IntersectionObserver((entries) => {
21
- entries.forEach((entry) => {
22
- isVisible.value = entry.isIntersecting;
23
- if (entry.isIntersecting && onVisible) {
24
- onVisible();
25
- }
26
- });
11
+ const startObserver = (el: Element) => {
12
+ stopObserver();
13
+
14
+ observer = new IntersectionObserver(([entry]) => {
15
+ const visible = Boolean(entry?.isIntersecting);
16
+ if (visible && onVisible.value) {
17
+ onVisible.value();
18
+ }
27
19
  }, { threshold });
28
-
29
- observer.value.observe(element);
30
- });
31
-
32
- onBeforeUnmount(() => {
33
- observer.value?.disconnect();
34
- });
35
-
36
- return {
37
- isVisible
20
+
21
+ observer.observe(el);
22
+ };
23
+
24
+ const stopObserver = () => {
25
+ observer?.disconnect();
26
+ observer = null;
38
27
  };
39
- }
28
+
29
+ watch(target , (newVal) => {
30
+ console.log('Target changed:', newVal);
31
+ if(newVal && newVal.$el){
32
+ startObserver(newVal.$el);
33
+ }
34
+ }, { immediate: true });
35
+
36
+ onBeforeUnmount(stopObserver);
37
+
38
+ return {
39
+ onVisible
40
+ };
41
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dative-gpi/foundation-shared-components",
3
3
  "sideEffects": false,
4
- "version": "1.0.180-utils-composable",
4
+ "version": "1.0.181-fix-sandbox-connectivity",
5
5
  "description": "",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -10,8 +10,8 @@
10
10
  "author": "",
11
11
  "license": "ISC",
12
12
  "dependencies": {
13
- "@dative-gpi/foundation-shared-domain": "1.0.180-utils-composable",
14
- "@dative-gpi/foundation-shared-services": "1.0.180-utils-composable"
13
+ "@dative-gpi/foundation-shared-domain": "1.0.181-fix-sandbox-connectivity",
14
+ "@dative-gpi/foundation-shared-services": "1.0.181-fix-sandbox-connectivity"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "@dative-gpi/bones-ui": "^1.0.0",
@@ -35,5 +35,5 @@
35
35
  "sass": "1.71.1",
36
36
  "sass-loader": "13.3.2"
37
37
  },
38
- "gitHead": "ec9f6b8dc92d0e7057f13f031d85436781302097"
38
+ "gitHead": "953ddcfdd3728cda9d1da39d665c3a2944ec56d5"
39
39
  }