nitro-web 0.0.19 → 0.0.21

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.
@@ -2,7 +2,7 @@
2
2
  <head>
3
3
  <meta charset="utf-8">
4
4
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover, minimum-scale=1.0">
5
- <title>Nitro</title>
5
+ <title>{NAME}</title>
6
6
  <link rel="icon" href="{PUBLIC_PATH}favicon.png">
7
7
  <link rel="icon" type="image/png" sizes="32x32" href="{PUBLIC_PATH}favicon.png">
8
8
  </head>
@@ -24,70 +24,70 @@ export const SigninPage = () => <Signin config={config} />
24
24
  SigninPage.route = {
25
25
  '/signin': true,
26
26
  '/signout': true,
27
- 'meta': { 'title': 'Sign In - Nitro', layout: 2 },
27
+ 'meta': { 'title': 'Sign In', layout: 2 },
28
28
  }
29
29
 
30
30
  // Signup page
31
31
  export const SignupPage = () => <Signup config={config} />
32
32
  SignupPage.route = {
33
33
  '/signup': true,
34
- 'meta': { 'title': 'Sign Up - Nitro', layout: 2 },
34
+ 'meta': { 'title': 'Sign Up', layout: 2 },
35
35
  }
36
36
 
37
37
  // Reset instructions page
38
38
  export const ResetInstructionsPage = () => <ResetInstructions />
39
39
  ResetInstructionsPage.route = {
40
40
  '/reset': true,
41
- 'meta': { 'title': 'Reset password - Nitro', layout: 2 },
41
+ 'meta': { 'title': 'Reset password', layout: 2 },
42
42
  }
43
43
 
44
44
  // Reset password page
45
45
  export const ResetPasswordPage = () => <ResetPassword />
46
46
  ResetPasswordPage.route = {
47
47
  '/reset/:token': true,
48
- 'meta': { 'title': 'Reset password - Nitro', layout: 2 },
48
+ 'meta': { 'title': 'Reset password', layout: 2 },
49
49
  }
50
50
 
51
51
  // // Settings Account page
52
52
  // export const SettingsAccountPage = () => <SettingsAccount />
53
53
  // SettingsAccountPage.route = {
54
54
  // '/settings/account': ['isUser'],
55
- // 'meta': { 'title': 'Account Settings - Nitro', layout: 1 },
55
+ // 'meta': { 'title': 'Account Settings', layout: 1 },
56
56
  // }
57
57
 
58
58
  // // Settings Business page
59
59
  // export const SettingsBusinessPage = () => <SettingsBusiness config={config} />
60
60
  // SettingsBusinessPage.route = {
61
61
  // '/settings/business': ['isUser'],
62
- // 'meta': { 'title': 'Business Settings - Nitro', layout: 1 },
62
+ // 'meta': { 'title': 'Business Settings', layout: 1 },
63
63
  // }
64
64
 
65
65
  // // Settings Team page
66
66
  // export const SettingsTeamPage = () => <SettingsTeam config={config} />
67
67
  // SettingsTeamPage.route = {
68
68
  // '/settings/team': ['isUser'],
69
- // 'meta': { 'title': 'Team Settings - Nitro', layout: 1 },
69
+ // 'meta': { 'title': 'Team Settings', layout: 1 },
70
70
  // }
71
71
 
72
72
  // Dashboard page
73
73
  export const DashboardPage = () => <Dashboard config={config} />
74
74
  DashboardPage.route = {
75
75
  '/': true,
76
- 'meta': { 'title': 'Dashboard - Nitro', layout: 1 },
76
+ 'meta': { 'title': 'Dashboard', layout: 1 },
77
77
  }
78
78
 
79
79
  // Styleguide page
80
80
  export const StyleguidePage = () => <Styleguide config={config} />
81
81
  StyleguidePage.route = {
82
82
  '/styleguide': true,
83
- 'meta': { title: `${isDemo ? 'Design System' : 'Style Guide'} - Nitro`, layout: 1 },
83
+ 'meta': { title: `${isDemo ? 'Design System' : 'Style Guide'}`, layout: 1 },
84
84
  }
85
85
 
86
86
  // Not found page
87
87
  export const NotFoundPage = () => <NotFound />
88
88
  NotFoundPage.route = {
89
89
  '*': true,
90
- 'meta': { 'title': 'Nothing found - Nitro', layout: 1 },
90
+ 'meta': { 'title': 'Nothing found', layout: 1 },
91
91
  }
92
92
 
93
93
  // Custom Tailwind UI page example
@@ -226,5 +226,5 @@ const style = css`
226
226
  `
227
227
  PricingPage.route = {
228
228
  '/pricing': true,
229
- 'meta': { 'title': 'Pricing - Nitro', layout: 1 },
229
+ 'meta': { 'title': 'Pricing', layout: 1 },
230
230
  }
@@ -1,9 +1,10 @@
1
1
  import { Layout1 as L1, Layout2 as L2 } from 'nitro-web'
2
2
  import Logo from '../../client/imgs/logo/logo.svg'
3
+ import { Config } from 'types'
3
4
 
4
5
  const links = [
5
6
  { name: 'Nitro on Github', to: 'https://github.com/boycce/nitro-web', initial: 'G' },
6
7
  ]
7
8
 
8
- export const Layout1 = () => { return <L1 Logo={Logo} links={links} /> }
9
- export const Layout2 = () => { return <L2 Logo={Logo} /> }
9
+ export const Layout1 = ({ config }: { config: Config }) => { return <L1 Logo={Logo} links={links} config={config} /> }
10
+ export const Layout2 = ({ config }: { config: Config }) => { return <L2 Logo={Logo} config={config} /> }
@@ -87,7 +87,7 @@
87
87
  "extends": "../.eslintrc.json"
88
88
  },
89
89
  "engines": {
90
- "node": "^18"
90
+ "node": ">=18"
91
91
  },
92
92
  "browserslist": [
93
93
  "> 1%",
@@ -5,7 +5,9 @@ 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 isStatic placeholderEmail stripePublishableKey version',
8
+ inject:
9
+ 'awsUrl clientUrl currencies countries env googleMapsApiKey isStatic name placeholderEmail ' +
10
+ 'stripePublishableKey titleSeparator version',
9
11
 
10
12
  clientUrl: process.env.originUrl || 'http://localhost:3000',
11
13
  emailFrom: process.env.emailFrom,
@@ -16,6 +18,7 @@ export default {
16
18
  isStatic: process.env.isStatic,
17
19
  masterPassword: process.env.masterPassword,
18
20
  mongoUrl: process.env.mongoUrl,
21
+ name: 'Nitro',
19
22
  placeholderEmail: process.env.placeholderEmail,
20
23
  publicPath: process.env.publicPath,
21
24
  pwd: pwd, // change to rootDir
package/client/app.tsx CHANGED
@@ -6,13 +6,19 @@ import { beforeCreate, Provider, exposedData } from './store'
6
6
  import { axios, camelCase, pick, toArray, setTimeoutPromise } from 'nitro-web/util'
7
7
  import { Config, Store } from 'types'
8
8
 
9
+ type LayoutProps = {
10
+ config: Config;
11
+ }
12
+
9
13
  type Settings = {
10
14
  afterApp?: () => void
11
- middleware: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
12
15
  beforeApp: (config: Config) => Promise<object>
13
16
  beforeStoreUpdate: (prevStore: Store | null, newData: Store) => Store
14
17
  isStatic?: boolean
15
- layouts: React.FC[]
18
+ layouts: React.FC<LayoutProps>[]
19
+ middleware: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
20
+ name: string
21
+ titleSeparator?: string
16
22
  }
17
23
 
18
24
  type Route = {
@@ -24,26 +30,28 @@ type Route = {
24
30
  redirect?: string
25
31
  }
26
32
 
27
- export async function setupApp(config: Config, layouts: React.FC[]) {
33
+ export async function setupApp(config: Config, layouts: React.FC<LayoutProps>[]) {
28
34
  // Fetch state and init app
29
35
  const settings: Settings = {
30
- middleware: Object.assign(defaultMiddleware, config.middleware || {}),
31
36
  beforeApp: config.beforeApp || beforeApp,
32
37
  beforeStoreUpdate: config.beforeStoreUpdate || beforeStoreUpdate,
33
38
  isStatic: config.isStatic,
34
39
  layouts: layouts,
40
+ middleware: Object.assign(defaultMiddleware, config.middleware || {}),
41
+ name: config.name,
42
+ titleSeparator: config.titleSeparator,
35
43
  }
36
44
  if (!settings.layouts) throw new Error('layouts are required')
37
45
  const initData = (await settings.beforeApp(config)) || {}
38
46
  beforeCreate(initData, settings.beforeStoreUpdate)
39
47
 
40
48
  const root = ReactDOM.createRoot(document.getElementById('app') as HTMLElement)
41
- root.render(<App settings={settings} />)
49
+ root.render(<App settings={settings} config={config} />)
42
50
  }
43
51
 
44
- function App({ settings }: { settings: Settings }): ReactNode {
52
+ function App({ settings, config }: { settings: Settings, config: Config }): ReactNode {
45
53
  // const themeNormalised = theme
46
- const router = getRouter(settings)
54
+ const router = getRouter({ settings, config })
47
55
  // const theme = pick(themeNormalised, []) // e.g. 'topPanelHeight'
48
56
 
49
57
  useEffect(() => {
@@ -82,7 +90,7 @@ function AfterApp({ settings }: { settings: Settings }) {
82
90
  return (null)
83
91
  }
84
92
 
85
- function getRouter(settings: Settings) {
93
+ function getRouter({ settings, config }: { settings: Settings, config: Config }) {
86
94
  /**
87
95
  * Get all routes from components folder
88
96
  * @return {array} routes for for creatingBrowserRouter
@@ -145,6 +153,7 @@ function getRouter(settings: Settings) {
145
153
  meta: {
146
154
  ...(route.meta || {}),
147
155
  layout: layoutNum,
156
+ title: `${route.meta?.title ? `${route.meta.title}${settings.titleSeparator || ' - '}` : ''}${settings.name}`,
148
157
  },
149
158
  middleware: middleware,
150
159
  name: componentName,
@@ -166,7 +175,7 @@ function getRouter(settings: Settings) {
166
175
  // path: '/', // (disbaled: page component with path:'/' doesnt work)
167
176
  element: (
168
177
  <Fragment>
169
- <Layout />
178
+ <Layout config={config} />
170
179
  <RestoreScroll />
171
180
  </Fragment>
172
181
  ),
@@ -2,6 +2,7 @@
2
2
  import { Dialog, DialogBackdrop, DialogPanel, TransitionChild } from '@headlessui/react'
3
3
  import avatarImg from 'nitro-web/client/imgs/avatar.jpg'
4
4
  import { isDemo } from 'nitro-web'
5
+ import { Config } from 'types'
5
6
  import {
6
7
  Bars3Icon,
7
8
  HomeIcon,
@@ -10,20 +11,20 @@ import {
10
11
  ArrowLeftCircleIcon,
11
12
  PaintBrushIcon,
12
13
  } from '@heroicons/react/24/outline'
13
-
14
14
  const sidebarWidth = 'lg:w-80'
15
15
 
16
16
  export type SidebarProps = {
17
17
  Logo: React.FC<{ width?: string, height?: string, alt?: string }>;
18
18
  menu?: { name: string; to: string; Icon: React.FC<{ className?: string }> }[]
19
19
  links?: { name: string; to: string; initial: string }[]
20
+ config?: Config
20
21
  }
21
22
 
22
23
  function classNames(...classes: string[]) {
23
24
  return classes.filter(Boolean).join(' ')
24
25
  }
25
26
 
26
- export function Sidebar({ Logo, menu, links }: SidebarProps) {
27
+ export function Sidebar({ Logo, menu, links, config }: SidebarProps) {
27
28
  const [sidebarOpen, setSidebarOpen] = useState(false)
28
29
  return (
29
30
  <>
@@ -46,14 +47,14 @@ export function Sidebar({ Logo, menu, links }: SidebarProps) {
46
47
  </button>
47
48
  </div>
48
49
  </TransitionChild>
49
- <SidebarContents Logo={Logo} menu={menu} links={links} />
50
+ <SidebarContents Logo={Logo} menu={menu} links={links} config={config} />
50
51
  </DialogPanel>
51
52
  </div>
52
53
  </Dialog>
53
54
 
54
55
  {/* Static sidebar for desktop */}
55
56
  <div className={`hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:flex-col ${sidebarWidth}`}>
56
- <SidebarContents Logo={Logo} menu={menu} links={links} />
57
+ <SidebarContents Logo={Logo} menu={menu} links={links} config={config} />
57
58
  </div>
58
59
 
59
60
  {/* mobile sidebar closed */}
@@ -72,7 +73,7 @@ export function Sidebar({ Logo, menu, links }: SidebarProps) {
72
73
  )
73
74
  }
74
75
 
75
- function SidebarContents ({ Logo, menu, links }: SidebarProps) {
76
+ function SidebarContents ({ Logo, menu, links, config }: SidebarProps) {
76
77
  const location = useLocation()
77
78
  const [store] = useTracked()
78
79
  const user = store.user
@@ -100,7 +101,7 @@ function SidebarContents ({ Logo, menu, links }: SidebarProps) {
100
101
  <div className="flex grow flex-col gap-y-8 overflow-y-auto bg-white py-5 px-10 lg:border-r lg:border-gray-200">
101
102
  <div className="flex h-16 shrink-0 items-center">
102
103
  <Link to="/">
103
- <Logo alt="Nitro" width="70" height={undefined} />
104
+ <Logo alt={config?.name || 'Nitro'} width="70" height={undefined} />
104
105
  </Link>
105
106
  </div>
106
107
  <nav className="flex flex-1 flex-col">
@@ -2,14 +2,14 @@ import { css } from 'twin.macro'
2
2
  import { Outlet } from 'react-router-dom'
3
3
  import { Message, Sidebar, SidebarProps } from 'nitro-web'
4
4
 
5
- export function Layout1({ Logo, menu, links }: SidebarProps) {
5
+ export function Layout1({ Logo, menu, links, config }: SidebarProps) {
6
6
  // Dashboard, app screens (only the <Outlet/> receives `params` and `location`)
7
7
  return (
8
8
  <div css={style} class="bg-[#F3F3F3]">
9
9
  <Message />
10
10
  <div class="flex-1">
11
11
  <div class="wrapper lg:flex min-h-[100%] w-[100%] bg-[#FDFDFD] shadow-[0_0_40px_0_rgb(237_237_237)]">
12
- <Sidebar Logo={Logo} menu={menu} links={links} />
12
+ <Sidebar Logo={Logo} menu={menu} links={links} config={config} />
13
13
  <div class="py-10 px-14 flex-1">
14
14
  <Outlet />
15
15
  </div>
@@ -1,9 +1,10 @@
1
1
  import { css } from 'twin.macro'
2
2
  import { Outlet } from 'react-router-dom'
3
3
  import { Message } from 'nitro-web'
4
+ import { Config } from 'types'
4
5
 
5
6
  // Signin, reset password, etc
6
- export function Layout2({ Logo }: { Logo: React.ComponentType<{ alt: string; width: string }> }) {
7
+ export function Layout2({ Logo, config }: { Logo: React.ComponentType<{ alt: string; width: string }>, config: Config }) {
7
8
  return (
8
9
  <div css={style} class="bg-[#F3F3F3]">
9
10
  <Message />
@@ -11,7 +12,7 @@ export function Layout2({ Logo }: { Logo: React.ComponentType<{ alt: string; wid
11
12
  <div class="flex-1 w-full wrapper-2 px-5 py-10">
12
13
  <div class="border-b mb-6">
13
14
  <Link to="/signin" class="logo relative block -ml-1 -mt-1 p-1">
14
- <Logo alt="Nitro" width="60" />
15
+ <Logo alt={config?.name || 'Nitro'} width="60" />
15
16
  </Link>
16
17
  </div>
17
18
  <Outlet />
@@ -24,7 +25,7 @@ export function Layout2({ Logo }: { Logo: React.ComponentType<{ alt: string; wid
24
25
  <li><Link class="underline1" to="/support">Support</Link></li>
25
26
  </ul>
26
27
  <div>
27
- 2025 © Nitro
28
+ 2025 © {config?.name || 'Nitro'}
28
29
  </div>
29
30
  </div>
30
31
 
@@ -2,8 +2,15 @@
2
2
  // todo: finish tailwind conversion
3
3
  import { Button, FormError, Field, Modal, Select } from 'nitro-web'
4
4
  import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
5
+ import { Config } from 'types'
5
6
 
6
- export function SettingsTeamMember ({ showModal, setShowModal }) {
7
+ type SettingsTeamMemberProps = {
8
+ showModal: boolean
9
+ setShowModal: (showModal: boolean) => void
10
+ config?: Config
11
+ }
12
+
13
+ export function SettingsTeamMember ({ showModal, setShowModal, config }: SettingsTeamMemberProps) {
7
14
  // @param {object} showModal - user
8
15
  const [{ user }] = sharedStore.useTracked()
9
16
  const [isLoading] = useState('')
@@ -16,7 +23,6 @@ export function SettingsTeamMember ({ showModal, setShowModal }) {
16
23
  },
17
24
  })
18
25
 
19
-
20
26
  // permit polit changes
21
27
  // typescripty,
22
28
 
@@ -28,7 +34,7 @@ export function SettingsTeamMember ({ showModal, setShowModal }) {
28
34
  <Modal show={showModal} setShow={setShowModal} class="p-modal">
29
35
 
30
36
  <h2 class="h2"><em>Add</em> Team Member</h2>
31
- <p class="subtitle">Invite a new team member to collaborate with you on Nitro.</p>
37
+ <p class="subtitle">Invite a new team member to collaborate with you on {config?.name || 'Nitro'}.</p>
32
38
 
33
39
  <form class="form" onSubmit={onSubmit}>
34
40
  <div class="cols cols-6 cols-gap-2-5">
@@ -83,7 +89,7 @@ export function SettingsTeamMember ({ showModal, setShowModal }) {
83
89
  <Field
84
90
  name="message"
85
91
  type="textarea"
86
- placeholder={`${user.firstName} is inviting you to collaborate on Nitro.`}
92
+ placeholder={`${user.firstName} is inviting you to collaborate on ${config?.name || 'Nitro'}.`}
87
93
  state={state} onChange={onChange.bind(setState)}
88
94
  />
89
95
  </div>
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀",
4
4
  "repository": "github:boycce/nitro-web",
5
5
  "homepage": "https://boycce.github.io/nitro-web/",
6
- "version": "0.0.19",
6
+ "version": "0.0.21",
7
7
  "main": "./client/index.ts",
8
8
  "type": "module",
9
9
  "keywords": [
package/readme.md CHANGED
@@ -11,7 +11,7 @@ npm i nitro-web
11
11
  ### Install
12
12
 
13
13
  1. Copy ./_example into your project
14
- 2. In package.json, replace `"nitro-web": "file:.."` with `"nitro-web": "~0.0.19"`
14
+ 2. In package.json, replace `"nitro-web": "file:.."` with `"nitro-web": "~0.0.21"`
15
15
  3. In package.json, replace `"../.eslintrc.json"` with `"./node_modules/nitro-web/.eslintrc.json"`
16
16
  4. Run `npm i`
17
17
 
package/types.ts CHANGED
@@ -1,18 +1,20 @@
1
1
  // Expected config to be available
2
2
  export type Config = {
3
3
  clientUrl: string
4
+ countries: { [key: string]: { numberFormats: { currency: string } } } // for input-currency.tsx
5
+ currencies: { [key: string]: { symbol: string, digits: number } } // for input-currency.tsx
4
6
  env: string
7
+ name: string
8
+
5
9
  awsUrl?: string
6
- // needed for input-currency.tsx
7
- currencies: { [key: string]: { symbol: string, digits: number } }
8
- countries: { [key: string]: { numberFormats: { currency: string } } }
10
+ beforeApp?: () => Promise<object>
11
+ beforeStoreUpdate?: (prevStore: Store | null, newData: Store) => Store
9
12
  googleMapsApiKey?: string
10
13
  isStatic?: boolean
14
+ middleware?: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
11
15
  placeholderEmail?: string
12
16
  stripePublishableKey?: string
13
- middleware?: Record<string, (route: unknown, store: Store) => undefined | { redirect: string }>
14
- beforeApp?: () => Promise<object>
15
- beforeStoreUpdate?: (prevStore: Store | null, newData: Store) => Store
17
+ titleSeparator?: string
16
18
  }
17
19
 
18
20
  export type User = {
package/webpack.config.js CHANGED
@@ -284,7 +284,7 @@ export const getConfig = (config) => {
284
284
  }),
285
285
  new MiniCssExtractPlugin({ filename: `assets/bundle.[name]${isBuild ? '.[contenthash]' : ''}.css` }),
286
286
  new HtmlWebpackPlugin({ template: clientDir + 'index.html', filename: distDir + 'index.html' }),
287
- new InterpolateHtmlPlugin(HtmlWebpackPlugin, { PUBLIC_PATH: publicPath }),
287
+ new InterpolateHtmlPlugin(HtmlWebpackPlugin, { PUBLIC_PATH: publicPath, NAME: config.name }),
288
288
  new CleanTerminalPlugin({ skipFirstRun: true }),
289
289
  // !isBuild && new ReactRefreshWebpackPlugin({ overlay: false }),
290
290
  ].filter(Boolean),