@dative-gpi/foundation-shared-components 1.0.181 → 1.0.182

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>
@@ -1,8 +1,11 @@
1
+ export * from "./useAccessibilityPreferences";
1
2
  export * from "./useAddress";
2
3
  export * from "./useAutocomplete";
3
4
  export * from "./useBreakpoints";
4
5
  export * from "./useColors";
6
+ export * from "./useCountUp";
5
7
  export * from "./useDebounce";
8
+ export * from "./useElementVisibility";
6
9
  export * from "./useMapLayers";
7
10
  export * from "./useRules";
8
11
  export * from "./useSlots";
@@ -0,0 +1,32 @@
1
+ import { ref, onMounted, onBeforeUnmount } from 'vue';
2
+
3
+ const PREFERS_REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
4
+
5
+ export function useAccessibilityPreferences() {
6
+ const prefersReducedMotion = ref(false);
7
+ let mediaQuery: MediaQueryList | null = null;
8
+
9
+ const handleChange = (e: MediaQueryListEvent) => {
10
+ prefersReducedMotion.value = e.matches;
11
+ };
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
+
29
+ return {
30
+ prefersReducedMotion
31
+ };
32
+ }
@@ -0,0 +1,41 @@
1
+ import { onBeforeUnmount, ref, readonly } from 'vue';
2
+
3
+ export const useAnimationFrame = (callback: () => void) => {
4
+ let animationId: number | null = null;
5
+ const isActive = ref(false);
6
+
7
+ const start = () => {
8
+ if (isActive.value) {
9
+ return;
10
+ }
11
+ isActive.value = true;
12
+
13
+ const animate = () => {
14
+ if (!isActive.value) {
15
+ return;
16
+ }
17
+
18
+ callback();
19
+ animationId = requestAnimationFrame(animate);
20
+ };
21
+ animationId = requestAnimationFrame(animate);
22
+ };
23
+
24
+ const stop = () => {
25
+ isActive.value = 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: readonly(isActive)
40
+ };
41
+ };
@@ -0,0 +1,74 @@
1
+ import { ref, computed, watch, type Ref, type ComponentPublicInstance } from 'vue';
2
+ import { useAccessibilityPreferences } from './useAccessibilityPreferences';
3
+ import { useAnimationFrame } from './useAnimationFrame';
4
+ import { useElementVisibility } from './useElementVisibility';
5
+
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
+ ) {
14
+ const { prefersReducedMotion } = useAccessibilityPreferences();
15
+
16
+ const { onVisible } = useElementVisibility(
17
+ elementRef
18
+ );
19
+
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
+
26
+ const displayText = computed(() => {
27
+ const text = String(current.value);
28
+ return pad.value > 0 ? text.padStart(pad.value, "0") : text;
29
+ });
30
+
31
+ const { start: afStart, stop: afStop } = useAnimationFrame(() => {
32
+ const elapsed = performance.now() - startTime.value;
33
+ const progress = Math.min(1, elapsed / duration.value);
34
+
35
+ if (progress >= 1) {
36
+ current.value = to.value;
37
+ animating.value = false;
38
+ afStop();
39
+ return;
40
+ }
41
+
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;
50
+ return;
51
+ }
52
+
53
+ from.value = current.value;
54
+ to.value = targetValue.value;
55
+ startTime.value = performance.now();
56
+ animating.value = true;
57
+ afStart();
58
+ }
59
+
60
+ watch(targetValue, () => {
61
+ start();
62
+ }, { immediate: true });
63
+
64
+ onVisible.value = () => {
65
+ if (startOnVisible.value && !animating.value) {
66
+ current.value = from.value;
67
+ start();
68
+ }
69
+ };
70
+
71
+ return {
72
+ displayText,
73
+ };
74
+ }
@@ -0,0 +1,41 @@
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;
10
+
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
+ }
19
+ }, { threshold });
20
+
21
+ observer.observe(el);
22
+ };
23
+
24
+ const stopObserver = () => {
25
+ observer?.disconnect();
26
+ observer = null;
27
+ };
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.181",
4
+ "version": "1.0.182",
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.181",
14
- "@dative-gpi/foundation-shared-services": "1.0.181"
13
+ "@dative-gpi/foundation-shared-domain": "1.0.182",
14
+ "@dative-gpi/foundation-shared-services": "1.0.182"
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": "a6836b28ce33081e2a05e2088a41df6d8083629e"
38
+ "gitHead": "1e64f82da4dcedf3908383bb0d7c6a33f85ac0f2"
39
39
  }