nitro-web 0.0.11 → 0.0.13

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.
Files changed (143) hide show
  1. package/.eslintrc.json +4 -19
  2. package/_example/.env +1 -1
  3. package/_example/client/config.ts +2 -1
  4. package/_example/client/index.ts +6 -24
  5. package/_example/components/index.tsx +1 -1
  6. package/_example/package.json +1 -1
  7. package/_example/server/config.js +6 -7
  8. package/_example/tailwind.config.js +1 -1
  9. package/_example/tsconfig.json +10 -2
  10. package/_example/types.ts +1 -0
  11. package/client/{app.js → app.tsx} +101 -99
  12. package/client/globals.ts +42 -0
  13. package/client/index.ts +52 -0
  14. package/client/store.ts +31 -0
  15. package/components/auth/auth.api.js +3 -2
  16. package/components/auth/{reset.jsx → reset.tsx} +21 -23
  17. package/components/auth/{signin.jsx → signin.tsx} +14 -16
  18. package/components/auth/{signup.jsx → signup.tsx} +15 -17
  19. package/components/billing/stripe.api.js +2 -1
  20. package/components/dashboard/{dashboard.jsx → dashboard.tsx} +3 -3
  21. package/components/partials/element/{accordion.jsx → accordion.tsx} +21 -13
  22. package/components/partials/element/avatar.tsx +40 -0
  23. package/components/partials/element/{button.jsx → button.tsx} +20 -16
  24. package/components/partials/element/{dropdown.jsx → dropdown.tsx} +32 -30
  25. package/components/partials/element/{github-link.jsx → github-link.tsx} +3 -3
  26. package/components/partials/element/{initials.jsx → initials.tsx} +11 -2
  27. package/components/partials/element/{message.jsx → message.tsx} +22 -23
  28. package/components/partials/element/{modal.jsx → modal.tsx} +4 -3
  29. package/components/partials/element/{sidebar.jsx → sidebar.tsx} +14 -7
  30. package/components/partials/element/{tooltip.jsx → tooltip.tsx} +11 -3
  31. package/components/partials/element/{topbar.jsx → topbar.tsx} +9 -7
  32. package/components/partials/form/{checkbox.jsx → checkbox.tsx} +13 -13
  33. package/components/partials/form/drop-handler.tsx +68 -0
  34. package/components/partials/form/{drop.jsx → drop.tsx} +51 -33
  35. package/components/partials/form/form-error.tsx +27 -0
  36. package/components/partials/form/{input-color.jsx → input-color.tsx} +27 -15
  37. package/components/partials/form/{input-currency.jsx → input-currency.tsx} +37 -32
  38. package/components/partials/form/{input-date.jsx → input-date.tsx} +4 -3
  39. package/components/partials/form/{input.jsx → input.tsx} +35 -19
  40. package/components/partials/form/{location.jsx → location.tsx} +21 -8
  41. package/components/partials/form/{select.jsx → select.tsx} +142 -143
  42. package/components/partials/form/{toggle.jsx → toggle.tsx} +10 -2
  43. package/components/partials/{is-first-render.js → is-first-render.ts} +1 -2
  44. package/components/partials/layout/layout1.tsx +29 -0
  45. package/components/partials/layout/{layout2.jsx → layout2.tsx} +3 -3
  46. package/components/partials/{styleguide.jsx → styleguide.tsx} +16 -19
  47. package/components/settings/{settings-account.jsx → settings-account.tsx} +9 -13
  48. package/components/settings/{settings-business.jsx → settings-business.tsx} +7 -8
  49. package/components/settings/{settings-team--member.jsx → settings-team--member.tsx} +4 -11
  50. package/components/settings/{settings-team.jsx → settings-team.tsx} +4 -8
  51. package/components/settings/settings.api.js +1 -0
  52. package/package.json +17 -31
  53. package/readme.md +1 -1
  54. package/server/email/index.js +2 -1
  55. package/server/index.js +1 -0
  56. package/server/models/company.js +2 -1
  57. package/server/models/user.js +2 -1
  58. package/server/router.js +8 -2
  59. package/tsconfig.json +36 -0
  60. package/types/required-globals.d.ts +39 -0
  61. package/types/util.d.ts +12 -2
  62. package/types/util.d.ts.map +1 -1
  63. package/types.ts +43 -0
  64. package/util.js +14 -34
  65. package/webpack.config.js +23 -4
  66. package/_example/types/index.d.ts +0 -13
  67. package/_example/types/twin.d.ts +0 -19
  68. package/client/index.js +0 -44
  69. package/components/partials/element/avatar.jsx +0 -31
  70. package/components/partials/form/drop-handler.jsx +0 -62
  71. package/components/partials/form/form-error.jsx +0 -21
  72. package/components/partials/layout/layout1.jsx +0 -38
  73. package/types/client/app.d.ts +0 -2
  74. package/types/client/app.d.ts.map +0 -1
  75. package/types/client/index.d.ts +0 -29
  76. package/types/client/index.d.ts.map +0 -1
  77. package/types/components/auth/reset.d.ts +0 -3
  78. package/types/components/auth/reset.d.ts.map +0 -1
  79. package/types/components/auth/signin.d.ts +0 -4
  80. package/types/components/auth/signin.d.ts.map +0 -1
  81. package/types/components/auth/signup.d.ts +0 -4
  82. package/types/components/auth/signup.d.ts.map +0 -1
  83. package/types/components/dashboard/dashboard.d.ts +0 -4
  84. package/types/components/dashboard/dashboard.d.ts.map +0 -1
  85. package/types/components/partials/element/accordion.d.ts +0 -7
  86. package/types/components/partials/element/accordion.d.ts.map +0 -1
  87. package/types/components/partials/element/avatar.d.ts +0 -8
  88. package/types/components/partials/element/avatar.d.ts.map +0 -1
  89. package/types/components/partials/element/button.d.ts +0 -11
  90. package/types/components/partials/element/button.d.ts.map +0 -1
  91. package/types/components/partials/element/dropdown.d.ts +0 -17
  92. package/types/components/partials/element/dropdown.d.ts.map +0 -1
  93. package/types/components/partials/element/initials.d.ts +0 -9
  94. package/types/components/partials/element/initials.d.ts.map +0 -1
  95. package/types/components/partials/element/message.d.ts +0 -2
  96. package/types/components/partials/element/message.d.ts.map +0 -1
  97. package/types/components/partials/element/modal.d.ts +0 -10
  98. package/types/components/partials/element/modal.d.ts.map +0 -1
  99. package/types/components/partials/element/sidebar.d.ts +0 -6
  100. package/types/components/partials/element/sidebar.d.ts.map +0 -1
  101. package/types/components/partials/element/tooltip.d.ts +0 -8
  102. package/types/components/partials/element/tooltip.d.ts.map +0 -1
  103. package/types/components/partials/element/topbar.d.ts +0 -8
  104. package/types/components/partials/element/topbar.d.ts.map +0 -1
  105. package/types/components/partials/form/checkbox.d.ts +0 -14
  106. package/types/components/partials/form/checkbox.d.ts.map +0 -1
  107. package/types/components/partials/form/drop-handler.d.ts +0 -6
  108. package/types/components/partials/form/drop-handler.d.ts.map +0 -1
  109. package/types/components/partials/form/drop.d.ts +0 -11
  110. package/types/components/partials/form/drop.d.ts.map +0 -1
  111. package/types/components/partials/form/form-error.d.ts +0 -6
  112. package/types/components/partials/form/form-error.d.ts.map +0 -1
  113. package/types/components/partials/form/input-color.d.ts +0 -10
  114. package/types/components/partials/form/input-color.d.ts.map +0 -1
  115. package/types/components/partials/form/input-currency.d.ts +0 -10
  116. package/types/components/partials/form/input-currency.d.ts.map +0 -1
  117. package/types/components/partials/form/input.d.ts +0 -9
  118. package/types/components/partials/form/input.d.ts.map +0 -1
  119. package/types/components/partials/form/location.d.ts +0 -12
  120. package/types/components/partials/form/location.d.ts.map +0 -1
  121. package/types/components/partials/form/select.d.ts +0 -27
  122. package/types/components/partials/form/select.d.ts.map +0 -1
  123. package/types/components/partials/form/toggle.d.ts +0 -9
  124. package/types/components/partials/form/toggle.d.ts.map +0 -1
  125. package/types/components/partials/is-first-render.d.ts +0 -2
  126. package/types/components/partials/is-first-render.d.ts.map +0 -1
  127. package/types/components/partials/layout/layout1.d.ts +0 -13
  128. package/types/components/partials/layout/layout1.d.ts.map +0 -1
  129. package/types/components/partials/layout/layout2.d.ts +0 -4
  130. package/types/components/partials/layout/layout2.d.ts.map +0 -1
  131. package/types/components/partials/not-found.d.ts +0 -2
  132. package/types/components/partials/not-found.d.ts.map +0 -1
  133. package/types/components/partials/styleguide.d.ts +0 -4
  134. package/types/components/partials/styleguide.d.ts.map +0 -1
  135. package/types/components/settings/settings-account.d.ts +0 -6
  136. package/types/components/settings/settings-account.d.ts.map +0 -1
  137. package/types/components/settings/settings-business.d.ts +0 -4
  138. package/types/components/settings/settings-business.d.ts.map +0 -1
  139. package/types/components/settings/settings-team--member.d.ts +0 -5
  140. package/types/components/settings/settings-team--member.d.ts.map +0 -1
  141. package/types/components/settings/settings-team.d.ts +0 -4
  142. package/types/components/settings/settings-team.d.ts.map +0 -1
  143. /package/components/partials/{not-found.jsx → not-found.tsx} +0 -0
package/.eslintrc.json CHANGED
@@ -11,24 +11,7 @@
11
11
  "node": true
12
12
  },
13
13
  "globals": {
14
- // Project globals
15
- "CONFIG": true,
16
- "cssVar": true,
17
- "onChange": true,
18
- "sharedStore": true,
19
- "sharedStoreCache": true,
20
- // Dependency globals
21
- "Link": true,
22
- "forwardRef": true,
23
- "useCallback": true,
24
- "useEffect": true,
25
- "useLocation": true,
26
- "useNavigate": true,
27
- "useImperativeHandle": true,
28
- "useMemo": true,
29
- "useParams": true,
30
- "useState": true,
31
- "useRef": true
14
+ "Link": true
32
15
  },
33
16
  "parser": "@typescript-eslint/parser",
34
17
  "parserOptions": {
@@ -58,10 +41,12 @@
58
41
  "allowObject": true
59
42
  }],
60
43
  "max-len": ["error", { "code": 140, "ignorePattern": "^\\s*(<(rect|path|line)\\s|class=|className=|'|`)" }],
44
+ "no-var": "off",
61
45
  "no-prototype-builtins": "off",
62
46
  "@typescript-eslint/no-this-alias": "off",
63
-
47
+ "@typescript-eslint/ban-ts-comment": "off",
64
48
  "no-unused-vars": "off",
49
+ "@typescript-eslint/no-unsafe-function-type": "off",
65
50
  "@typescript-eslint/no-unused-vars": ["error", {
66
51
  "argsIgnorePattern": "^_",
67
52
  "varsIgnorePattern": "^_",
package/_example/.env CHANGED
@@ -11,6 +11,6 @@ mailgunDomain=mg.nitro.com
11
11
  mailgunKey=
12
12
  masterPassword=1234
13
13
  mongoUrl=localhost:27017/nitro-dev
14
+ placeholderEmail=bruce@wayneenterprises.com
14
15
  stripePublishableKey=
15
16
  stripeSecretKey=
16
- testEmail=bruce@wayneenterprises.com
@@ -1 +1,2 @@
1
- export default { ...CONFIG } // Environment variables from webpack
1
+ export type Config = import('nitro-web/types').Config & {}
2
+ export default { ...INJECTED } as Config
@@ -1,29 +1,11 @@
1
- import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
2
- import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
3
- import { setupApp, onChange } from 'nitro-web'
4
1
 
5
- import config from './config'
2
+ import 'nitro-web/client/globals'
3
+ import { setupApp } from 'nitro-web'
4
+
6
5
  import './css/index.css'
6
+ import config from './config'
7
7
  import { Layout1, Layout2 } from '../components/partials/layouts'
8
8
 
9
- Object.assign(window, {
10
- // application globals
11
- onChange: onChange,
12
- sharedStore: undefined, // defined in setupApp
13
- sharedStoreCache: undefined, // defined in setupApp
14
-
15
- // dependency globals
16
- Link: Link,
17
- useCallback: useCallback,
18
- useEffect: useEffect,
19
- useLocation: useLocation,
20
- useNavigate: useNavigate,
21
- useImperativeHandle: useImperativeHandle,
22
- useMemo: useMemo,
23
- useParams: useParams,
24
- useRef: useRef,
25
- useState: useState,
26
- })
27
-
28
- // Fetch state and start app
9
+ // Auto-import page components, initilize app, and run config.beforeApp
29
10
  setupApp(config, [Layout1, Layout2])
11
+
@@ -3,6 +3,7 @@
3
3
  * will be automatically imported and setup by the client router, we're just listing them all here for the sake of example.
4
4
  */
5
5
  import { css, theme } from 'twin.macro'
6
+ import config from '../client/config'
6
7
  import {
7
8
  Signin,
8
9
  Signup,
@@ -16,7 +17,6 @@ import {
16
17
  // SettingsTeam,
17
18
  } from 'nitro-web'
18
19
 
19
- import config from '../client/config'
20
20
 
21
21
  // Signin page (can be saved onto a seperate .jsx/.tsx file under the components folder)
22
22
  export const SigninPage = () => <Signin config={config} />
@@ -9,6 +9,7 @@
9
9
  "build": "NODE_ENV=production webpack --target=web",
10
10
  "dev": "clear && npm run dev:lint --silent & npm run dev:server --silent & npm run dev:client --silent",
11
11
  "dev:client": "webpack serve --progress --config ./webpack.config.js",
12
+ "dev:client-only": "isStatic=true npm run dev:client",
12
13
  "dev:server": "nodemon ./server -q -w ./server/ -w ./components/ -e js",
13
14
  "dev:lint": "eslint ./components ./server/",
14
15
  "minor": "standard-version --release-as minor && git push staging",
@@ -48,7 +49,6 @@
48
49
  "@typescript-eslint/eslint-plugin": "^8.18.1",
49
50
  "@typescript-eslint/parser": "^8.18.1",
50
51
  "autoprefixer": "^9.8.8",
51
- "babel-eslint": "^10.0.3",
52
52
  "babel-loader": "^8.0.6",
53
53
  "babel-plugin-macros": "^3.1.0",
54
54
  "babel-plugin-react-html-attrs": "^2.1.0",
@@ -5,17 +5,21 @@ const env = process.env.env || (process.env.NODE_ENV !== 'production' ? 'develop
5
5
  const pwd = process.env.PWD + '/'
6
6
 
7
7
  export default {
8
- inject: 'awsUrl clientUrl currencies countries env googleMapsApiKey stripePublishableKey testEmail',
8
+ inject: 'awsUrl clientUrl currencies countries env googleMapsApiKey isStatic placeholderEmail stripePublishableKey version',
9
9
 
10
10
  clientUrl: process.env.originUrl || 'http://localhost:3000',
11
11
  emailFrom: process.env.emailFrom,
12
12
  emailReplyTo: process.env.emailReplyTo,
13
13
  emailTestMode: process.env.emailTestMode,
14
14
  env: env,
15
+ homepage: _require(pwd + 'package.json').homepage,
16
+ isStatic: process.env.isStatic,
15
17
  masterPassword: process.env.masterPassword,
16
18
  mongoUrl: process.env.mongoUrl,
19
+ placeholderEmail: process.env.placeholderEmail,
17
20
  publicPath: process.env.publicPath,
18
- testEmail: process.env.testEmail,
21
+ pwd: pwd, // change to rootDir
22
+ version: _require(pwd + 'package.json').version,
19
23
 
20
24
  awsUrl: process.env.awsUrl,
21
25
  googleMapsApiKey: process.env.googleMapsApiKey,
@@ -120,9 +124,4 @@ export default {
120
124
  else res.unauthorized('Please sign in first.')
121
125
  },
122
126
  },
123
-
124
- pwd: pwd,
125
- homepage: _require(pwd + 'package.json').homepage,
126
- repository: _require(pwd + 'package.json').repository,
127
- version: _require(pwd + 'package.json').version,
128
127
  }
@@ -11,7 +11,7 @@ export default {
11
11
  relative: true,
12
12
  files: [
13
13
  './components/**/*.{ts,tsx}',
14
- path.join(nitroDir, '../components/**/*.{js,jsx}'),
14
+ path.join(nitroDir, '../components/**/*.{ts,tsx}'),
15
15
  ],
16
16
  },
17
17
  experimental: {
@@ -17,11 +17,19 @@
17
17
  "noImplicitThis": true,
18
18
  "noStrictGenericChecks": false,
19
19
  "outDir": "./dist",
20
+ "paths": {
21
+ "types": ["./types.ts"]
22
+ },
20
23
  "resolveJsonModule": true,
21
24
  "skipLibCheck": true,
22
25
  "sourceMap": true,
23
26
  "strict": true,
24
- "target": "es6",
27
+ "target": "es6"
25
28
  },
26
- "include": ["client", "components", "types"],
29
+ "include": [
30
+ "client",
31
+ "components/**/*.tsx",
32
+ "components/**/*.ts",
33
+ "types"
34
+ ]
27
35
  }
@@ -0,0 +1 @@
1
+ export type { Config, Errors, User } from 'nitro-web/types'
@@ -1,28 +1,47 @@
1
1
  import { createBrowserRouter, createHashRouter, redirect, useParams, RouterProvider } from 'react-router-dom'
2
+ import { Fragment, ReactNode } from 'react'
2
3
  import ReactDOM from 'react-dom/client'
3
- import { createContainer } from 'react-tracked'
4
- import { axios, camelCase, pick, toArray, setTimeoutPromise } from '../util.js'
4
+ import { AxiosRequestConfig } from '@hokify/axios'
5
+ import { beforeCreate, Provider, exposedData } from './store'
6
+ import { axios, camelCase, pick, toArray, setTimeoutPromise } from 'nitro-web/util'
7
+ import { Config, Store } from 'types'
5
8
 
6
- export async function setupApp(config, layouts) {
9
+ type Settings = {
10
+ afterApp?: () => void
11
+ middleware: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
12
+ beforeApp: (config: Config) => Promise<object>
13
+ beforeStoreUpdate: (prevStore: Store | null, newData: Store) => Store
14
+ isStatic?: boolean
15
+ layouts: React.FC[]
16
+ }
17
+
18
+ type Route = {
19
+ component: React.FC<{ route?: Route; params?: object; location?: object }>
20
+ meta?: { title?: string }
21
+ middleware: string[]
22
+ name: string
23
+ path: string
24
+ redirect?: string
25
+ }
26
+
27
+ export async function setupApp(config: Config, layouts: React.FC[]) {
7
28
  // Fetch state and init app
8
- const settings = {
29
+ const settings: Settings = {
9
30
  middleware: Object.assign(defaultMiddleware, config.middleware || {}),
10
- beforeApp: (config.beforeApp || defaultBeforeApp).bind(null, config),
11
- beforeStoreUpdate: config.beforeStoreUpdate || defaultBeforeStoreUpdate,
12
- isDemo: config.isDemo,
31
+ beforeApp: config.beforeApp || beforeApp,
32
+ beforeStoreUpdate: config.beforeStoreUpdate || beforeStoreUpdate,
33
+ isStatic: config.isStatic,
13
34
  layouts: layouts,
14
35
  }
15
36
  if (!settings.layouts) throw new Error('layouts are required')
16
- const data = (await settings.beforeApp()) || {}
17
- window.sharedStore = createStore(data, settings.beforeStoreUpdate)
37
+ const initData = (await settings.beforeApp(config)) || {}
38
+ beforeCreate(initData, settings.beforeStoreUpdate)
18
39
 
19
- const root = ReactDOM.createRoot(document.getElementById('app'))
40
+ const root = ReactDOM.createRoot(document.getElementById('app') as HTMLElement)
20
41
  root.render(<App settings={settings} />)
21
42
  }
22
43
 
23
- function App({ settings }) {
24
- // Should only render once
25
- const Provider = window.sharedStore.Provider
44
+ function App({ settings }: { settings: Settings }): ReactNode {
26
45
  // const themeNormalised = theme
27
46
  const router = getRouter(settings)
28
47
  // const theme = pick(themeNormalised, []) // e.g. 'topPanelHeight'
@@ -39,67 +58,42 @@ function App({ settings }) {
39
58
  * 'idle' event is fired, after route/layout change.
40
59
  */
41
60
  const sessionHistory = JSON.parse(sessionStorage.getItem('sessionHistory') || '{}')
42
- router.subscribe((state, a, b) => { // eslint-disable-line
61
+ router?.subscribe((state) => {
43
62
  // @param state.historyAction - 'POP', 'PUSH', 'REPLACE'
44
63
  // @param state.navigation.state - 'loading', 'idle' (after navigation)
45
64
  if (state.navigation.state === 'idle') return
46
- sessionHistory[state.location.key] = scrollbarElement().scrollTop // 100k pages == 1.6MB (we're good, lol)
65
+ sessionHistory[state.location.key] = scrollbarElement()?.scrollTop // 100k pages == 1.6MB (we're good, lol)
47
66
  sessionStorage.setItem('sessionHistory', JSON.stringify(sessionHistory))
48
67
  })
49
- }, [])
68
+ }, [!!router])
50
69
 
51
70
  return (
52
71
  <Provider>
53
72
  {/* <ThemeProvider theme={themeNormalised}> */}
54
- <RouterProvider router={router} />
73
+ { router && <RouterProvider router={router} /> }
55
74
  <AfterApp settings={settings} />
56
75
  {/* </ThemeProvider> */}
57
76
  </Provider>
58
77
  )
59
78
  }
60
79
 
61
- function AfterApp(settings) {
80
+ function AfterApp({ settings }: { settings: Settings }) {
62
81
  if (settings.afterApp) settings.afterApp()
63
82
  return (null)
64
83
  }
65
84
 
66
- function createStore(newStoreData, beforeStoreUpdate) {
67
- // @param {object} newStoreData - normally provided from a /login or /state request data
68
- return createContainer(() => {
69
- // Normally only setup once
70
- // Fast reload - rebuilds <Root/>, and then createContainer() without providing the initialState
71
- // Hot reload - rebuilds the whole app
72
- let [store, setStore] = useState(() => beforeStoreUpdate(null, newStoreData || window.sharedStoreCache || {})) // runs only once
73
-
74
- // Wrap the setState function to always run beforeStoreUpdate
75
- const wrappedSetStore = (updater) => {
76
- if (typeof updater === 'function') {
77
- setStore((prevStore) => {
78
- const nextState = updater(prevStore)
79
- return beforeStoreUpdate(prevStore, nextState)
80
- })
81
- } else {
82
- setStore((prevStore) => beforeStoreUpdate(prevStore, updater))
83
- }
84
- }
85
-
86
- window.sharedStoreCache = store
87
- newStoreData = null
88
- return [store, wrappedSetStore]
89
- })
90
- }
91
-
92
- function getRouter(settings) {
85
+ function getRouter(settings: Settings) {
93
86
  /**
94
87
  * Get all routes from components folder
95
88
  * @return {array} routes for for creatingBrowserRouter
89
+ *
90
+ * During build time, webpack returns `require` function, which also contains all matching files
91
+ * https://webpack.js.org/guides/dependency-management/#requirecontext
96
92
  */
97
- // During build time, webpack returns `require` function, which also contains all matching files
98
- // https://webpack.js.org/guides/dependency-management/#requirecontext
99
-
100
93
  let requireContext
101
94
  try {
102
- requireContext = import.meta.webpackContext('componentsDir', { // components is the alias in webpack.config.js
95
+ // @ts-expect-error
96
+ requireContext = import.meta.webpackContext('componentsDir', {
103
97
  recursive: true,
104
98
  regExp: /(?<!\.api)\.(j|t)sx$/,
105
99
  })
@@ -110,7 +104,7 @@ function getRouter(settings) {
110
104
  }
111
105
  // Loop files
112
106
  // const components = {}
113
- const layouts = []
107
+ const layouts: Route[][] = []
114
108
 
115
109
  for (const filename of requireContext.keys()) {
116
110
  const file = requireContext(filename) // require
@@ -120,7 +114,7 @@ function getRouter(settings) {
120
114
  const isReactFnComponentOrFnRef = typeof file[key] === 'function' || !!file[key]?.render
121
115
  if (!file.hasOwnProperty(key) || key.match(/route/i) || !isReactFnComponentOrFnRef) continue
122
116
  const componentRoutes = toArray(file[key].route || file.route || file.Route)
123
- const componentName = key || camelCase(key.replace(/^.*[\\\/]|\.jsx$/g, '')) // eslint-disable-line
117
+ const componentName = key || camelCase(key.replace(/^.*[\\\/]|\.jsx$/g, '')) as string // eslint-disable-line
124
118
  // console.log(file)
125
119
  // Todo: need to retrieve the original function name for default exports during minification.
126
120
  // console.log(1, file[key].name, key, componentName, file[key])
@@ -136,12 +130,14 @@ function getRouter(settings) {
136
130
 
137
131
  for (const routePath of routePaths) {
138
132
  const layoutNum = (route.meta?.layout || 1) - 1
133
+
139
134
  // get the routes middleware
140
135
  const middleware = toArray(route[routePath]).filter(o => {
141
136
  if (o === true) return // ignore true
142
137
  else if (settings.middleware[o]) return true
143
138
  else console.error(`No middleware named '${o}' defined under config.middleware, skipping..`)
144
139
  })
140
+
145
141
  // Push route to layout
146
142
  if (!layouts[layoutNum]) layouts[layoutNum] = []
147
143
  layouts[layoutNum].push({
@@ -161,26 +157,30 @@ function getRouter(settings) {
161
157
  }
162
158
 
163
159
  // Generate createBrowserRouter array
164
- let nonce
165
- const createRouterArray = layouts.map((routes, i) => {
166
- if (!routes) return
160
+ let nonce: boolean
161
+ const createRouterArray = layouts.map((layout, i) => {
162
+ if (!layout) return
167
163
  const Layout = settings.layouts[i]
168
164
  if (!Layout) throw new Error(`Please pass Layout${i+1 || ''} to appSetup()`)
169
165
  return {
170
166
  // path: '/', // (disbaled: page component with path:'/' doesnt work)
171
- element: <>
172
- <Layout />
173
- <RestoreScroll />
174
- </>,
175
- children: routes.map((route) => {
167
+ element: (
168
+ <Fragment>
169
+ <Layout />
170
+ <RestoreScroll />
171
+ </Fragment>
172
+ ),
173
+ children: layout.map((route) => {
176
174
  return {
177
- element: <RouteComponent route={route} />,
175
+ element: (
176
+ <RouteComponent route={route} />
177
+ ),
178
178
  path: route.path,
179
- loader: async (/*{ request }*/) => {
180
- // wait for sharedStore/sharedStoreCache to be setup
179
+ loader: async () => { // request
180
+ // wait for container/exposedData to be setup
181
181
  if (!nonce) nonce = true && await setTimeoutPromise(() => {}, 0) // eslint-disable-line
182
- for (let key of route.middleware) {
183
- let error = settings.middleware[key](route, window.sharedStoreCache||{})
182
+ for (const key of route.middleware) {
183
+ const error = settings.middleware[key](route, exposedData || {})
184
184
  if (error && error.redirect) {
185
185
  return redirect(error.redirect)
186
186
  }
@@ -190,13 +190,13 @@ function getRouter(settings) {
190
190
  }
191
191
  }),
192
192
  }
193
- }).filter(o => o)
193
+ }).filter(o => !!o)
194
194
 
195
195
  // console.log(createRouterArray) ////////////////////////
196
196
 
197
197
  // Create Browser/HashRouter (if there is no error above otheriwse router will be empty)
198
198
  if (createRouterArray.length) {
199
- if (settings.isDemo) return createHashRouter(createRouterArray) // Use HashRouter for demo
199
+ if (settings.isStatic) return createHashRouter(createRouterArray) // Use HashRouter for demo
200
200
  else return createBrowserRouter(createRouterArray) // Use BrowserRouter otherwise
201
201
  }
202
202
  }
@@ -210,8 +210,8 @@ function RestoreScroll() {
210
210
  const previousScrollY = sessionHistory[location.key] || 0
211
211
  // console.log(3, previousScrollY, window.history.state)
212
212
  const attemptScroll = (attempts = 0) => {
213
- if (scrollbarElement().scrollTop !== previousScrollY && attempts < 100) {
214
- scrollbarElement().scrollTo(0, previousScrollY)
213
+ if (scrollbarElement()?.scrollTop !== previousScrollY && attempts < 100) {
214
+ scrollbarElement()?.scrollTo(0, previousScrollY)
215
215
  setTimeout(() => attemptScroll(attempts + 1), 10)
216
216
  }
217
217
  }
@@ -221,11 +221,14 @@ function RestoreScroll() {
221
221
  return (null)
222
222
  }
223
223
 
224
- function RouteComponent({ route }) {
224
+ function RouteComponent({ route }: { route: Route }) {
225
+ const Component = route.component
225
226
  const params = useParams()
226
227
  const location = useLocation()
227
- document.title = route.meta.title
228
- return <route.component route={route} params={params} location={location} />
228
+ document.title = route.meta?.title || ''
229
+ return (
230
+ <Component route={route} params={params} location={location} />
231
+ )
229
232
  }
230
233
 
231
234
  function scrollbarElement() {
@@ -236,45 +239,44 @@ function scrollbarElement() {
236
239
 
237
240
  // ---- Overridable defaults ------------
238
241
 
239
- async function defaultBeforeApp(config) {
242
+ async function beforeApp(config: Config) {
240
243
  /**
241
244
  * Gets called once before React is initialised
242
- * @return {promise} - newStoreData which is used for window.store, later merged with the config.store() defaults
245
+ * @return {promise} - newStoreData which is used for sharedStore, later merged with the config.store() defaults
243
246
  */
244
247
  let apiAvailable
245
- let newStoreData
248
+ let stateData
246
249
  try {
247
250
  // Unload prehot data
248
- if (window.prehot) {
249
- window.sharedStoreCache = window.prehot.sharedStoreCache
250
- delete window.prehot
251
- }
252
- if (!window.sharedStoreCache && !config.isDemo) {
253
- newStoreData = (await axios().get('/api/state', { 'axios-retry': { retries: 3 }, timeout: 4000 })).data
251
+ // if (window.prehot) {
252
+ // sharedStoreCache = window.prehot.sharedStoreCache
253
+ // delete window.prehot
254
+ // }
255
+ if (!exposedData && !config.isStatic) {
256
+ stateData = (await axios().get('/api/state', { 'axios-retry': { retries: 3 }, timeout: 4000 } as AxiosRequestConfig)).data
254
257
  apiAvailable = true
255
258
  }
256
259
  } catch (err) {
257
260
  console.error('We had trouble connecting to the API, please refresh')
258
261
  console.log(err)
259
262
  }
260
- return { ...(newStoreData || window.sharedStoreCache), apiAvailable }
263
+ return { ...(stateData || exposedData), apiAvailable }
261
264
  }
262
265
 
263
- function defaultBeforeStoreUpdate(store, newStoreData) {
266
+ function beforeStoreUpdate(prevStore: Store | null, newData: Store) {
264
267
  /**
265
268
  * Get store object (called on signup/signin/signout/state)
266
269
  * @param {object} store - existing store
267
270
  * @param {object} <newStoreData> - pass to override store with /login or /state request data
268
271
  * @return {object} store
269
272
  */
270
- if (!newStoreData) return newStoreData
271
- store = {
272
- ...(store || {
273
- message: null,
274
- stripeProducts: [],
275
- user: null, // defined if user is signed in
273
+ if (!newData) return newData
274
+ const store = {
275
+ ...(prevStore || {
276
+ message: undefined,
277
+ user: undefined, // defined if user is signed in
276
278
  }),
277
- ...(newStoreData || {}),
279
+ ...(newData || {}),
278
280
  }
279
281
 
280
282
  // Used to verify if the current cookie belongs to this user
@@ -285,20 +287,20 @@ function defaultBeforeStoreUpdate(store, newStoreData) {
285
287
 
286
288
  const defaultMiddleware = {
287
289
  // Global middleware that can referenced from component routes
288
- isAdmin: (route, store) => {
289
- let user = store.user || { type: 'visitor' }
290
- if (user.type.match(/admin/)) return
291
- else if (user.type && user.type !== 'visitor') return { redirect: '/signin?unauth' }
290
+ isAdmin: (route: unknown, store: { user?: { type?: string } }) => {
291
+ const user = store.user || { type: 'visitor' }
292
+ if (user?.type?.match(/admin/)) return
293
+ else if (user?.type && user?.type !== 'visitor') return { redirect: '/signin?unauth' }
292
294
  else return { redirect: '/signin?signin' }
293
295
  },
294
- isSubscribed: (route, store) => {
295
- let user = store.user || { type: 'visitor', company: {} }
296
- if (!user.company.currentSubscription) return
296
+ isSubscribed: (route: unknown, store: { user?: { company?: { currentSubscription?: string } } }) => {
297
+ const user = store?.user || { type: 'visitor', company: { currentSubscription: '' } }
298
+ if (!user?.company?.currentSubscription) return
297
299
  else return { redirect: '/plans/subscribe' }
298
300
  },
299
- isUser: (route, store) => {
300
- let user = store.user || { type: 'visitor' }
301
- if (user.type !== 'visitor') return
301
+ isUser: (route: unknown, store: { user?: { type?: string } }) => {
302
+ const user = store.user || { type: 'visitor' }
303
+ if (user?.type !== 'visitor') return
302
304
  else return { redirect: '/signin?signin' }
303
305
  },
304
306
  }
@@ -0,0 +1,42 @@
1
+ import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
2
+ import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
3
+ import { onChange } from 'nitro-web'
4
+ import { useTracked } from './store'
5
+
6
+ declare global {
7
+ // Common application globals
8
+ const onChange: typeof import('nitro-web').onChange
9
+ /** Global shared store, a react-tracked container initialized in `setupApp()` */
10
+ let useTracked: typeof import('./store').useTracked
11
+
12
+ // Common aependency globals
13
+ /** The public API for rendering a history-aware `<a>`. */
14
+ const Link: typeof import('react-router-dom').Link
15
+ const useCallback: typeof import('react').useCallback
16
+ const useEffect: typeof import('react').useEffect
17
+ const useImperativeHandle: typeof import('react').useImperativeHandle
18
+ const useLocation: typeof import('react-router-dom').useLocation
19
+ const useMemo: typeof import('react').useMemo
20
+ const useNavigate: typeof import('react-router-dom').useNavigate
21
+ const useParams: typeof import('react-router-dom').useParams
22
+ const useRef: typeof import('react').useRef
23
+ const useState: typeof import('react').useState
24
+ }
25
+
26
+ Object.assign(window, {
27
+ // application globals
28
+ onChange: onChange,
29
+ useTracked: useTracked,
30
+
31
+ // dependency globals
32
+ Link: Link,
33
+ useCallback: useCallback,
34
+ useEffect: useEffect,
35
+ useImperativeHandle: useImperativeHandle,
36
+ useLocation: useLocation,
37
+ useMemo: useMemo,
38
+ useNavigate: useNavigate,
39
+ useParams: useParams,
40
+ useRef: useRef,
41
+ useState: useState,
42
+ })
@@ -0,0 +1,52 @@
1
+ // Required Global Types
2
+ import '../types/required-globals.d.ts'
3
+
4
+ // export const pi = parseFloat(3.142)
5
+ export * from './app'
6
+ export * from '../util.js'
7
+ export * as util from '../util.js'
8
+
9
+ // Component Pages
10
+ export { Signin } from '../components/auth/signin'
11
+ export { Signup } from '../components/auth/signup'
12
+ export { ResetInstructions, ResetPassword } from '../components/auth/reset'
13
+ export { Dashboard } from '../components/dashboard/dashboard'
14
+ export { NotFound } from '../components/partials/not-found'
15
+ export { Styleguide } from '../components/partials/styleguide'
16
+ // export { SettingsAccount } from '../components/settings/settings-account'
17
+ // export { SettingsBusiness } from '../components/settings/settings-business'
18
+ // export { SettingsTeamMember } from '../components/settings/settings-team--member'
19
+ // export { SettingsTeam } from '../components/settings/settings-team'
20
+
21
+ // Component Elements
22
+ export { Accordion } from '../components/partials/element/accordion'
23
+ export { Avatar } from '../components/partials/element/avatar'
24
+ export { Button } from '../components/partials/element/button'
25
+ export { Dropdown } from '../components/partials/element/dropdown'
26
+ export { GithubLink } from '../components/partials/element/github-link'
27
+ export { Initials } from '../components/partials/element/initials'
28
+ export { Message } from '../components/partials/element/message'
29
+ // export { Modal } from '../components/partials/element/modal'
30
+ export { Sidebar, type SidebarProps } from '../components/partials/element/sidebar'
31
+ export { Tooltip } from '../components/partials/element/tooltip'
32
+ export { Topbar } from '../components/partials/element/topbar'
33
+
34
+ // Component Form
35
+ export { Checkbox } from '../components/partials/form/checkbox'
36
+ export { Drop } from '../components/partials/form/drop'
37
+ export { DropHandler } from '../components/partials/form/drop-handler'
38
+ export { FormError } from '../components/partials/form/form-error'
39
+ export { Input } from '../components/partials/form/input'
40
+ export { InputColor } from '../components/partials/form/input-color'
41
+ export { InputCurrency } from '../components/partials/form/input-currency'
42
+ // export { InputDate } from '../components/partials/form/input-date'
43
+ export { Location } from '../components/partials/form/location'
44
+ export { Select, getSelectStyle } from '../components/partials/form/select'
45
+ export { Toggle } from '../components/partials/form/toggle'
46
+
47
+ // Component Layouts
48
+ export { Layout1 } from '../components/partials/layout/layout1'
49
+ export { Layout2 } from '../components/partials/layout/layout2'
50
+
51
+ // Component Other
52
+ export { IsFirstRender } from '../components/partials/is-first-render'