nextia 7.0.12 → 8.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nextia",
3
3
  "description": "Create fast web applications",
4
- "version": "7.0.12",
4
+ "version": "8.0.1",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "engines": {
@@ -42,8 +42,8 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@vitejs/plugin-react": "6.0.1",
45
- "@vitest/coverage-v8": "4.1.3",
46
- "jsdom": "29.0.2",
47
- "vitest": "4.1.3"
45
+ "@vitest/coverage-v8": "4.1.5",
46
+ "jsdom": "29.1.0",
47
+ "vitest": "4.1.5"
48
48
  }
49
49
  }
package/src/bin.js CHANGED
@@ -21,7 +21,7 @@ function toPascalCase(str) {
21
21
  }
22
22
 
23
23
  async function createPage(name) {
24
- const dirName = `./src/pages/${name}`
24
+ const dirName = `./src/app/${name}`
25
25
  const pageName = `${toPascalCase(name)}Page`
26
26
 
27
27
  try {
@@ -30,15 +30,13 @@ async function createPage(name) {
30
30
  // index.jsx
31
31
  writeFile(
32
32
  `${dirName}/index.jsx`,
33
- `import { css, useFx } from 'nextia'
34
- import functions from './functions'
35
- import './style.css'
33
+ `import useFunctions from './functions'
36
34
 
37
- export default function ${pageName} () {
38
- const { state, fx } = useFx(functions)
35
+ export default function ${pageName}() {
36
+ const { state, fx } = useFunctions()
39
37
 
40
38
  return (
41
- <section className={css('${pageName}', '')}>
39
+ <section>
42
40
  ${pageName}
43
41
  </section>
44
42
  )
@@ -46,20 +44,18 @@ export default function ${pageName} () {
46
44
  `
47
45
  )
48
46
 
49
- // style.sss
50
- writeFile(
51
- `${dirName}/style.css`,
52
- `.${pageName} {
53
- }
54
- `
55
- )
56
-
57
47
  // function.js
58
48
  writeFile(
59
49
  `${dirName}/functions.js`,
60
- `const initialState = {}
50
+ `import { useFx } from 'nextia'
51
+
52
+ export default () => {
53
+ const initialState = {}
61
54
 
62
- export default { initialState }
55
+ return useFx({
56
+ initialState
57
+ })
58
+ }
63
59
  `
64
60
  )
65
61
  console.info(`✔ Page "${pageName}" created at ${dirName}`)
@@ -81,7 +77,7 @@ async function createComponent(name) {
81
77
  `import { css } from 'nextia'
82
78
  import './style.css'
83
79
 
84
- export default function ${componentName} ({ className, style }) {
80
+ export default function ${componentName}({ className, style }) {
85
81
  return (
86
82
  <article className={css('${componentName}', className)} style={style}>
87
83
  ${componentName}
@@ -104,54 +100,6 @@ export default function ${componentName} ({ className, style }) {
104
100
  }
105
101
  }
106
102
 
107
- async function createComponentFx(name) {
108
- const dirName = `./src/components/${name}`
109
- const componentFxName = toPascalCase(name)
110
-
111
- try {
112
- await mkdir(dirName)
113
-
114
- // index.jsx
115
- writeFile(
116
- `${dirName}/index.jsx`,
117
- `import { css, useFx } from 'nextia'
118
- import functions from './functions'
119
- import './style.css'
120
-
121
- export default function ${componentFxName} ({ className, style }) {
122
- const { state, fx } = useFx(functions)
123
-
124
- return (
125
- <article className={css('${componentFxName}', className, '')} style={style}>
126
- ${componentFxName}
127
- </article>
128
- )
129
- }
130
- `
131
- )
132
-
133
- // style.css
134
- writeFile(
135
- `${dirName}/style.css`,
136
- `.${componentFxName} {
137
- }
138
- `
139
- )
140
-
141
- // function.js
142
- writeFile(
143
- `${dirName}/functions.js`,
144
- `const initialState = {}
145
-
146
- export default { initialState }
147
- `
148
- )
149
- console.info(`✔ ComponentFx "${name}" created at ${dirName}`)
150
- } catch (err) {
151
- console.error(`Failed to create component:fx: ${err.message}`)
152
- }
153
- }
154
-
155
103
  async function main() {
156
104
  const ARG1 = process.argv[2]
157
105
  const ARG2 = process.argv[3]
@@ -166,11 +114,6 @@ async function main() {
166
114
  if (ARG2) await createComponent(ARG2)
167
115
  else console.warn('node --run component -- <ComponentName>')
168
116
  break
169
-
170
- case 'component:fx':
171
- if (ARG2) await createComponentFx(ARG2)
172
- else console.warn('node --run component:fx -- <ComponentFxName>')
173
- break
174
117
  }
175
118
  }
176
119
 
package/src/lib/fx.js CHANGED
@@ -7,11 +7,11 @@
7
7
  * https://github.com/sinuhedev/nextia
8
8
  */
9
9
 
10
- import { createContext, use, useReducer } from 'react'
10
+ import { createContext, use, useEffect, useReducer, useState } from 'react'
11
11
  import { env } from './utils.js'
12
12
 
13
13
  const LOGGER = env.DEV && env.PUBLIC_LOGGER !== 'false'
14
- const PagesFx = createContext()
14
+ const Pages = createContext()
15
15
 
16
16
  /**
17
17
  * util
@@ -69,7 +69,7 @@ const reducer = (state, action) => {
69
69
 
70
70
  switch (type) {
71
71
  case 'set':
72
- // Merge only item
72
+ // Merge custom items
73
73
  if (Object.keys(payload).length === 1) {
74
74
  const key = Object.keys(payload)[0]
75
75
  return values(state, key, payload[key])
@@ -93,8 +93,19 @@ const reducer = (state, action) => {
93
93
  : payload.target.value
94
94
  )
95
95
 
96
+ case 'toggle':
97
+ // toggle custom items
98
+ if (payload) {
99
+ const paths = Array.isArray(payload) ? payload : [payload]
100
+
101
+ return paths.reduce((ac, path) => {
102
+ const value = path.split('.').reduce((ac, e) => ac[e], state)
103
+ return values(ac, path, !value)
104
+ }, state)
105
+ } else return state
106
+
96
107
  case 'reset':
97
- // value reset
108
+ // reset custom items
98
109
  if (payload) {
99
110
  const paths = Array.isArray(payload) ? payload : [payload]
100
111
 
@@ -133,7 +144,7 @@ const reducerLogger = (state, action) => {
133
144
  }
134
145
 
135
146
  console.log(
136
- `%c${action.isContext ? 'Pages Context' : 'Page'} %c${action.type}`,
147
+ `%c${action.isContext ? 'Context' : 'Page'} %c${action.type}`,
137
148
  'color: #90b1d1',
138
149
  'color: #6592c8',
139
150
  payloadLog(action),
@@ -144,11 +155,32 @@ const reducerLogger = (state, action) => {
144
155
  }
145
156
 
146
157
  /**
147
- * useFx
158
+ * useCx and useFx
148
159
  */
149
160
 
161
+ function useCx() {
162
+ const pages = use(Pages)
163
+ const [icons, setIcons] = useState()
164
+
165
+ useEffect(() => {
166
+ fetch(pages?.icons)
167
+ .then((r) => r.text())
168
+ .then((text) => {
169
+ setIcons(
170
+ new DOMParser().parseFromString(text, 'image/svg+xml').documentElement
171
+ )
172
+ })
173
+ }, [pages?.icons])
174
+
175
+ return {
176
+ context: pages?.context,
177
+ i18n: pages?.i18n,
178
+ icons
179
+ }
180
+ }
181
+
150
182
  function useFx(functions = { initialState: {} }) {
151
- const pagesFx = use(PagesFx)
183
+ const cx = useCx()
152
184
  const { initialState } = functions
153
185
  const [state, dispatch] = useReducer(
154
186
  LOGGER ? reducerLogger : reducer,
@@ -156,19 +188,23 @@ function useFx(functions = { initialState: {} }) {
156
188
  )
157
189
 
158
190
  // Common actions
159
- const commonActions = ['set', 'show', 'hide', 'change', 'reset'].reduce(
160
- (acc, e) => {
161
- acc[e] = (payload) =>
162
- dispatch({
163
- type: e,
164
- payload,
165
- initialState,
166
- isContext: !pagesFx?.context
167
- })
168
- return acc
169
- },
170
- {}
171
- )
191
+ const commonActions = [
192
+ 'set',
193
+ 'show',
194
+ 'hide',
195
+ 'change',
196
+ 'toggle',
197
+ 'reset'
198
+ ].reduce((acc, e) => {
199
+ acc[e] = (payload) =>
200
+ dispatch({
201
+ type: e,
202
+ payload,
203
+ initialState,
204
+ isContext: !cx?.context
205
+ })
206
+ return acc
207
+ }, {})
172
208
 
173
209
  // Actions
174
210
  const actions = Object.keys(functions).reduce((ac, e) => {
@@ -178,7 +214,7 @@ function useFx(functions = { initialState: {} }) {
178
214
  ...commonActions,
179
215
  state,
180
216
  payload,
181
- context: pagesFx?.context
217
+ context: cx?.context
182
218
  }
183
219
 
184
220
  return functions[e](Object.freeze(actionsProps))
@@ -192,13 +228,10 @@ function useFx(functions = { initialState: {} }) {
192
228
  initialState,
193
229
  state,
194
230
  fx: { ...commonActions, ...actions },
195
- //
196
- context: pagesFx?.context,
197
- iconsFile: pagesFx?.iconsFile,
198
- i18nFile: pagesFx?.i18nFile
231
+ context: cx.context
199
232
  }
200
233
 
201
234
  return Object.freeze(props)
202
235
  }
203
236
 
204
- export { PagesFx, useFx }
237
+ export { Pages, useCx, useFx }
package/src/lib/index.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * https://github.com/sinuhedev/nextia
8
8
  */
9
9
 
10
- import { PagesFx, useFx } from './fx.js'
10
+ import { Pages, useCx, useFx } from './fx.js'
11
11
  import { useQueryString, useResize } from './hooks.js'
12
12
  import { I18n, Icon, Link, Svg } from './ui.js'
13
13
  import { css, env, startViewTransition } from './utils.js'
@@ -18,9 +18,10 @@ export {
18
18
  I18n,
19
19
  Icon,
20
20
  Link,
21
- PagesFx,
21
+ Pages,
22
22
  Svg,
23
23
  startViewTransition,
24
+ useCx,
24
25
  useFx,
25
26
  useQueryString,
26
27
  useResize
package/src/lib/ui.js CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { createElement, useEffect, useRef } from 'react'
11
- import { useFx } from './fx'
11
+ import { useCx } from './fx'
12
12
 
13
13
  function Link({ children, href, value = {}, ...props }) {
14
14
  href ??= window.location.hash.split('?')[0]
@@ -20,18 +20,14 @@ function Link({ children, href, value = {}, ...props }) {
20
20
  }
21
21
 
22
22
  function I18n({ value, args = [] }) {
23
- const { context, i18nFile } = useFx()
23
+ const { context, i18n } = useCx()
24
24
 
25
- if (i18nFile) {
25
+ if (i18n) {
26
26
  try {
27
- let text = value.split('.').reduce((ac, el) => ac[el], i18nFile)
27
+ let text = value.split('.').reduce((ac, el) => ac[el], i18n)
28
28
 
29
29
  text =
30
- text[
31
- i18nFile.locales.indexOf(
32
- context.state?.i18n || i18nFile.defaultLocale
33
- )
34
- ]
30
+ text[i18n.locales.indexOf(context.state?.i18n || i18n.defaultLocale)]
35
31
 
36
32
  if (args) {
37
33
  text = text.replace(
@@ -64,20 +60,14 @@ function Icon({
64
60
  strokeLinejoin = 'round',
65
61
  ...props
66
62
  }) {
67
- const { iconsFile } = useFx()
63
+ const { icons } = useCx()
68
64
  const ref = useRef()
69
65
 
70
66
  useEffect(() => {
71
- if (iconsFile) {
72
- const svg = new DOMParser()
73
- .parseFromString(iconsFile, 'image/svg+xml')
74
- .documentElement.getElementById(id)
75
-
76
- if (svg) {
77
- ref.current.innerHTML = svg.innerHTML
78
- }
67
+ if (icons) {
68
+ ref.current.innerHTML = icons.getElementById(id).innerHTML
79
69
  }
80
- }, [id, iconsFile])
70
+ }, [id, icons])
81
71
 
82
72
  return createElement('svg', {
83
73
  xmlns: 'http://www.w3.org/2000/svg',
@@ -102,17 +92,21 @@ function Svg({ ref, src, width, height, ...props }) {
102
92
  ref ??= useRef()
103
93
 
104
94
  useEffect(() => {
105
- const svg = new DOMParser().parseFromString(
106
- src,
107
- 'image/svg+xml'
108
- ).documentElement
109
-
110
- for (const { name, value } of svg.attributes) {
111
- if (name !== 'width' && name !== 'height')
112
- ref.current.setAttribute(name, value)
113
- }
114
-
115
- ref.current.replaceChildren(...svg.children)
95
+ fetch(src)
96
+ .then((r) => r.text())
97
+ .then((text) => {
98
+ const svg = new DOMParser().parseFromString(
99
+ text,
100
+ 'image/svg+xml'
101
+ ).documentElement
102
+
103
+ for (const { name, value } of svg.attributes) {
104
+ if (name !== 'width' && name !== 'height')
105
+ ref.current.setAttribute(name, value)
106
+ }
107
+
108
+ ref.current.replaceChildren(...svg.children)
109
+ })
116
110
  }, [src, ref])
117
111
 
118
112
  return createElement('svg', {