@saasmakers/ui 1.5.10 → 2.0.1
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/app/components/bases/BaseAlert.vue +1 -2
- package/app/components/bases/BaseCard.vue +2 -4
- package/app/components/bases/BaseDivider.vue +2 -3
- package/app/components/bases/BaseIcon.vue +4 -5
- package/app/components/bases/BaseMetric.vue +4 -6
- package/app/components/bases/BaseOverlay.vue +1 -5
- package/app/components/bases/BaseQuote.vue +1 -2
- package/app/components/bases/BaseShortcut.vue +1 -3
- package/app/components/bases/BaseTag.vue +2 -3
- package/app/components/bases/BaseTags.vue +2 -3
- package/app/components/bases/BaseToast.vue +1 -3
- package/app/components/fields/FieldEmojis.vue +0 -1
- package/app/components/fields/FieldSelect.vue +2 -5
- package/app/components/fields/FieldTime.vue +1 -2
- package/app/components/layout/LayoutApps.vue +152 -0
- package/app/components/layout/LayoutToasts.vue +0 -1
- package/app/types/global.d.ts +3 -2
- package/app/types/layout.d.ts +5 -0
- package/app/utils/animations.ts +31 -0
- package/app/utils/chartist.ts +126 -0
- package/app/{composables/useLayerUtils.ts → utils/formatting.ts} +2 -9
- package/app/{composables/useLayerIcons.ts → utils/layerIcons.ts} +6 -13
- package/nuxt.config.ts +4 -0
- package/package.json +4 -1
- package/public/images/layout/LayoutApps/appstore-de.svg +40 -0
- package/public/images/layout/LayoutApps/appstore-en.svg +46 -0
- package/public/images/layout/LayoutApps/appstore-es.svg +47 -0
- package/public/images/layout/LayoutApps/appstore-fr.svg +50 -0
- package/public/images/layout/LayoutApps/appstore-id.svg +46 -0
- package/public/images/layout/LayoutApps/appstore-it.svg +42 -0
- package/public/images/layout/LayoutApps/appstore-ja.svg +35 -0
- package/public/images/layout/LayoutApps/appstore-ko.svg +35 -0
- package/public/images/layout/LayoutApps/appstore-nl.svg +40 -0
- package/public/images/layout/LayoutApps/appstore-pl.svg +46 -0
- package/public/images/layout/LayoutApps/appstore-pt-BR.svg +47 -0
- package/public/images/layout/LayoutApps/appstore-pt.svg +47 -0
- package/public/images/layout/LayoutApps/appstore-vi.svg +46 -0
- package/public/images/layout/LayoutApps/googleplay-de.svg +37 -0
- package/public/images/layout/LayoutApps/googleplay-en.svg +45 -0
- package/public/images/layout/LayoutApps/googleplay-es.svg +45 -0
- package/public/images/layout/LayoutApps/googleplay-fr.svg +45 -0
- package/public/images/layout/LayoutApps/googleplay-id.svg +45 -0
- package/public/images/layout/LayoutApps/googleplay-it.svg +14 -0
- package/public/images/layout/LayoutApps/googleplay-ja.svg +51 -0
- package/public/images/layout/LayoutApps/googleplay-ko.svg +14 -0
- package/public/images/layout/LayoutApps/googleplay-nl.svg +37 -0
- package/public/images/layout/LayoutApps/googleplay-pl.svg +45 -0
- package/public/images/layout/LayoutApps/googleplay-pt-BR.svg +45 -0
- package/public/images/layout/LayoutApps/googleplay-pt.svg +45 -0
- package/public/images/layout/LayoutApps/googleplay-vi.svg +45 -0
- package/app/composables/useChartist.ts +0 -130
- package/app/composables/useMotion.ts +0 -39
|
@@ -20,7 +20,6 @@ defineSlots<{
|
|
|
20
20
|
}>()
|
|
21
21
|
|
|
22
22
|
const { t } = useI18n()
|
|
23
|
-
const { getIcon } = useLayerIcons()
|
|
24
23
|
const isClosed = ref(false)
|
|
25
24
|
|
|
26
25
|
const buttonColor = computed<BaseColor>(() => {
|
|
@@ -89,7 +88,7 @@ async function onClose(event: MouseEvent) {
|
|
|
89
88
|
<BaseButton
|
|
90
89
|
v-if="isClosable"
|
|
91
90
|
:color="buttonColor"
|
|
92
|
-
:icon="
|
|
91
|
+
:icon="getLayerIcon('closeCircle')"
|
|
93
92
|
light
|
|
94
93
|
size="xs"
|
|
95
94
|
:text="t('closeThisMessage')"
|
|
@@ -35,8 +35,6 @@ defineSlots<{
|
|
|
35
35
|
right?: () => VNode[]
|
|
36
36
|
}>()
|
|
37
37
|
|
|
38
|
-
const { getIcon } = useLayerIcons()
|
|
39
|
-
|
|
40
38
|
const hasAvatarBox = computed<boolean>(() => {
|
|
41
39
|
return !!(props.avatar || props.emoji || props.icon || props.image)
|
|
42
40
|
})
|
|
@@ -199,7 +197,7 @@ function onClick(event: MouseEvent) {
|
|
|
199
197
|
'text-2xl': size === 'lg',
|
|
200
198
|
}"
|
|
201
199
|
color="green"
|
|
202
|
-
:icon="
|
|
200
|
+
:icon="getLayerIcon('checkCircle')"
|
|
203
201
|
/>
|
|
204
202
|
|
|
205
203
|
<BaseIcon
|
|
@@ -210,7 +208,7 @@ function onClick(event: MouseEvent) {
|
|
|
210
208
|
'text-xl': size === 'base',
|
|
211
209
|
'text-2xl': size === 'lg',
|
|
212
210
|
}"
|
|
213
|
-
:icon="
|
|
211
|
+
:icon="getLayerIcon('chevronRight')"
|
|
214
212
|
/>
|
|
215
213
|
|
|
216
214
|
<slot name="right" />
|
|
@@ -18,7 +18,6 @@ const emit = defineEmits<{
|
|
|
18
18
|
}>()
|
|
19
19
|
|
|
20
20
|
const { t } = useI18n()
|
|
21
|
-
const { getIcon } = useLayerIcons()
|
|
22
21
|
|
|
23
22
|
function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection) {
|
|
24
23
|
if (!props.loading) {
|
|
@@ -59,7 +58,7 @@ function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection)
|
|
|
59
58
|
v-if="navigable && !loading && !hidePrevious"
|
|
60
59
|
class="mr-2 shrink-0 hover:!no-underline"
|
|
61
60
|
clickable
|
|
62
|
-
:icon="
|
|
61
|
+
:icon="getLayerIcon('chevronLeft')"
|
|
63
62
|
size="xs"
|
|
64
63
|
:text="t('previous')"
|
|
65
64
|
@click="onNavigate($event, 'previous')"
|
|
@@ -127,7 +126,7 @@ function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection)
|
|
|
127
126
|
class="ml-2 shrink-0 hover:!no-underline"
|
|
128
127
|
clickable
|
|
129
128
|
reverse
|
|
130
|
-
:icon="
|
|
129
|
+
:icon="getLayerIcon('chevronRight')"
|
|
131
130
|
size="xs"
|
|
132
131
|
:text="t('next')"
|
|
133
132
|
@click="onNavigate($event, 'next')"
|
|
@@ -25,7 +25,6 @@ const emit = defineEmits<{
|
|
|
25
25
|
|
|
26
26
|
const confirming = ref(false)
|
|
27
27
|
const { t } = useI18n()
|
|
28
|
-
const { getIcon } = useLayerIcons()
|
|
29
28
|
|
|
30
29
|
const isClickable = computed(() => {
|
|
31
30
|
return props.clickable || props.to || props.confirmation
|
|
@@ -34,16 +33,16 @@ const isClickable = computed(() => {
|
|
|
34
33
|
const statusIcon = computed<string | undefined>(() => {
|
|
35
34
|
switch (props.status) {
|
|
36
35
|
case 'error': {
|
|
37
|
-
return
|
|
36
|
+
return getLayerIcon('closeCircle')
|
|
38
37
|
}
|
|
39
38
|
case 'info': {
|
|
40
|
-
return
|
|
39
|
+
return getLayerIcon('infoCircle')
|
|
41
40
|
}
|
|
42
41
|
case 'success': {
|
|
43
|
-
return
|
|
42
|
+
return getLayerIcon('checkCircle')
|
|
44
43
|
}
|
|
45
44
|
case 'warning': {
|
|
46
|
-
return
|
|
45
|
+
return getLayerIcon('exclamationCircle')
|
|
47
46
|
}
|
|
48
47
|
default: {
|
|
49
48
|
return undefined
|
|
@@ -10,18 +10,16 @@ const props = withDefaults(defineProps<BaseMetric>(), {
|
|
|
10
10
|
value: undefined,
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
-
const { getIcon } = useLayerIcons()
|
|
14
|
-
|
|
15
13
|
const performanceIcon = computed<string | undefined>(() => {
|
|
16
14
|
switch (props.performance) {
|
|
17
15
|
case 'down': {
|
|
18
|
-
return
|
|
16
|
+
return getLayerIcon('arrowDown')
|
|
19
17
|
}
|
|
20
18
|
case 'equal': {
|
|
21
|
-
return
|
|
19
|
+
return getLayerIcon('arrowRight')
|
|
22
20
|
}
|
|
23
21
|
case 'up': {
|
|
24
|
-
return
|
|
22
|
+
return getLayerIcon('arrowUp')
|
|
25
23
|
}
|
|
26
24
|
default: {
|
|
27
25
|
return undefined
|
|
@@ -51,7 +49,7 @@ const performanceIcon = computed<string | undefined>(() => {
|
|
|
51
49
|
v-if="performanceTooltip"
|
|
52
50
|
v-tooltip="performanceTooltip"
|
|
53
51
|
class="mr-1"
|
|
54
|
-
:icon="
|
|
52
|
+
:icon="getLayerIcon('infoCircle')"
|
|
55
53
|
/>
|
|
56
54
|
|
|
57
55
|
<BaseText
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { onKeyStroke } from '@vueuse/core'
|
|
3
3
|
import { Motion } from 'motion-v'
|
|
4
4
|
import type { BaseOverlay } from '../../types/bases'
|
|
5
|
-
import useMotion from '../../composables/useMotion'
|
|
6
5
|
|
|
7
6
|
const props = withDefaults(defineProps<BaseOverlay>(), {
|
|
8
7
|
active: true,
|
|
@@ -22,9 +21,6 @@ defineSlots<{
|
|
|
22
21
|
default?: () => VNode[]
|
|
23
22
|
}>()
|
|
24
23
|
|
|
25
|
-
const { getIcon } = useLayerIcons()
|
|
26
|
-
const { fadeIn } = useMotion()
|
|
27
|
-
|
|
28
24
|
const isClickable = computed(() => {
|
|
29
25
|
return props.clickable || props.hasClose
|
|
30
26
|
})
|
|
@@ -65,7 +61,7 @@ onKeyStroke('Escape', (event) => {
|
|
|
65
61
|
v-if="hasClose"
|
|
66
62
|
class="pointer-events-auto absolute right-4 top-4 z-50 text-gray-200 dark:text-gray-800"
|
|
67
63
|
clickable
|
|
68
|
-
:icon="
|
|
64
|
+
:icon="getLayerIcon('close')"
|
|
69
65
|
@click="onClose"
|
|
70
66
|
/>
|
|
71
67
|
|
|
@@ -29,7 +29,6 @@ defineSlots<{
|
|
|
29
29
|
|
|
30
30
|
const { locale, t } = useI18n()
|
|
31
31
|
const { translatedContent } = useTranslation(useSlots(), locale)
|
|
32
|
-
const { getIcon } = useLayerIcons()
|
|
33
32
|
const closed = ref(false)
|
|
34
33
|
|
|
35
34
|
const finalBackground = computed(() => {
|
|
@@ -148,7 +147,7 @@ function onClose(event: MouseEvent) {
|
|
|
148
147
|
'ml-3': size === 'sm',
|
|
149
148
|
'ml-4': size === 'base',
|
|
150
149
|
}"
|
|
151
|
-
:icon="
|
|
150
|
+
:icon="getLayerIcon('close')"
|
|
152
151
|
@click="onClose"
|
|
153
152
|
/>
|
|
154
153
|
</div>
|
|
@@ -11,8 +11,6 @@ const emit = defineEmits<{
|
|
|
11
11
|
trigger: [event: KeyboardEvent]
|
|
12
12
|
}>()
|
|
13
13
|
|
|
14
|
-
const { getIcon } = useLayerIcons()
|
|
15
|
-
|
|
16
14
|
function isTypingTarget(target: EventTarget | null) {
|
|
17
15
|
const element = target as HTMLElement | null
|
|
18
16
|
const typingTags = ['INPUT', 'SELECT', 'TEXTAREA']
|
|
@@ -53,7 +51,7 @@ onKeyStroke(
|
|
|
53
51
|
<Icon
|
|
54
52
|
v-if="shortcut.toLowerCase() === 'enter'"
|
|
55
53
|
class="size-2"
|
|
56
|
-
:name="
|
|
54
|
+
:name="getLayerIcon('enter')"
|
|
57
55
|
/>
|
|
58
56
|
|
|
59
57
|
<template v-else>
|
|
@@ -35,7 +35,6 @@ defineSlots<{
|
|
|
35
35
|
default?: () => VNode[]
|
|
36
36
|
}>()
|
|
37
37
|
|
|
38
|
-
const { getIcon } = useLayerIcons()
|
|
39
38
|
const hovered = ref(false)
|
|
40
39
|
const form = reactive({ name: '' })
|
|
41
40
|
|
|
@@ -136,7 +135,7 @@ function onRemove(event: MouseEvent) {
|
|
|
136
135
|
class="js-drag-handle mr-2 cursor-move"
|
|
137
136
|
clickable
|
|
138
137
|
color="gray"
|
|
139
|
-
:icon="
|
|
138
|
+
:icon="getLayerIcon('drag')"
|
|
140
139
|
/>
|
|
141
140
|
|
|
142
141
|
<span
|
|
@@ -190,7 +189,7 @@ function onRemove(event: MouseEvent) {
|
|
|
190
189
|
<BaseIcon
|
|
191
190
|
v-if="removable"
|
|
192
191
|
class="ml-1.5 text-red-700 dark:text-red-300 hover:text-black dark:hover:text-white"
|
|
193
|
-
:icon="
|
|
192
|
+
:icon="getLayerIcon('closeCircle')"
|
|
194
193
|
@click.prevent.stop="onRemove"
|
|
195
194
|
/>
|
|
196
195
|
</component>
|
|
@@ -34,7 +34,6 @@ const root = ref<HTMLDivElement>()
|
|
|
34
34
|
const showingAllTags = ref(false)
|
|
35
35
|
const showingTagCreationField = ref(false)
|
|
36
36
|
const { t } = useI18n()
|
|
37
|
-
const { getIcon } = useLayerIcons()
|
|
38
37
|
|
|
39
38
|
const sortedTags = computed({
|
|
40
39
|
get() {
|
|
@@ -143,7 +142,7 @@ function onUpdateTag(event: FocusEvent | KeyboardEvent, name: string, tagId?: nu
|
|
|
143
142
|
'm-1': size === 'base',
|
|
144
143
|
}"
|
|
145
144
|
color="indigo"
|
|
146
|
-
:icon="
|
|
145
|
+
:icon="getLayerIcon('back')"
|
|
147
146
|
:size="size"
|
|
148
147
|
:text="t('cancel')"
|
|
149
148
|
@click="onGoBack"
|
|
@@ -158,7 +157,7 @@ function onUpdateTag(event: FocusEvent | KeyboardEvent, name: string, tagId?: nu
|
|
|
158
157
|
}"
|
|
159
158
|
color="indigo"
|
|
160
159
|
:editable="showingTagCreationField"
|
|
161
|
-
:icon="showingTagCreationField ?
|
|
160
|
+
:icon="showingTagCreationField ? getLayerIcon('tags') : getLayerIcon('plus')"
|
|
162
161
|
is-creation
|
|
163
162
|
:light="false"
|
|
164
163
|
:size="size"
|
|
@@ -14,8 +14,6 @@ const emit = defineEmits<{
|
|
|
14
14
|
close: [event: KeyboardEvent | MouseEvent, toast: BaseToast]
|
|
15
15
|
}>()
|
|
16
16
|
|
|
17
|
-
const { getIcon } = useLayerIcons()
|
|
18
|
-
|
|
19
17
|
function onAction(event: KeyboardEvent | MouseEvent) {
|
|
20
18
|
if (props.action) {
|
|
21
19
|
emit('action', event, props)
|
|
@@ -82,7 +80,7 @@ function onClose(event: KeyboardEvent | MouseEvent) {
|
|
|
82
80
|
'group-hover:text-green-600 dark:group-hover:text-green-400': status === 'success',
|
|
83
81
|
'group-hover:text-orange-600 dark:group-hover:text-orange-400': status === 'warning',
|
|
84
82
|
}"
|
|
85
|
-
:icon="
|
|
83
|
+
:icon="getLayerIcon('close')"
|
|
86
84
|
/>
|
|
87
85
|
</template>
|
|
88
86
|
</div>
|
|
@@ -10,7 +10,6 @@ const emit = defineEmits<{
|
|
|
10
10
|
}>()
|
|
11
11
|
|
|
12
12
|
const { locale, t } = useI18n()
|
|
13
|
-
const { normalizeText } = useLayerUtils()
|
|
14
13
|
const modelValue = defineModel<FieldEmojis['modelValue']>({ default: '' })
|
|
15
14
|
const searchRaw = ref('')
|
|
16
15
|
const searchQuery = refDebounced(searchRaw, 250)
|
|
@@ -31,9 +31,6 @@ const emit = defineEmits<{
|
|
|
31
31
|
optionClick: [event: MouseEvent, value: string]
|
|
32
32
|
}>()
|
|
33
33
|
|
|
34
|
-
const { getIcon } = useLayerIcons()
|
|
35
|
-
const { fadeIn } = useMotion()
|
|
36
|
-
const { normalizeText } = useLayerUtils()
|
|
37
34
|
const { t } = useI18n()
|
|
38
35
|
const id = useId()
|
|
39
36
|
const modelValue = defineModel<FieldSelect['modelValue']>({ default: '' })
|
|
@@ -264,7 +261,7 @@ function selectOption(event: MouseEvent, value: string) {
|
|
|
264
261
|
class="ml-2 flex-initial"
|
|
265
262
|
:class="{ 'rotate-180': opened }"
|
|
266
263
|
color="gray"
|
|
267
|
-
:icon="
|
|
264
|
+
:icon="getLayerIcon('arrowDown')"
|
|
268
265
|
/>
|
|
269
266
|
</div>
|
|
270
267
|
|
|
@@ -320,7 +317,7 @@ function selectOption(event: MouseEvent, value: string) {
|
|
|
320
317
|
<BaseIcon
|
|
321
318
|
class="pointer-events-none"
|
|
322
319
|
color="gray"
|
|
323
|
-
:icon="
|
|
320
|
+
:icon="getLayerIcon('infoCircle')"
|
|
324
321
|
:size="size"
|
|
325
322
|
:text="t('noResults')"
|
|
326
323
|
/>
|
|
@@ -19,7 +19,6 @@ const emit = defineEmits<{
|
|
|
19
19
|
blur: [event: FocusEvent, value: string, name?: string]
|
|
20
20
|
}>()
|
|
21
21
|
|
|
22
|
-
const { getIcon } = useLayerIcons()
|
|
23
22
|
const id = useId()
|
|
24
23
|
const modelValue = defineModel<FieldTime['modelValue']>({ default: '' })
|
|
25
24
|
const inputRef = ref<HTMLInputElement>()
|
|
@@ -75,7 +74,7 @@ function onFieldBlur(event: FocusEvent) {
|
|
|
75
74
|
@keydown.space.prevent="onContainerKeyDown"
|
|
76
75
|
>
|
|
77
76
|
<BaseIcon
|
|
78
|
-
:icon="
|
|
77
|
+
:icon="getLayerIcon('clock')"
|
|
79
78
|
:size="size"
|
|
80
79
|
/>
|
|
81
80
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { getAppStoreUrl, getPlayStoreUrl } from '@saasmakers/apps-helpers'
|
|
3
|
+
import type { LayoutApps } from '../../types/layout'
|
|
4
|
+
|
|
5
|
+
const props = defineProps<LayoutApps>()
|
|
6
|
+
const { locale, t } = useI18n()
|
|
7
|
+
|
|
8
|
+
const appStoreUrl = computed(() => {
|
|
9
|
+
return getAppStoreUrl(props.appStoreId, locale.value)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const playStoreUrl = computed(() => {
|
|
13
|
+
return getPlayStoreUrl(props.playStoreId, locale.value)
|
|
14
|
+
})
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div>
|
|
19
|
+
<BaseDivider
|
|
20
|
+
border-style="dashed"
|
|
21
|
+
class="mb-12 mt-6"
|
|
22
|
+
:title="t('title')"
|
|
23
|
+
/>
|
|
24
|
+
|
|
25
|
+
<div class="flex items-center justify-center gap-3">
|
|
26
|
+
<a
|
|
27
|
+
v-if="appStoreUrl"
|
|
28
|
+
:aria-label="t('appStore')"
|
|
29
|
+
:href="appStoreUrl"
|
|
30
|
+
target="_blank"
|
|
31
|
+
>
|
|
32
|
+
<img
|
|
33
|
+
:alt="t('appStore')"
|
|
34
|
+
class="h-11 sm:h-12"
|
|
35
|
+
:src="`/images/layout/LayoutApps/appstore-${locale}.svg`"
|
|
36
|
+
>
|
|
37
|
+
</a>
|
|
38
|
+
|
|
39
|
+
<img
|
|
40
|
+
v-else
|
|
41
|
+
v-tooltip="t('comingSoon')"
|
|
42
|
+
:alt="t('appStore')"
|
|
43
|
+
class="h-11 sm:h-12"
|
|
44
|
+
:src="`/images/layout/LayoutApps/appstore-${locale}.svg`"
|
|
45
|
+
>
|
|
46
|
+
|
|
47
|
+
<a
|
|
48
|
+
v-if="playStoreUrl"
|
|
49
|
+
:aria-label="t('googlePlay')"
|
|
50
|
+
:href="playStoreUrl"
|
|
51
|
+
target="_blank"
|
|
52
|
+
>
|
|
53
|
+
<img
|
|
54
|
+
:alt="t('googlePlay')"
|
|
55
|
+
class="h-11 sm:h-12"
|
|
56
|
+
:src="`/images/layout/LayoutApps/googleplay-${locale}.svg`"
|
|
57
|
+
>
|
|
58
|
+
</a>
|
|
59
|
+
|
|
60
|
+
<img
|
|
61
|
+
v-else
|
|
62
|
+
v-tooltip="t('comingSoon')"
|
|
63
|
+
:alt="t('googlePlay')"
|
|
64
|
+
class="h-11 sm:h-12"
|
|
65
|
+
:src="`/images/layout/LayoutApps/googleplay-${locale}.svg`"
|
|
66
|
+
>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<i18n lang="json">
|
|
72
|
+
{
|
|
73
|
+
"de": {
|
|
74
|
+
"appStore": "Im App Store laden",
|
|
75
|
+
"comingSoon": "Demnächst",
|
|
76
|
+
"googlePlay": "Bei Google Play holen",
|
|
77
|
+
"title": "Probiere unsere Apps aus"
|
|
78
|
+
},
|
|
79
|
+
"en": {
|
|
80
|
+
"appStore": "Download on the App Store",
|
|
81
|
+
"comingSoon": "Coming soon",
|
|
82
|
+
"googlePlay": "Get it on Google Play",
|
|
83
|
+
"title": "Try our applications"
|
|
84
|
+
},
|
|
85
|
+
"es": {
|
|
86
|
+
"appStore": "Descargar en App Store",
|
|
87
|
+
"comingSoon": "Próximamente",
|
|
88
|
+
"googlePlay": "Disponible en Google Play",
|
|
89
|
+
"title": "Prueba nuestras aplicaciones"
|
|
90
|
+
},
|
|
91
|
+
"fr": {
|
|
92
|
+
"appStore": "Télécharger sur l'App Store",
|
|
93
|
+
"comingSoon": "Bientôt disponible",
|
|
94
|
+
"googlePlay": "Disponible sur Google Play",
|
|
95
|
+
"title": "Essayez nos applications"
|
|
96
|
+
},
|
|
97
|
+
"id": {
|
|
98
|
+
"appStore": "Unduh di App Store",
|
|
99
|
+
"comingSoon": "Segera hadir",
|
|
100
|
+
"googlePlay": "Dapatkan di Google Play",
|
|
101
|
+
"title": "Coba aplikasi kami"
|
|
102
|
+
},
|
|
103
|
+
"it": {
|
|
104
|
+
"appStore": "Scarica su App Store",
|
|
105
|
+
"comingSoon": "In arrivo",
|
|
106
|
+
"googlePlay": "Disponibile su Google Play",
|
|
107
|
+
"title": "Prova le nostre app"
|
|
108
|
+
},
|
|
109
|
+
"ja": {
|
|
110
|
+
"appStore": "App Storeでダウンロード",
|
|
111
|
+
"comingSoon": "近日公開予定",
|
|
112
|
+
"googlePlay": "Google Playで入手",
|
|
113
|
+
"title": "アプリケーションを試す"
|
|
114
|
+
},
|
|
115
|
+
"ko": {
|
|
116
|
+
"appStore": "App Store에서 다운로드",
|
|
117
|
+
"comingSoon": "곧 출시",
|
|
118
|
+
"googlePlay": "Google Play에서 받기",
|
|
119
|
+
"title": "앱을 사용해 보세요"
|
|
120
|
+
},
|
|
121
|
+
"nl": {
|
|
122
|
+
"appStore": "Download in de App Store",
|
|
123
|
+
"comingSoon": "Binnenkort beschikbaar",
|
|
124
|
+
"googlePlay": "Downloaden via Google Play",
|
|
125
|
+
"title": "Probeer onze apps"
|
|
126
|
+
},
|
|
127
|
+
"pl": {
|
|
128
|
+
"appStore": "Pobierz w App Store",
|
|
129
|
+
"comingSoon": "Wkrótce",
|
|
130
|
+
"googlePlay": "Pobierz z Google Play",
|
|
131
|
+
"title": "Wypróbuj nasze aplikacje"
|
|
132
|
+
},
|
|
133
|
+
"pt": {
|
|
134
|
+
"appStore": "Descarregar na App Store",
|
|
135
|
+
"comingSoon": "Em breve",
|
|
136
|
+
"googlePlay": "Disponível no Google Play",
|
|
137
|
+
"title": "Experimenta as nossas apps"
|
|
138
|
+
},
|
|
139
|
+
"pt-BR": {
|
|
140
|
+
"appStore": "Baixar na App Store",
|
|
141
|
+
"comingSoon": "Em breve",
|
|
142
|
+
"googlePlay": "Disponível no Google Play",
|
|
143
|
+
"title": "Experimente nossos aplicativos"
|
|
144
|
+
},
|
|
145
|
+
"vi": {
|
|
146
|
+
"appStore": "Tải trên App Store",
|
|
147
|
+
"comingSoon": "Sắp ra mắt",
|
|
148
|
+
"googlePlay": "Tải trên Google Play",
|
|
149
|
+
"title": "Dùng thử ứng dụng của chúng tôi"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
</i18n>
|
package/app/types/global.d.ts
CHANGED
|
@@ -96,12 +96,13 @@ declare global {
|
|
|
96
96
|
type FieldTime = import('./fields').FieldTime
|
|
97
97
|
|
|
98
98
|
// Layout
|
|
99
|
+
type LayoutApps = import('./layout').LayoutApps
|
|
99
100
|
type LayoutBottomSheet = import('./layout').LayoutBottomSheet
|
|
100
101
|
type LayoutModal = import('./layout').LayoutModal
|
|
101
102
|
|
|
102
103
|
// Project
|
|
103
|
-
type LayerIconIcon = import('../
|
|
104
|
-
type LayerIconValue = import('../
|
|
104
|
+
type LayerIconIcon = import('../utils/layerIcons').LayerIconIcon
|
|
105
|
+
type LayerIconValue = import('../utils/layerIcons').LayerIconValue
|
|
105
106
|
|
|
106
107
|
// Navigator
|
|
107
108
|
interface Navigator {
|
package/app/types/layout.d.ts
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const fadeIn = {
|
|
2
|
+
animate: {
|
|
3
|
+
opacity: 1,
|
|
4
|
+
transition: { duration: 0.25 },
|
|
5
|
+
},
|
|
6
|
+
initial: { opacity: 0 },
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const fadeInLeft = {
|
|
10
|
+
animate: {
|
|
11
|
+
opacity: 1,
|
|
12
|
+
transition: { duration: 0.25 },
|
|
13
|
+
x: 0,
|
|
14
|
+
},
|
|
15
|
+
initial: {
|
|
16
|
+
opacity: 0,
|
|
17
|
+
x: -25,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const fadeInUp = {
|
|
22
|
+
animate: {
|
|
23
|
+
opacity: 1,
|
|
24
|
+
transition: { duration: 0.25 },
|
|
25
|
+
y: 0,
|
|
26
|
+
},
|
|
27
|
+
initial: {
|
|
28
|
+
opacity: 0,
|
|
29
|
+
y: 25,
|
|
30
|
+
},
|
|
31
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { AreaDrawEvent, BarChartOptions, BarDrawEvent, BaseChart, DrawEvent, LineDrawEvent, Options } from 'chartist'
|
|
2
|
+
import { easings } from 'chartist'
|
|
3
|
+
|
|
4
|
+
type EasingType = keyof typeof easings | number[]
|
|
5
|
+
|
|
6
|
+
export function progressiveLinePlugin(params: {
|
|
7
|
+
animateArea?: boolean
|
|
8
|
+
delay?: number
|
|
9
|
+
duration?: number
|
|
10
|
+
easing?: EasingType
|
|
11
|
+
stagger?: number
|
|
12
|
+
} = {}) {
|
|
13
|
+
const animateArea = params.animateArea ?? true
|
|
14
|
+
const delay = params.delay ?? 0
|
|
15
|
+
const duration = params.duration ?? 500
|
|
16
|
+
const easing = params.easing ?? easings.easeOutQuart
|
|
17
|
+
const stagger = params.stagger ?? 0
|
|
18
|
+
|
|
19
|
+
return (chart: BaseChart) => {
|
|
20
|
+
chart.on('draw', (ctx: DrawEvent) => {
|
|
21
|
+
const begin = delay + (stagger ? stagger * (ctx.seriesIndex ?? 0) : 0)
|
|
22
|
+
|
|
23
|
+
// Animation for line charts
|
|
24
|
+
if (ctx.type === 'line') {
|
|
25
|
+
const lineCtx = ctx as LineDrawEvent
|
|
26
|
+
const node = lineCtx.element.getNode<SVGPathElement>()
|
|
27
|
+
const length = node.getTotalLength()
|
|
28
|
+
|
|
29
|
+
// Set the stroke dasharray and dashoffset
|
|
30
|
+
lineCtx.element.attr({
|
|
31
|
+
'stroke-dasharray': `${length}px ${length}px`,
|
|
32
|
+
'stroke-dashoffset': `${length}px`,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Animate the stroke dashoffset to 0
|
|
36
|
+
lineCtx.element.animate({
|
|
37
|
+
'stroke-dashoffset': {
|
|
38
|
+
begin,
|
|
39
|
+
dur: duration,
|
|
40
|
+
easing,
|
|
41
|
+
fill: 'freeze',
|
|
42
|
+
from: `${length}px`,
|
|
43
|
+
to: '0px',
|
|
44
|
+
},
|
|
45
|
+
}, false)
|
|
46
|
+
|
|
47
|
+
// Clean up the dash attributes after it finishes
|
|
48
|
+
const total = delay + duration + (stagger ? stagger * (lineCtx.seriesIndex ?? 0) : 0)
|
|
49
|
+
|
|
50
|
+
globalThis.setTimeout(() => {
|
|
51
|
+
lineCtx.element.attr({
|
|
52
|
+
'stroke-dasharray': undefined,
|
|
53
|
+
'stroke-dashoffset': undefined,
|
|
54
|
+
})
|
|
55
|
+
}, total + 30)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Animation for area charts
|
|
59
|
+
if (animateArea && ctx.type === 'area') {
|
|
60
|
+
const areaCtx = ctx as AreaDrawEvent
|
|
61
|
+
|
|
62
|
+
areaCtx.element.animate({
|
|
63
|
+
|
|
64
|
+
d: {
|
|
65
|
+
begin,
|
|
66
|
+
dur: duration,
|
|
67
|
+
easing,
|
|
68
|
+
fill: 'freeze',
|
|
69
|
+
from: areaCtx.path
|
|
70
|
+
.clone()
|
|
71
|
+
.scale(1, 0)
|
|
72
|
+
.translate(0, areaCtx.chartRect.height())
|
|
73
|
+
.stringify(),
|
|
74
|
+
to: areaCtx.path.stringify(),
|
|
75
|
+
},
|
|
76
|
+
}, false)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Animation for bar charts
|
|
80
|
+
if (ctx.type === 'bar') {
|
|
81
|
+
const barCtx = ctx as BarDrawEvent
|
|
82
|
+
|
|
83
|
+
// Access private options property via type assertion
|
|
84
|
+
const chartOptions = (chart as unknown as {
|
|
85
|
+
options?: Options & Partial<BarChartOptions>
|
|
86
|
+
}).options
|
|
87
|
+
|
|
88
|
+
const horizontal = !!(chartOptions as BarChartOptions | undefined)?.horizontalBars
|
|
89
|
+
|
|
90
|
+
// For vertical bars, y1 is baseline, y2 is top. For horizontal, x1 is baseline, x2 is end.
|
|
91
|
+
const from = horizontal ? barCtx.x1 : barCtx.y1
|
|
92
|
+
const to = horizontal ? barCtx.x2 : barCtx.y2
|
|
93
|
+
|
|
94
|
+
// Start collapsed at baseline
|
|
95
|
+
if (horizontal) {
|
|
96
|
+
barCtx.element.attr({ x2: from })
|
|
97
|
+
|
|
98
|
+
barCtx.element.animate({
|
|
99
|
+
x2: {
|
|
100
|
+
begin,
|
|
101
|
+
dur: duration,
|
|
102
|
+
easing,
|
|
103
|
+
fill: 'freeze',
|
|
104
|
+
from: String(from),
|
|
105
|
+
to: String(to),
|
|
106
|
+
},
|
|
107
|
+
}, false)
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
barCtx.element.attr({ y2: from })
|
|
111
|
+
|
|
112
|
+
barCtx.element.animate({
|
|
113
|
+
y2: {
|
|
114
|
+
begin,
|
|
115
|
+
dur: duration,
|
|
116
|
+
easing,
|
|
117
|
+
fill: 'freeze',
|
|
118
|
+
from: String(from),
|
|
119
|
+
to: String(to),
|
|
120
|
+
},
|
|
121
|
+
}, false)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|