nextia 7.0.0 → 7.0.2
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 +9 -13
- package/src/bin.js +1 -2
- package/src/{lib.js → lib/fx.js} +16 -31
- package/src/lib/index.js +32 -0
- package/src/lib/ui.js +112 -0
- package/src/lib/utils.js +107 -0
- package/template/biome.json +21 -0
- package/template/package.json +3 -3
- package/template/src/assets/img/image.svg +6 -0
- package/template/src/assets/img/image.webp +0 -0
- package/template/src/components/Counter/index.jsx +1 -2
- package/template/src/components/Message/index.jsx +1 -1
- package/template/src/components/index.js +1 -5
- package/template/src/components/ui/Translate/index.jsx +10 -8
- package/template/src/pages/env/index.jsx +1 -2
- package/template/src/pages/icons/index.jsx +10 -9
- package/template/src/pages/icons/style.css +3 -0
- package/template/src/pages/images/index.jsx +10 -5
- package/template/src/pages/images/style.css +21 -2
- package/template/src/pages/index.jsx +22 -12
- package/template/src/pages/search-params/index.jsx +1 -2
- package/template/src/pages/translate/index.jsx +2 -2
- package/template/src/pages/{counter → view-transition}/index.jsx +2 -23
- package/template/src/pages/view-transition/style.css +2 -0
- package/template/src/services/api.js +1 -1
- package/template/src/theme/icons/icons.svg +3 -4
- package/template/src/theme/icons/index.css +55 -0
- package/template/src/theme/index.css +2 -2
- package/template/src/theme/{util.css → utils/index.css} +2 -0
- package/template/src/utils/index.js +1 -15
- package/template/test/index.test.js +2 -3
- package/template/src/assets/img/image.jpg +0 -0
- package/template/src/components/ui/I18n/index.jsx +0 -23
- package/template/src/components/ui/Icon/index.jsx +0 -50
- package/template/src/components/ui/Link/index.jsx +0 -12
- package/template/src/components/ui/Svg/index.jsx +0 -54
- package/template/src/pages/counter/style.css +0 -2
- package/template/src/theme/icons/exit.svg +0 -69
- package/template/src/utils/hooks.js +0 -49
- /package/template/src/pages/{counter → view-transition}/functions.js +0 -0
- /package/template/src/theme/{animations.css → utils/view-transition.css} +0 -0
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextia",
|
|
3
3
|
"description": "Create fast web applications",
|
|
4
|
-
"version": "7.0.
|
|
4
|
+
"version": "7.0.2",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
5
7
|
"engines": {
|
|
6
8
|
"node": ">22"
|
|
7
9
|
},
|
|
8
|
-
"type": "module",
|
|
9
|
-
"license": "MIT",
|
|
10
10
|
"author": {
|
|
11
11
|
"name": "Sinuhe Maceda",
|
|
12
12
|
"email": "sinuhe.dev@gmail.com",
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
"bin": {
|
|
24
24
|
"nextia": "src/bin.js"
|
|
25
25
|
},
|
|
26
|
-
"
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./src/lib/index.js"
|
|
28
|
+
},
|
|
27
29
|
"scripts": {
|
|
28
30
|
"clean": "rm -fr my-app node_modules package-lock.json .coverage out",
|
|
29
31
|
"format": "biome format",
|
|
@@ -32,13 +34,7 @@
|
|
|
32
34
|
"test": "vitest run",
|
|
33
35
|
"test:name": "vitest run --testNamePattern",
|
|
34
36
|
"test:silent": "vitest run --silent",
|
|
35
|
-
"test:coverage": "vitest run --silent --coverage"
|
|
36
|
-
"test:version": "./src/bin.js",
|
|
37
|
-
"test:my-app": "rm -fr my-app && src/bin.js my-app",
|
|
38
|
-
"test:my-app:exists": "src/bin.js my-app",
|
|
39
|
-
"test:my-page": "cd my-app && ../src/bin.js page my-page",
|
|
40
|
-
"test:my-component": "cd my-app && ../src/bin.js component MyComponent",
|
|
41
|
-
"test:my-container": "cd my-app && ../src/bin.js container MyContainer"
|
|
37
|
+
"test:coverage": "vitest run --silent --coverage"
|
|
42
38
|
},
|
|
43
39
|
"peerDependencies": {
|
|
44
40
|
"react": "^19.2.3",
|
|
@@ -46,8 +42,8 @@
|
|
|
46
42
|
},
|
|
47
43
|
"devDependencies": {
|
|
48
44
|
"@vitejs/plugin-react": "^6.0.1",
|
|
49
|
-
"@vitest/coverage-v8": "^4.
|
|
45
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
50
46
|
"jsdom": "^29.0.1",
|
|
51
|
-
"vitest": "^4.
|
|
47
|
+
"vitest": "^4.1.2"
|
|
52
48
|
}
|
|
53
49
|
}
|
package/src/bin.js
CHANGED
|
@@ -190,7 +190,6 @@ async function createProject(name) {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
await cp(templatePath, projectPath, { recursive: true })
|
|
193
|
-
await cp(`${__dirname}/../biome.json`, `${projectPath}/biome.json`)
|
|
194
193
|
|
|
195
194
|
await Promise.all(
|
|
196
195
|
['env.dev', 'env.prod', 'env.test', 'gitignore'].map((fileName) =>
|
|
@@ -230,7 +229,7 @@ async function main() {
|
|
|
230
229
|
|
|
231
230
|
default:
|
|
232
231
|
if (ARG1) await createProject(ARG1)
|
|
233
|
-
else console.info(`v${pkg.version}\
|
|
232
|
+
else console.info(`v${pkg.version}\nnextia <ProjectName>`)
|
|
234
233
|
break
|
|
235
234
|
}
|
|
236
235
|
}
|
package/src/{lib.js → lib/fx.js}
RENAMED
|
@@ -10,31 +10,12 @@
|
|
|
10
10
|
import { createContext, use, useReducer } from 'react'
|
|
11
11
|
|
|
12
12
|
const LOGGER = import.meta.env.DEV && import.meta.env.PUBLIC_LOGGER !== 'false'
|
|
13
|
-
const
|
|
13
|
+
const PagesFx = createContext()
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* util
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
function css(...classNames) {
|
|
20
|
-
return classNames
|
|
21
|
-
.reduce((accumulator, currentValue) => {
|
|
22
|
-
if (typeof currentValue === 'string') {
|
|
23
|
-
accumulator.push(currentValue.trim())
|
|
24
|
-
} else if (
|
|
25
|
-
!Array.isArray(currentValue) &&
|
|
26
|
-
typeof currentValue === 'object'
|
|
27
|
-
) {
|
|
28
|
-
for (const e in currentValue) {
|
|
29
|
-
if (currentValue[e]) accumulator.push(e.trim())
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return accumulator
|
|
33
|
-
}, [])
|
|
34
|
-
.filter((e) => e)
|
|
35
|
-
.join(' ')
|
|
36
|
-
}
|
|
37
|
-
|
|
38
19
|
function values(state, payload, value) {
|
|
39
20
|
const paths = payload.split('.')
|
|
40
21
|
|
|
@@ -166,7 +147,7 @@ const reducerLogger = (state, action) => {
|
|
|
166
147
|
*/
|
|
167
148
|
|
|
168
149
|
function useFx(functions = { initialState: {} }) {
|
|
169
|
-
const
|
|
150
|
+
const pagesFx = use(PagesFx)
|
|
170
151
|
const { initialState } = functions
|
|
171
152
|
const [state, dispatch] = useReducer(
|
|
172
153
|
LOGGER ? reducerLogger : reducer,
|
|
@@ -177,7 +158,12 @@ function useFx(functions = { initialState: {} }) {
|
|
|
177
158
|
const commonActions = ['set', 'show', 'hide', 'change', 'reset'].reduce(
|
|
178
159
|
(acc, e) => {
|
|
179
160
|
acc[e] = (payload) =>
|
|
180
|
-
dispatch({
|
|
161
|
+
dispatch({
|
|
162
|
+
type: e,
|
|
163
|
+
payload,
|
|
164
|
+
initialState,
|
|
165
|
+
isContext: !pagesFx?.context
|
|
166
|
+
})
|
|
181
167
|
return acc
|
|
182
168
|
},
|
|
183
169
|
{}
|
|
@@ -190,10 +176,8 @@ function useFx(functions = { initialState: {} }) {
|
|
|
190
176
|
const actionsProps = {
|
|
191
177
|
...commonActions,
|
|
192
178
|
state,
|
|
193
|
-
payload
|
|
194
|
-
|
|
195
|
-
if (pageContext) {
|
|
196
|
-
actionsProps.context = pageContext
|
|
179
|
+
payload,
|
|
180
|
+
context: pagesFx?.context
|
|
197
181
|
}
|
|
198
182
|
|
|
199
183
|
return functions[e](Object.freeze(actionsProps))
|
|
@@ -206,13 +190,14 @@ function useFx(functions = { initialState: {} }) {
|
|
|
206
190
|
const props = {
|
|
207
191
|
initialState,
|
|
208
192
|
state,
|
|
209
|
-
fx: { ...commonActions, ...actions }
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
193
|
+
fx: { ...commonActions, ...actions },
|
|
194
|
+
//
|
|
195
|
+
context: pagesFx?.context,
|
|
196
|
+
icons: pagesFx?.icons,
|
|
197
|
+
i18n: pagesFx?.i18n
|
|
213
198
|
}
|
|
214
199
|
|
|
215
200
|
return Object.freeze(props)
|
|
216
201
|
}
|
|
217
202
|
|
|
218
|
-
export {
|
|
203
|
+
export { PagesFx, useFx }
|
package/src/lib/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Sinuhe Maceda https://sinuhe.dev
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* https://github.com/sinuhedev/nextia
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { PagesFx, useFx } from './fx.js'
|
|
11
|
+
import { I18n, Icon, Link, Svg } from './ui.js'
|
|
12
|
+
import {
|
|
13
|
+
css,
|
|
14
|
+
env,
|
|
15
|
+
startViewTransition,
|
|
16
|
+
useQueryString,
|
|
17
|
+
useResize
|
|
18
|
+
} from './utils.js'
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
css,
|
|
22
|
+
env,
|
|
23
|
+
I18n,
|
|
24
|
+
Icon,
|
|
25
|
+
Link,
|
|
26
|
+
PagesFx,
|
|
27
|
+
Svg,
|
|
28
|
+
startViewTransition,
|
|
29
|
+
useFx,
|
|
30
|
+
useQueryString,
|
|
31
|
+
useResize
|
|
32
|
+
}
|
package/src/lib/ui.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { createElement, useEffect, useRef } from 'react'
|
|
2
|
+
import { useFx } from './fx'
|
|
3
|
+
import { css } from './utils'
|
|
4
|
+
|
|
5
|
+
function Link({ children, href, value = {}, ...props }) {
|
|
6
|
+
href ??= window.location.hash.split('?')[0]
|
|
7
|
+
value = Object.keys(value).length
|
|
8
|
+
? `?${new URLSearchParams(value).toString()}`
|
|
9
|
+
: ''
|
|
10
|
+
|
|
11
|
+
return createElement('a', { href: href + value, ...props }, children)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function I18n({ value, args = [] }) {
|
|
15
|
+
const { context, i18n } = useFx()
|
|
16
|
+
|
|
17
|
+
if (i18n) {
|
|
18
|
+
try {
|
|
19
|
+
let text = value.split('.').reduce((ac, el) => ac[el], i18n)
|
|
20
|
+
text = text[i18n.locales.indexOf(context.state.i18n.currentLocale)]
|
|
21
|
+
|
|
22
|
+
if (args) {
|
|
23
|
+
text = text.replace(
|
|
24
|
+
/([{}])\\1|[{](.*?)(?:!(.+?))?[}]/g,
|
|
25
|
+
(match, _literal, number) => args[number] || match
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return text
|
|
30
|
+
} catch {
|
|
31
|
+
console.error(`Error in [il8n] => ${value}`)
|
|
32
|
+
return value
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Icon({
|
|
38
|
+
id,
|
|
39
|
+
className,
|
|
40
|
+
animate = false,
|
|
41
|
+
style,
|
|
42
|
+
width = '48',
|
|
43
|
+
height,
|
|
44
|
+
viewBox = '0 0 48 48',
|
|
45
|
+
fill = 'none',
|
|
46
|
+
color = 'currentColor',
|
|
47
|
+
stroke = 'currentColor',
|
|
48
|
+
strokeWidth = '2',
|
|
49
|
+
strokeLinecap = 'round',
|
|
50
|
+
strokeLinejoin = 'round',
|
|
51
|
+
...props
|
|
52
|
+
}) {
|
|
53
|
+
const { icons } = useFx()
|
|
54
|
+
const ref = useRef()
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (icons) {
|
|
58
|
+
const svg = new DOMParser()
|
|
59
|
+
.parseFromString(icons, 'image/svg+xml')
|
|
60
|
+
.documentElement.getElementById(id)
|
|
61
|
+
|
|
62
|
+
if (svg) {
|
|
63
|
+
ref.current.innerHTML = svg.innerHTML
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}, [id, icons])
|
|
67
|
+
|
|
68
|
+
return createElement('svg', {
|
|
69
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
70
|
+
ref,
|
|
71
|
+
id,
|
|
72
|
+
className: css({ 'nextia-animate-icon': animate }, className),
|
|
73
|
+
style,
|
|
74
|
+
width,
|
|
75
|
+
height: height ?? width,
|
|
76
|
+
viewBox,
|
|
77
|
+
fill,
|
|
78
|
+
color,
|
|
79
|
+
stroke,
|
|
80
|
+
strokeWidth,
|
|
81
|
+
strokeLinecap,
|
|
82
|
+
strokeLinejoin,
|
|
83
|
+
...props
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function Svg({ ref, src, width, height, ...props }) {
|
|
88
|
+
ref ??= useRef()
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
const svg = new DOMParser().parseFromString(
|
|
92
|
+
src,
|
|
93
|
+
'image/svg+xml'
|
|
94
|
+
).documentElement
|
|
95
|
+
|
|
96
|
+
for (const { name, value } of svg.attributes) {
|
|
97
|
+
if (name !== 'width' && name !== 'height')
|
|
98
|
+
ref.current.setAttribute(name, value)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
ref.current.replaceChildren(...svg.children)
|
|
102
|
+
}, [src, ref])
|
|
103
|
+
|
|
104
|
+
return createElement('svg', {
|
|
105
|
+
ref,
|
|
106
|
+
width,
|
|
107
|
+
height: height ?? width,
|
|
108
|
+
...props
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export { I18n, Icon, Link, Svg }
|
package/src/lib/utils.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Sinuhe Maceda https://sinuhe.dev
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* https://github.com/sinuhedev/nextia
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
11
|
+
import { flushSync } from 'react-dom'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* env
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const env = import.meta.env
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* View Transition
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
async function startViewTransition(fun = () => {}, ref, animation = 'fade') {
|
|
24
|
+
if (!document.startViewTransition || env.PUBLIC_VIEW_TRANSITION === 'false')
|
|
25
|
+
return fun()
|
|
26
|
+
|
|
27
|
+
ref.style.viewTransitionName = animation
|
|
28
|
+
await document.startViewTransition(() => flushSync(fun)).finished
|
|
29
|
+
ref.style.viewTransitionName = ''
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* hooks
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
function useQueryString() {
|
|
37
|
+
const getQueryString = useCallback(
|
|
38
|
+
() => ({
|
|
39
|
+
hash: window.location.hash.split('?')[0],
|
|
40
|
+
queryString: Object.fromEntries(
|
|
41
|
+
new URLSearchParams(window.location.hash.split('?')[1])
|
|
42
|
+
)
|
|
43
|
+
}),
|
|
44
|
+
[]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const [queryString, setQueryString] = useState(getQueryString)
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const handlePopState = () => setQueryString(getQueryString())
|
|
51
|
+
|
|
52
|
+
window.addEventListener('popstate', handlePopState)
|
|
53
|
+
return () => {
|
|
54
|
+
window.removeEventListener('popstate', handlePopState)
|
|
55
|
+
}
|
|
56
|
+
}, [getQueryString])
|
|
57
|
+
|
|
58
|
+
return queryString
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function useResize() {
|
|
62
|
+
const getResize = useCallback(
|
|
63
|
+
() => ({
|
|
64
|
+
width: window.innerWidth,
|
|
65
|
+
height: window.innerHeight
|
|
66
|
+
}),
|
|
67
|
+
[]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const [resize, setResize] = useState(getResize)
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const handleResize = () => setResize(getResize())
|
|
74
|
+
|
|
75
|
+
window.addEventListener('resize', handleResize)
|
|
76
|
+
return () => {
|
|
77
|
+
window.removeEventListener('resize', handleResize)
|
|
78
|
+
}
|
|
79
|
+
}, [getResize])
|
|
80
|
+
|
|
81
|
+
return resize
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* util
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
function css(...classNames) {
|
|
89
|
+
return classNames
|
|
90
|
+
.reduce((accumulator, currentValue) => {
|
|
91
|
+
if (typeof currentValue === 'string') {
|
|
92
|
+
accumulator.push(currentValue.trim())
|
|
93
|
+
} else if (
|
|
94
|
+
!Array.isArray(currentValue) &&
|
|
95
|
+
typeof currentValue === 'object'
|
|
96
|
+
) {
|
|
97
|
+
for (const e in currentValue) {
|
|
98
|
+
if (currentValue[e]) accumulator.push(e.trim())
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return accumulator
|
|
102
|
+
}, [])
|
|
103
|
+
.filter((e) => e)
|
|
104
|
+
.join(' ')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { css, env, startViewTransition, useQueryString, useResize }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatter": {
|
|
3
|
+
"indentStyle": "space",
|
|
4
|
+
"indentWidth": 2
|
|
5
|
+
},
|
|
6
|
+
|
|
7
|
+
"javascript": {
|
|
8
|
+
"formatter": {
|
|
9
|
+
"semicolons": "asNeeded",
|
|
10
|
+
"quoteStyle": "single",
|
|
11
|
+
"jsxQuoteStyle": "double",
|
|
12
|
+
"trailingCommas": "none"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
"css": {
|
|
17
|
+
"parser": {
|
|
18
|
+
"tailwindDirectives": true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/template/package.json
CHANGED
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@vitejs/plugin-react": "^6.0.1",
|
|
25
|
-
"@vitest/coverage-v8": "^4.
|
|
25
|
+
"@vitest/coverage-v8": "^4.1.2",
|
|
26
26
|
"jsdom": "^29.0.1",
|
|
27
|
-
"vite": "^8.0.
|
|
28
|
-
"vitest": "^4.
|
|
27
|
+
"vite": "^8.0.3",
|
|
28
|
+
"vitest": "^4.1.2"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"nextia": "file:../",
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg width="256" height="256" viewBox="0 0 48 48" strokeWidth="1" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs id="defs" />
|
|
3
|
+
<path id="background" d="m 6.9596e-8,0 h 50.155103930404003 v 48 h -50.155103930404003 z" fill="#f7df1e" />
|
|
4
|
+
<path id="label"
|
|
5
|
+
d="m 14.776102,40.11225 3.673125,-2.223 c 0.70875,1.256438 1.353375,2.319562 2.899688,2.319562 1.482186,0 2.416875,-0.57975 2.416875,-2.835 v -15.337125 h 4.510687 v 15.400875 c 0,4.671937 -2.738625,6.798562 -6.73425,6.798562 -3.608437,0 -5.703001,-1.868812 -6.766313,-4.124249 m 15.950625,-0.483 3.67275,-2.126438 c 0.966938,1.578938 2.223563,2.738813 4.446562,2.738813 1.869189,0 3.060938,-0.934501 3.060938,-2.223375 0,-1.5465 -1.224375,-2.094376 -3.286499,-2.99625 l -1.127439,-0.48375 c -3.254437,-1.385062 -5.413124,-3.125063 -5.413124,-6.798188 0,-3.38325 2.577562,-5.961 6.605249,-5.961 2.867626,0 4.929751,0.999001 6.411751,3.608813 l -3.51225,2.255625 c -0.773438,-1.385438 -1.610813,-1.933125 -2.899688,-1.933125 -1.321124,0 -2.158875,0.83775 -2.158875,1.933125 0,1.353187 0.837751,1.90125 2.770875,2.739 l 1.127625,0.483187 c 3.834375,1.643438 5.993063,3.318751 5.993063,7.088251 0,4.060124 -3.18975,6.283124 -7.475063,6.283124 -4.188562,0 -6.895125,-1.997625 -8.216062,-4.607625" />
|
|
6
|
+
</svg>
|
|
Binary file
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import Counter from './Counter'
|
|
2
2
|
import Message from './Message'
|
|
3
3
|
// ui
|
|
4
|
-
import I18n from './ui/I18n'
|
|
5
|
-
import Icon from './ui/Icon'
|
|
6
|
-
import Link from './ui/Link'
|
|
7
|
-
import Svg from './ui/Svg'
|
|
8
4
|
import Translate from './ui/Translate'
|
|
9
5
|
|
|
10
|
-
export { Counter,
|
|
6
|
+
export { Counter, Message, Translate }
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
1
|
+
import { useFx } from 'nextia'
|
|
2
|
+
|
|
3
|
+
export default function UiTranslate({ className, style }) {
|
|
4
|
+
const { context } = useFx()
|
|
5
|
+
|
|
6
|
+
const { currentLocale, locales } = context.state.i18n
|
|
7
|
+
const { changeI18n } = context.fx
|
|
8
|
+
|
|
7
9
|
return (
|
|
8
10
|
<article className={className} style={style}>
|
|
9
|
-
<select value={
|
|
10
|
-
{
|
|
11
|
+
<select value={currentLocale} onChange={changeI18n}>
|
|
12
|
+
{locales.map((e) => (
|
|
11
13
|
<option key={e} value={e} className="m-2">
|
|
12
14
|
{e}
|
|
13
15
|
</option>
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { Icon,
|
|
2
|
-
import { css, useFx } from 'nextia'
|
|
1
|
+
import { css, Icon, useFx } from 'nextia'
|
|
3
2
|
import functions from './functions'
|
|
4
3
|
import './style.css'
|
|
5
|
-
import exitSvg from 'theme/icons/exit.svg?raw'
|
|
6
4
|
|
|
7
5
|
export default function IconsPage() {
|
|
8
6
|
const { state, fx } = useFx(functions)
|
|
9
7
|
|
|
10
8
|
return (
|
|
11
9
|
<section className={css('IconsPage', '')}>
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
<article>
|
|
11
|
+
<Icon id="globe" width="32" />
|
|
12
|
+
<Icon id="camera" width="32" />
|
|
13
|
+
<Icon id="video" width="32" />
|
|
14
|
+
</article>
|
|
15
|
+
|
|
16
|
+
<article>
|
|
17
|
+
<Icon id="exit" animate width="32" />
|
|
18
|
+
</article>
|
|
18
19
|
</section>
|
|
19
20
|
)
|
|
20
21
|
}
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import image from 'assets/img/image.
|
|
2
|
-
import { css, useFx } from 'nextia'
|
|
1
|
+
import image from 'assets/img/image.webp'
|
|
2
|
+
import { css, Svg, useFx } from 'nextia'
|
|
3
3
|
import functions from './functions'
|
|
4
4
|
import './style.css'
|
|
5
|
+
import imageSvg from 'assets/img/image.svg?raw'
|
|
5
6
|
|
|
6
7
|
export default function ImagesPage() {
|
|
7
8
|
const { state, fx } = useFx(functions)
|
|
8
9
|
|
|
9
10
|
return (
|
|
10
11
|
<section className={css('ImagesPage', '')}>
|
|
11
|
-
<br />
|
|
12
12
|
<p>css-img</p>
|
|
13
13
|
<div className="css-img" />
|
|
14
14
|
|
|
15
|
-
<br />
|
|
16
15
|
<p>img</p>
|
|
17
|
-
<img src={image} alt="img"
|
|
16
|
+
<img src={image} alt="img" width="64" />
|
|
17
|
+
|
|
18
|
+
<p>svg</p>
|
|
19
|
+
<Svg src={imageSvg} width="64" />
|
|
20
|
+
|
|
21
|
+
<p>svg+css</p>
|
|
22
|
+
<Svg className="svg-css" src={imageSvg} width="64" />
|
|
18
23
|
</section>
|
|
19
24
|
)
|
|
20
25
|
}
|
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
.ImagesPage {
|
|
2
|
+
margin-left: 50px;
|
|
3
|
+
|
|
2
4
|
.css-img {
|
|
3
|
-
background: url("assets/img/image.
|
|
4
|
-
height:
|
|
5
|
+
background: url("assets/img/image.webp") no-repeat;
|
|
6
|
+
height: 64px;
|
|
5
7
|
background-repeat: no-repeat;
|
|
6
8
|
background-size: contain;
|
|
7
9
|
}
|
|
10
|
+
|
|
11
|
+
.svg-css {
|
|
12
|
+
#label {
|
|
13
|
+
animation: svg-css_arrow 2s ease-in-out infinite;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@keyframes svg-css_arrow {
|
|
19
|
+
0%,
|
|
20
|
+
100% {
|
|
21
|
+
transform: translateY(-2px);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
50% {
|
|
25
|
+
transform: translateY(2px);
|
|
26
|
+
}
|
|
8
27
|
}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import i18nFile from 'assets/i18n'
|
|
2
|
+
import { Translate } from 'components'
|
|
3
|
+
import {
|
|
4
|
+
I18n,
|
|
5
|
+
Icon,
|
|
6
|
+
Link,
|
|
7
|
+
PagesFx,
|
|
8
|
+
startViewTransition,
|
|
9
|
+
useFx,
|
|
10
|
+
useQueryString,
|
|
11
|
+
useResize
|
|
12
|
+
} from 'nextia'
|
|
3
13
|
import { lazy, useEffect, useRef, useState } from 'react'
|
|
4
|
-
import
|
|
14
|
+
import iconsFile from 'theme/icons/icons.svg?raw'
|
|
5
15
|
import functions from './functions.js'
|
|
6
16
|
|
|
7
17
|
export default function Pages() {
|
|
8
|
-
const
|
|
9
|
-
const { state, fx } =
|
|
18
|
+
const pages = useFx(functions)
|
|
19
|
+
const { state, fx } = pages
|
|
10
20
|
|
|
11
21
|
const [Page, setPage] = useState()
|
|
12
22
|
const qs = useQueryString()
|
|
@@ -35,11 +45,11 @@ export default function Pages() {
|
|
|
35
45
|
}, [qs.hash])
|
|
36
46
|
|
|
37
47
|
return (
|
|
38
|
-
<
|
|
48
|
+
<PagesFx value={{ context: pages, icons: iconsFile, i18n: i18nFile }}>
|
|
39
49
|
<header style={{ display: 'flex', gap: '20px' }}>
|
|
40
50
|
<Icon id="globe" width="24" />
|
|
41
51
|
|
|
42
|
-
<Translate
|
|
52
|
+
<Translate />
|
|
43
53
|
|
|
44
54
|
<I18n value="page.name" args={['Sinuhe', 'Maceda', 'Bouchan']} />
|
|
45
55
|
|
|
@@ -89,8 +99,8 @@ export default function Pages() {
|
|
|
89
99
|
<Link href="#/translate" className="mr-2">
|
|
90
100
|
/translate
|
|
91
101
|
</Link>
|
|
92
|
-
<Link href="#/
|
|
93
|
-
/
|
|
102
|
+
<Link href="#/view-transition" className="mr-2">
|
|
103
|
+
/view-transition
|
|
94
104
|
</Link>
|
|
95
105
|
<Link href="#/images" className="mr-2">
|
|
96
106
|
/images
|
|
@@ -101,14 +111,14 @@ export default function Pages() {
|
|
|
101
111
|
<Link href="#/resize" className="mr-2">
|
|
102
112
|
/resize
|
|
103
113
|
</Link>
|
|
104
|
-
<Link href="#/
|
|
105
|
-
/
|
|
114
|
+
<Link href="#/dashboard" className="mr-2">
|
|
115
|
+
/not-found
|
|
106
116
|
</Link>
|
|
107
117
|
</aside>
|
|
108
118
|
|
|
109
119
|
<main ref={ref} className="m-2">
|
|
110
120
|
{Page && <Page qs={qs.queryString} resize={resize} />}
|
|
111
121
|
</main>
|
|
112
|
-
</
|
|
122
|
+
</PagesFx>
|
|
113
123
|
)
|
|
114
124
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { css, useFx } from 'nextia'
|
|
1
|
+
import { css, I18n, useFx } from 'nextia'
|
|
2
2
|
import functions from './functions'
|
|
3
3
|
import './style.css'
|
|
4
|
-
import {
|
|
4
|
+
import { Message } from 'components'
|
|
5
5
|
|
|
6
6
|
export default function TranslatePage() {
|
|
7
7
|
const { state, fx } = useFx(functions)
|
|
@@ -3,33 +3,12 @@ import { css, useFx } from 'nextia'
|
|
|
3
3
|
import functions from './functions'
|
|
4
4
|
import './style.css'
|
|
5
5
|
|
|
6
|
-
export default function
|
|
6
|
+
export default function ViewTransitionPage() {
|
|
7
7
|
const { state, fx } = useFx(functions)
|
|
8
8
|
|
|
9
9
|
return (
|
|
10
|
-
<section
|
|
11
|
-
className={css(
|
|
12
|
-
'CounterPage',
|
|
13
|
-
'',
|
|
14
|
-
'class-test',
|
|
15
|
-
'class-test',
|
|
16
|
-
{},
|
|
17
|
-
null,
|
|
18
|
-
true,
|
|
19
|
-
false,
|
|
20
|
-
[],
|
|
21
|
-
{ 'css-false': false },
|
|
22
|
-
undefined,
|
|
23
|
-
{ 'css-true': true },
|
|
24
|
-
{ 'css-true': true }
|
|
25
|
-
)}
|
|
26
|
-
>
|
|
10
|
+
<section className={css('ViewTransitionPage')}>
|
|
27
11
|
Counters
|
|
28
|
-
<div className={css(null)} />
|
|
29
|
-
<div className={css(undefined)} />
|
|
30
|
-
<div className={css([])} />
|
|
31
|
-
<div className={css({})} />
|
|
32
|
-
<div className={css()} />
|
|
33
12
|
<Counter
|
|
34
13
|
value={state.count}
|
|
35
14
|
animation="count"
|
|
@@ -16,12 +16,11 @@
|
|
|
16
16
|
inkscape:export-filename="icons.svg"
|
|
17
17
|
inkscape:export-xdpi="96"
|
|
18
18
|
inkscape:export-ydpi="96"
|
|
19
|
+
aria-hidden="true"
|
|
19
20
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
20
21
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
21
22
|
xmlns="http://www.w3.org/2000/svg"
|
|
22
23
|
xmlns:svg="http://www.w3.org/2000/svg">
|
|
23
|
-
<title
|
|
24
|
-
id="title">icons</title>
|
|
25
24
|
<sodipodi:namedview
|
|
26
25
|
id="namedview"
|
|
27
26
|
pagecolor="#505050"
|
|
@@ -96,10 +95,10 @@
|
|
|
96
95
|
style="display:none"
|
|
97
96
|
sodipodi:insensitive="true">
|
|
98
97
|
<path
|
|
99
|
-
id="
|
|
98
|
+
id="background"
|
|
100
99
|
d="M 12,3.4 C 5.8,7.2 1.8,14 1.8,22 1.8,35 12,45 24,45 36,45 46,35 46,22 46,14 42,7.2 36,3.4" />
|
|
101
100
|
<path
|
|
102
|
-
id="
|
|
101
|
+
id="ring"
|
|
103
102
|
d="M 12,3.4 C 5.8,7.2 1.8,14 1.8,22 1.8,35 12,45 24,45 36,45 46,35 46,22 46,14 42,7.2 36,3.4" />
|
|
104
103
|
<g
|
|
105
104
|
id="arrow">
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#exit.nextia-animate-icon {
|
|
2
|
+
transition: transform 200ms linear;
|
|
3
|
+
|
|
4
|
+
#ring {
|
|
5
|
+
stroke-dasharray: 1, 200;
|
|
6
|
+
stroke-dashoffset: 1;
|
|
7
|
+
transition:
|
|
8
|
+
stroke-dashoffset 400ms linear,
|
|
9
|
+
stroke-dasharray 400ms linear;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#arrow {
|
|
13
|
+
animation: exit_arrow 2s ease-in-out infinite;
|
|
14
|
+
|
|
15
|
+
#right,
|
|
16
|
+
#left {
|
|
17
|
+
display: none;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&:hover {
|
|
22
|
+
transform: rotate(-90deg);
|
|
23
|
+
|
|
24
|
+
#ring {
|
|
25
|
+
stroke-dasharray: 200, 1;
|
|
26
|
+
stroke: var(--primary-color);
|
|
27
|
+
stroke-width: 4px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#arrow {
|
|
31
|
+
transform: translateY(2px);
|
|
32
|
+
stroke: var(--primary-color);
|
|
33
|
+
|
|
34
|
+
line {
|
|
35
|
+
stroke-width: 4px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#right,
|
|
39
|
+
#left {
|
|
40
|
+
display: block;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@keyframes exit_arrow {
|
|
47
|
+
0%,
|
|
48
|
+
100% {
|
|
49
|
+
transform: translateY(2px);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
50% {
|
|
53
|
+
transform: translateY(6px);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
import { flushSync } from 'react-dom'
|
|
2
|
-
import { useQueryString, useResize } from './hooks'
|
|
3
|
-
|
|
4
|
-
const env = import.meta.env
|
|
5
|
-
|
|
6
|
-
async function startViewTransition(fun = () => {}, ref, animation = 'fade') {
|
|
7
|
-
if (!document.startViewTransition || env.PUBLIC_VIEW_TRANSITION === 'false')
|
|
8
|
-
return fun()
|
|
9
|
-
|
|
10
|
-
ref.style.viewTransitionName = animation
|
|
11
|
-
await document.startViewTransition(() => flushSync(fun)).finished
|
|
12
|
-
ref.style.viewTransitionName = ''
|
|
13
|
-
}
|
|
14
|
-
|
|
15
1
|
function sum(a, b) {
|
|
16
2
|
return a + b
|
|
17
3
|
}
|
|
18
4
|
|
|
19
|
-
export {
|
|
5
|
+
export { sum }
|
|
Binary file
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import i18nFile from 'assets/i18n'
|
|
2
|
-
import { useFx } from 'nextia'
|
|
3
|
-
|
|
4
|
-
export default function UiI18n({ value, args = [] }) {
|
|
5
|
-
const { context } = useFx()
|
|
6
|
-
|
|
7
|
-
try {
|
|
8
|
-
let text = value.split('.').reduce((ac, el) => ac[el], i18nFile)
|
|
9
|
-
text = text[i18nFile.locales.indexOf(context.state.i18n.currentLocale)]
|
|
10
|
-
|
|
11
|
-
if (args) {
|
|
12
|
-
text = text.replace(
|
|
13
|
-
/([{}])\\1|[{](.*?)(?:!(.+?))?[}]/g,
|
|
14
|
-
(match, _literal, number) => args[number] || match
|
|
15
|
-
)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return text
|
|
19
|
-
} catch {
|
|
20
|
-
console.error(`Error in [il8n] => ${value}`)
|
|
21
|
-
return value
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react'
|
|
2
|
-
import icons from 'theme/icons/icons.svg?raw'
|
|
3
|
-
|
|
4
|
-
export default function UiIcon({
|
|
5
|
-
id,
|
|
6
|
-
className,
|
|
7
|
-
style,
|
|
8
|
-
width = '48',
|
|
9
|
-
height,
|
|
10
|
-
viewBox = '0 0 48 48',
|
|
11
|
-
fill = 'none',
|
|
12
|
-
color = 'currentColor',
|
|
13
|
-
stroke = 'currentColor',
|
|
14
|
-
strokeWidth = '2',
|
|
15
|
-
strokeLinecap = 'round',
|
|
16
|
-
strokeLinejoin = 'round',
|
|
17
|
-
...props
|
|
18
|
-
}) {
|
|
19
|
-
const ref = useRef()
|
|
20
|
-
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
const svg = new DOMParser()
|
|
23
|
-
.parseFromString(icons, 'image/svg+xml')
|
|
24
|
-
.documentElement.getElementById(id)
|
|
25
|
-
|
|
26
|
-
if (svg) {
|
|
27
|
-
ref.current.innerHTML = svg.innerHTML
|
|
28
|
-
}
|
|
29
|
-
}, [id])
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<svg
|
|
33
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
34
|
-
ref={ref}
|
|
35
|
-
id={id}
|
|
36
|
-
className={className}
|
|
37
|
-
style={style}
|
|
38
|
-
width={width}
|
|
39
|
-
height={height}
|
|
40
|
-
viewBox={viewBox}
|
|
41
|
-
fill={fill}
|
|
42
|
-
color={color}
|
|
43
|
-
stroke={stroke}
|
|
44
|
-
strokeWidth={strokeWidth}
|
|
45
|
-
strokeLinecap={strokeLinecap}
|
|
46
|
-
strokeLinejoin={strokeLinejoin}
|
|
47
|
-
{...props}
|
|
48
|
-
/>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export default function UiLink({ children, href, value = {}, ...props }) {
|
|
2
|
-
href ??= window.location.hash.split('?')[0]
|
|
3
|
-
value = Object.keys(value).length
|
|
4
|
-
? `?${new URLSearchParams(value).toString()}`
|
|
5
|
-
: ''
|
|
6
|
-
|
|
7
|
-
return (
|
|
8
|
-
<a href={href + value} {...props}>
|
|
9
|
-
{children}
|
|
10
|
-
</a>
|
|
11
|
-
)
|
|
12
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react'
|
|
2
|
-
|
|
3
|
-
export default function UiSvg({
|
|
4
|
-
src,
|
|
5
|
-
width = '48',
|
|
6
|
-
height,
|
|
7
|
-
viewBox = '0 0 48 48',
|
|
8
|
-
fill = 'none',
|
|
9
|
-
color = 'currentColor',
|
|
10
|
-
stroke = 'currentColor',
|
|
11
|
-
strokeWidth = '2',
|
|
12
|
-
strokeLinecap = 'round',
|
|
13
|
-
strokeLinejoin = 'round'
|
|
14
|
-
}) {
|
|
15
|
-
const ref = useRef()
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (!ref.current) return
|
|
19
|
-
|
|
20
|
-
const svg = new DOMParser()
|
|
21
|
-
.parseFromString(src, 'image/svg+xml')
|
|
22
|
-
.querySelector('svg')
|
|
23
|
-
|
|
24
|
-
svg.setAttribute('width', width)
|
|
25
|
-
svg.setAttribute('height', height ?? width)
|
|
26
|
-
svg.setAttribute('viewBox', viewBox)
|
|
27
|
-
svg.setAttribute('fill', fill)
|
|
28
|
-
svg.setAttribute('color', color)
|
|
29
|
-
svg.setAttribute('stroke', stroke)
|
|
30
|
-
svg.setAttribute('stroke-width', strokeWidth)
|
|
31
|
-
svg.setAttribute('stroke-linecap', strokeLinecap)
|
|
32
|
-
svg.setAttribute('stroke-linejoin', strokeLinejoin)
|
|
33
|
-
|
|
34
|
-
const shadow =
|
|
35
|
-
ref.current.shadowRoot ?? ref.current.attachShadow({ mode: 'open' })
|
|
36
|
-
shadow.innerHTML = svg.outerHTML
|
|
37
|
-
|
|
38
|
-
ref.current.style.width = `${width}px`
|
|
39
|
-
ref.current.style.height = `${height ?? width}px`
|
|
40
|
-
}, [
|
|
41
|
-
src,
|
|
42
|
-
width,
|
|
43
|
-
height,
|
|
44
|
-
viewBox,
|
|
45
|
-
fill,
|
|
46
|
-
color,
|
|
47
|
-
stroke,
|
|
48
|
-
strokeWidth,
|
|
49
|
-
strokeLinecap,
|
|
50
|
-
strokeLinejoin
|
|
51
|
-
])
|
|
52
|
-
|
|
53
|
-
return <div ref={ref} />
|
|
54
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" width="256" height="256" viewBox="0 0 48 48" fill="none"
|
|
2
|
-
color="currentColor" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
|
3
|
-
|
|
4
|
-
<style type="text/css">
|
|
5
|
-
svg {
|
|
6
|
-
transition: transform 200ms linear;
|
|
7
|
-
|
|
8
|
-
#ani {
|
|
9
|
-
transition:
|
|
10
|
-
stroke-dashoffset 400ms linear,
|
|
11
|
-
stroke-dasharray 400ms linear;
|
|
12
|
-
stroke: var(--primary-color);
|
|
13
|
-
stroke-dasharray: 1, 200;
|
|
14
|
-
stroke-dashoffset: 1;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
#arrow {
|
|
18
|
-
animation: arrow 2s ease-in-out infinite;
|
|
19
|
-
|
|
20
|
-
#right,
|
|
21
|
-
#left {
|
|
22
|
-
display: none;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
svg:hover {
|
|
28
|
-
transform: rotate(-90deg);
|
|
29
|
-
|
|
30
|
-
#ani {
|
|
31
|
-
stroke-dasharray: 200, 1;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
#arrow {
|
|
35
|
-
transform: translateY(2px);
|
|
36
|
-
stroke: var(--primary-color);
|
|
37
|
-
|
|
38
|
-
line {
|
|
39
|
-
stroke-width: 6px;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
#right,
|
|
43
|
-
#left {
|
|
44
|
-
display: block;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
@keyframes arrow {
|
|
50
|
-
|
|
51
|
-
0%,
|
|
52
|
-
100% {
|
|
53
|
-
transform: translateY(2px);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
50% {
|
|
57
|
-
transform: translateY(6px);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
</style>
|
|
61
|
-
|
|
62
|
-
<path id="solid" d="M 12,3.4 C 5.8,7.2 1.8,14 1.8,22 1.8,35 12,45 24,45 36,45 46,35 46,22 46,14 42,7.2 36,3.4" />
|
|
63
|
-
<path id="ani" d="M 12,3.4 C 5.8,7.2 1.8,14 1.8,22 1.8,35 12,45 24,45 36,45 46,35 46,22 46,14 42,7.2 36,3.4" />
|
|
64
|
-
<g id="arrow">
|
|
65
|
-
<line id="left" x1="24" y1="25" x2="10" y2="14" />
|
|
66
|
-
<line id="center" x1="24" y1="1.3" x2="24" y2="25" />
|
|
67
|
-
<line id="right" x1="24" y1="25" x2="35" y2="14" />
|
|
68
|
-
</g>
|
|
69
|
-
</svg>
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
export function useQueryString() {
|
|
4
|
-
const getQueryString = useCallback(
|
|
5
|
-
() => ({
|
|
6
|
-
hash: window.location.hash.split('?')[0],
|
|
7
|
-
queryString: Object.fromEntries(
|
|
8
|
-
new URLSearchParams(window.location.hash.split('?')[1])
|
|
9
|
-
)
|
|
10
|
-
}),
|
|
11
|
-
[]
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
const [queryString, setQueryString] = useState(getQueryString)
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
const handlePopState = () => setQueryString(getQueryString())
|
|
18
|
-
|
|
19
|
-
window.addEventListener('popstate', handlePopState)
|
|
20
|
-
return () => {
|
|
21
|
-
window.removeEventListener('popstate', handlePopState)
|
|
22
|
-
}
|
|
23
|
-
}, [getQueryString])
|
|
24
|
-
|
|
25
|
-
return queryString
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function useResize() {
|
|
29
|
-
const getResize = useCallback(
|
|
30
|
-
() => ({
|
|
31
|
-
width: window.innerWidth,
|
|
32
|
-
height: window.innerHeight
|
|
33
|
-
}),
|
|
34
|
-
[]
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
const [resize, setResize] = useState(getResize)
|
|
38
|
-
|
|
39
|
-
useEffect(() => {
|
|
40
|
-
const handleResize = () => setResize(getResize())
|
|
41
|
-
|
|
42
|
-
window.addEventListener('resize', handleResize)
|
|
43
|
-
return () => {
|
|
44
|
-
window.removeEventListener('resize', handleResize)
|
|
45
|
-
}
|
|
46
|
-
}, [getResize])
|
|
47
|
-
|
|
48
|
-
return resize
|
|
49
|
-
}
|
|
File without changes
|
|
File without changes
|