nitro-web 0.0.87 → 0.0.89

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 (35) hide show
  1. package/client/index.ts +0 -3
  2. package/components/auth/auth.api.js +411 -0
  3. package/components/auth/reset.tsx +86 -0
  4. package/components/auth/signin.tsx +76 -0
  5. package/components/auth/signup.tsx +62 -0
  6. package/components/billing/stripe.api.js +268 -0
  7. package/components/dashboard/dashboard.tsx +32 -0
  8. package/components/partials/element/accordion.tsx +102 -0
  9. package/components/partials/element/avatar.tsx +40 -0
  10. package/components/partials/element/button.tsx +98 -0
  11. package/components/partials/element/calendar.tsx +125 -0
  12. package/components/partials/element/dropdown.tsx +248 -0
  13. package/components/partials/element/filters.tsx +194 -0
  14. package/components/partials/element/github-link.tsx +16 -0
  15. package/components/partials/element/initials.tsx +66 -0
  16. package/components/partials/element/message.tsx +141 -0
  17. package/components/partials/element/modal.tsx +90 -0
  18. package/components/partials/element/sidebar.tsx +195 -0
  19. package/components/partials/element/tooltip.tsx +154 -0
  20. package/components/partials/element/topbar.tsx +15 -0
  21. package/components/partials/form/checkbox.tsx +150 -0
  22. package/components/partials/form/drop-handler.tsx +68 -0
  23. package/components/partials/form/drop.tsx +141 -0
  24. package/components/partials/form/field-color.tsx +86 -0
  25. package/components/partials/form/field-currency.tsx +158 -0
  26. package/components/partials/form/field-date.tsx +252 -0
  27. package/components/partials/form/field.tsx +231 -0
  28. package/components/partials/form/form-error.tsx +27 -0
  29. package/components/partials/form/location.tsx +225 -0
  30. package/components/partials/form/select.tsx +360 -0
  31. package/components/partials/is-first-render.ts +14 -0
  32. package/components/partials/not-found.tsx +7 -0
  33. package/components/partials/styleguide.tsx +407 -0
  34. package/package.json +2 -1
  35. package/types/globals.d.ts +0 -1
@@ -0,0 +1,407 @@
1
+ import {
2
+ Drop, Dropdown, Field, Select, Button as ButtonNitro, Checkbox, GithubLink, Modal, Calendar, injectedConfig,
3
+ Filters, FiltersHandleType, FilterType,
4
+ } from 'nitro-web'
5
+ import { getCountryOptions, getCurrencyOptions, ucFirst } from 'nitro-web/util'
6
+ import { Check, FileEditIcon } from 'lucide-react'
7
+
8
+ type StyleguideProps = {
9
+ className?: string
10
+ elements?: {
11
+ Button?: typeof ButtonNitro
12
+ }
13
+ children?: React.ReactNode
14
+ }
15
+
16
+ export function Styleguide({ className, elements, children }: StyleguideProps) {
17
+ const [customerSearch, setCustomerSearch] = useState('')
18
+ const [showModal1, setShowModal1] = useState(false)
19
+ const [state, setState] = useState({
20
+ address: '',
21
+ amount: 100,
22
+ brandColor: '#F3CA5F',
23
+ colorsMulti: ['blue', 'green'],
24
+ country: 'nz',
25
+ currency: 'nzd', // can be commented too
26
+ date: Date.now(),
27
+ 'date-range': [Date.now(), Date.now() + 1000 * 60 * 60 * 24 * 33],
28
+ 'date-time': Date.now(),
29
+ calendar: [Date.now(), Date.now() + 1000 * 60 * 60 * 24 * 8],
30
+ firstName: 'Bruce',
31
+ errors: [
32
+ { title: 'address', detail: 'Address is required' },
33
+ ],
34
+ })
35
+ const [filterState, setFilterState] = useState({})
36
+ const filtersRef = useRef<FiltersHandleType>(null)
37
+ const filters = useMemo(() => {
38
+ const filters: FilterType[] = [
39
+ {
40
+ type: 'date',
41
+ name: 'dateRange',
42
+ mode: 'range',
43
+ placeholder: 'Select a range...',
44
+ },
45
+ {
46
+ type: 'search',
47
+ name: 'search',
48
+ label: 'Keyword Search',
49
+ placeholder: 'Job, employee name...',
50
+ },
51
+ {
52
+ type: 'select',
53
+ name: 'status',
54
+ rowClassName: 'flex-1',
55
+ options: [
56
+ { label: 'Pending', value: 'pending' },
57
+ { label: 'Approved', value: 'approved' },
58
+ { label: 'Rejected', value: 'rejected' },
59
+ ],
60
+ },
61
+ {
62
+ type: 'color',
63
+ name: 'color',
64
+ label: 'Half column',
65
+ placeholder: 'Select color...',
66
+ rowClassName: 'flex-1',
67
+ },
68
+ ]
69
+ return filters
70
+ }, [])
71
+
72
+ const options = useMemo(() => [
73
+ { label: 'Open customer preview' },
74
+ { label: 'Add a payment', isSelected: true },
75
+ { label: 'Email invoice' },
76
+ { label: 'Download' },
77
+ { label: 'Edit' },
78
+ { label: 'Copy' },
79
+ { label: 'Delete' },
80
+ ], [])
81
+
82
+ const Button = elements?.Button || ButtonNitro
83
+
84
+ function onCustomerInputChange (e: { target: { name: string, value: unknown } }) {
85
+ if (e.target.name == 'customer' && e.target.value == '0') {
86
+ setCustomerSearch('')
87
+ e.target.value = null // clear the select's selected value
88
+ setTimeout(() => alert('Adding new customer...'), 0)
89
+ }
90
+ onChange(setState, e)
91
+ }
92
+
93
+ function onCustomerSearch (search: string) {
94
+ setCustomerSearch(search || '')
95
+ }
96
+
97
+ // Example of updating state
98
+ // useEffect(() => {
99
+ // setTimeout(() => {
100
+ // setState({ ...state, amount: 123456, currency: 'usd', brandColor: '#8656ED' })
101
+ // }, 2000)
102
+ // }, [])
103
+
104
+ return (
105
+ <div class={`text-left max-w-[1100px] ${className}`}>
106
+ <GithubLink filename={__filename} />
107
+ <div class="mb-7">
108
+ <h1 class="h1">{injectedConfig.isDemo ? 'Design System' : 'Style Guide'}</h1>
109
+ <p>
110
+ Components are styled using&nbsp;
111
+ <a href="https://v3.tailwindcss.com/docs/configuration" class="underline" target="_blank" rel="noreferrer">TailwindCSS</a>.
112
+ </p>
113
+ </div>
114
+
115
+ <h2 class="h3">Links</h2>
116
+ <div class="mb-10">
117
+ <a class="mr-2" href="#">Default</a>
118
+ <a class="underline1 is-active mr-2" href="#">Underline1</a>
119
+ <a class="underline2 is-active mr-2" href="#">Underline2</a>
120
+ </div>
121
+
122
+ <h2 class="h3">Modals</h2>
123
+ <div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
124
+ <div><Button color="primary" onClick={() => setShowModal1(true)}>Modal (default)</Button></div>
125
+ </div>
126
+
127
+ <h2 class="h3">Dropdowns</h2>
128
+ <div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
129
+ <div>
130
+ <Dropdown options={options} minWidth="250px">
131
+ <Button IconRight="v" class="gap-x-3">Dropdown</Button>
132
+ </Dropdown>
133
+ </div>
134
+ <div>
135
+ <Dropdown
136
+ // menuIsOpen={true}
137
+ dir="bottom-right"
138
+ minWidth="330px"
139
+ options={[{ label: <><b>New Customer</b> / Add <b>Bruce Lee</b></>, className: 'border-bottom-with-space' }, ...options]}
140
+ >
141
+ <Button color="white" IconRight="v" class="gap-x-3">Dropdown bottom-right</Button>
142
+ </Dropdown>
143
+ </div>
144
+ <div>
145
+ <Dropdown options={options} dir="top-left" minWidth="250px">
146
+ <Button color="white" IconRight="v" class="gap-x-3">Dropdown top-left</Button>
147
+ </Dropdown>
148
+ </div>
149
+ </div>
150
+
151
+ <h2 class="h3">Filters</h2>
152
+ <div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
153
+ {/* Filter dropdown */}
154
+ <Filters
155
+ ref={filtersRef}
156
+ filters={filters}
157
+ state={filterState}
158
+ setState={setFilterState}
159
+ dropdownProps={{ dir: 'bottom-left' }}
160
+ elements={{ Button: Button }}
161
+ />
162
+ {/* Search bar */}
163
+ <Field
164
+ class="!my-0 min-w-[242px]"
165
+ type="search"
166
+ name="search"
167
+ id="search2"
168
+ iconPos="left"
169
+ state={filterState}
170
+ onChange={(e) => {
171
+ onChange(setFilterState, e)
172
+ filtersRef.current?.submit()
173
+ }}
174
+ placeholder="Linked search bar..."
175
+ />
176
+ </div>
177
+
178
+ <h2 class="h3">Buttons</h2>
179
+ <div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
180
+ <div><Button color="primary">primary (default)</Button></div>
181
+ <div><Button color="secondary">secondary button</Button></div>
182
+ <div><Button color="black">black button</Button></div>
183
+ <div><Button color="dark">dark button</Button></div>
184
+ <div><Button color="white">white button</Button></div>
185
+ <div><Button color="clear">clear button</Button></div>
186
+ <div><Button color="primary" size="xs">*-xs button</Button></div>
187
+ <div><Button color="primary" size="sm">*-sm button</Button></div>
188
+ <div><Button color="primary">*-md (default)</Button></div>
189
+ <div><Button color="primary" size="lg">*-lg button</Button></div>
190
+ <div><Button IconLeft={<Check size={19} className="-my-5" />}>IconLeft</Button></div>
191
+ <div><Button IconLeft={<Check size={19} className="-my-5" />}
192
+ className="w-[160px]">IconLeft 160px</Button></div>
193
+ <div><Button IconLeftEnd={<Check size={19} className="-my-5" />}
194
+ className="w-[190px]">IconLeftEnd 190px</Button></div>
195
+ <div><Button IconRight="v">IconRight</Button></div>
196
+ <div><Button IconRightEnd="v" className="w-[190px]">IconRightEnd 190px</Button></div>
197
+ <div><Button color="primary" IconRight="v" isLoading>primary isLoading</Button></div>
198
+ <div><Button IconCenter={<FileEditIcon size={18}/>}></Button></div>
199
+ <div><Button size="sm" IconCenter={<FileEditIcon size={16}/>}></Button></div>
200
+ <div><Button size="xs" IconCenter={<FileEditIcon size={14}/>}></Button></div>
201
+ </div>
202
+
203
+ <h2 class="h3">Varients</h2>
204
+ <div class="grid grid-cols-3 gap-x-6 mb-4">
205
+ <div>
206
+ <label for="input2">Toggles</label>
207
+ <Checkbox name="input2" type="toggle" text="Toggle sm" subtext="some additional text here." class="!mb-0"
208
+ state={state} onChange={(e) => onChange(setState, e)} />
209
+ <Checkbox name="input3" type="toggle" text="Toggle 22px" subtext="some additional text here." size={22} />
210
+ </div>
211
+ <div>
212
+ <label for="input1">Radios</label>
213
+ <Checkbox name="input1" type="radio" text="Radio" subtext="some additional text here 1." id="input1-1" class="!mb-0"
214
+ defaultChecked />
215
+ <Checkbox name="input1" type="radio" text="Radio 16px" subtext="some additional text here 2." id="input1-2" size={16} />
216
+ </div>
217
+ <div>
218
+ <label for="input0">Checkboxes</label>
219
+ <Checkbox name="input0" type="checkbox" text="Checkbox" subtext="some additional text here." class="!mb-0" defaultChecked />
220
+ <Checkbox name="input0.1" type="checkbox" text="Checkbox 16px" size={16}
221
+ subtext="some additional text here which is a bit longer that will be line-wrap to the next line." />
222
+ </div>
223
+ </div>
224
+
225
+ <h2 class="h3">Selects</h2>
226
+ <div class="grid grid-cols-3 lg:grid-cols-3 gap-x-6 mb-4">
227
+ <div>
228
+ <label for="action">Default</label>
229
+ <Select
230
+ // menuIsOpen={true}
231
+ name="action"
232
+ isSearchable={false}
233
+ options={useMemo(() => [
234
+ { value: 'edit', label: 'Edit' },
235
+ { value: 'delete', label: 'Delete' },
236
+ ], [])}
237
+ />
238
+ </div>
239
+ <div>
240
+ <label for="colorsMulti">Mutli Select</label>
241
+ <Select
242
+ name="colorsMulti"
243
+ isMulti={true}
244
+ state={state}
245
+ options={useMemo(() => [
246
+ { value: 'blue', label: 'Blue' },
247
+ { value: 'green', label: 'Green' },
248
+ { value: 'yellow', label: 'Yellow' },
249
+ { value: 'red', label: 'Red' },
250
+ { value: 'orange', label: 'Orange' },
251
+ { value: 'purple', label: 'Purple' },
252
+ { value: 'pink', label: 'Pink' },
253
+ { value: 'gray', label: 'Gray' },
254
+ { value: 'black', label: 'Black' },
255
+ { value: 'white', label: 'White' },
256
+ ], [])}
257
+ onChange={(e) => onChange(setState, e)}
258
+ />
259
+ </div>
260
+ <div>
261
+ <label for="country">Countries</label>
262
+ <Select
263
+ // https://github.com/lipis/flag-icons
264
+ name="country"
265
+ mode="country"
266
+ state={state}
267
+ options={useMemo(() => getCountryOptions(injectedConfig.countries), [])}
268
+ onChange={(e) => onChange(setState, e)}
269
+ />
270
+ </div>
271
+ <div>
272
+ <label for="customer">List Item with Action</label>
273
+ <Select
274
+ // menuIsOpen={true}
275
+ placeholder="Select or add customer..."
276
+ name="customer"
277
+ mode="customer"
278
+ state={state}
279
+ onChange={onCustomerInputChange}
280
+ onInputChange={onCustomerSearch}
281
+ options={useMemo(() => [
282
+ {
283
+ className: 'bb',
284
+ fixed: true,
285
+ value: '0',
286
+ label: (
287
+ <>
288
+ <b>New Customer</b> (and clear select)
289
+ {customerSearch ? <> / Add <b>{ucFirst(customerSearch)}</b></> : ''}
290
+ </>
291
+ ),
292
+ },
293
+ { value: '1', label: 'Iron Man Industries' },
294
+ { value: '2', label: 'Captain America' },
295
+ { value: '3', label: 'Thor Limited' },
296
+ ], [customerSearch])}
297
+ />
298
+ </div>
299
+ <div>
300
+ <label for="currency">Currencies (Error)</label>
301
+ <Select
302
+ name="currency"
303
+ state={state}
304
+ options={useMemo(() => getCurrencyOptions(injectedConfig.currencies), [])}
305
+ onChange={(e) => onChange(setState, e)}
306
+ />
307
+ </div>
308
+ </div>
309
+
310
+ <h2 class="h3">Inputs</h2>
311
+ <div class="grid grid-cols-3 gap-x-6 mb-4">
312
+ <div>
313
+ <label for="firstName">First Name</label>
314
+ <Field name="firstName" state={state} onChange={(e) => onChange(setState, e)} />
315
+ </div>
316
+ <div>
317
+ <label for="email">Email Address</label>
318
+ <Field name="email" type="email" placeholder="Your email address..."/>
319
+ </div>
320
+ <div>
321
+ <div class="flex justify-between">
322
+ <label for="password">Password</label>
323
+ <a href="#" class="label">Forgot?</a>
324
+ </div>
325
+ <Field name="password" type="password"/>
326
+ </div>
327
+ <div>
328
+ <label for="search3">Search</label>
329
+ <Field name="search" id="search3" type="search" placeholder="Search..." />
330
+ </div>
331
+ <div>
332
+ <label for="filter">Filter by Code</label>
333
+ <Field name="filter" type="filter" iconPos="left" />
334
+ </div>
335
+ <div>
336
+ <label for="address">Input Error</label>
337
+ <Field name="address" placeholder="Address..." state={state} onChange={(e) => onChange(setState, e)} />
338
+ </div>
339
+ <div>
340
+ <label for="description">Description</label>
341
+ <Field name="description" type="textarea" rows={2} />
342
+ </div>
343
+ <div>
344
+ <label for="brandColor">Brand Color</label>
345
+ <Field name="brandColor" type="color" iconPos="left" state={state} onChange={(e) => onChange(setState, e)} />
346
+ </div>
347
+ <div>
348
+ <label for="amount">Amount ({state.amount})</label>
349
+ <Field name="amount" type="currency" state={state} currency={state.currency || 'nzd'} onChange={(e) => onChange(setState, e)}
350
+ config={injectedConfig} />
351
+ </div>
352
+ </div>
353
+
354
+ <h2 class="h3">Date Inputs</h2>
355
+ <div class="grid grid-cols-1 gap-x-6 mb-4 sm:grid-cols-3">
356
+ <div>
357
+ <label for="date">Date with time</label>
358
+ <Field name="date-time" type="date" mode="single" showTime={true} state={state} onChange={(e) => onChange(setState, e)} />
359
+ </div>
360
+ <div>
361
+ <label for="date-range">Date range with prefix</label>
362
+ <Field name="date-range" type="date" mode="range" prefix="Date:" state={state} onChange={(e) => onChange(setState, e)} />
363
+ </div>
364
+ <div>
365
+ <label for="date">Date multi-select (right aligned)</label>
366
+ <Field name="date" type="date" mode="multiple" state={state} onChange={(e) => onChange(setState, e)} dir="bottom-right" />
367
+ </div>
368
+ </div>
369
+
370
+ <Modal show={showModal1} setShow={setShowModal1}>
371
+ <h3 class="h3">Edit Profile</h3>
372
+ <p class="mb-5">An example modal containing a basic form for editing profiles.</p>
373
+ <form class="mb-8 text-left">
374
+ <div>
375
+ <label for="firstName2">First Name</label>
376
+ <Field name="firstName2" state={state} onChange={(e) => onChange(setState, e)} />
377
+ </div>
378
+ <div>
379
+ <label for="email2">Email Address</label>
380
+ <Field name="email2" type="email" placeholder="Your email address..."/>
381
+ </div>
382
+ </form>
383
+ <div class="flex justify-end">
384
+ <Button color="primary" onClick={() => setShowModal1(false)}>Save</Button>
385
+ </div>
386
+ </Modal>
387
+
388
+ <h2 class="h3">File Inputs & Calendar</h2>
389
+ <div class="grid grid-cols-3 gap-x-6 mb-4 last:mb-0">
390
+ <div>
391
+ <label for="avatar">Avatar</label>
392
+ <Drop class="is-small" name="avatar" state={state} onChange={(e) => onChange(setState, e)} awsUrl={injectedConfig.awsUrl} />
393
+ </div>
394
+ <div>
395
+ <label for="calendar">Calendar</label>
396
+ <Calendar mode="range" value={state.calendar} numberOfMonths={1}
397
+ onChange={(mode, value) => {
398
+ onChange(setState, { target: { name: 'calendar', value: value } })
399
+ }}
400
+ />
401
+ </div>
402
+ </div>
403
+
404
+ {children}
405
+ </div>
406
+ )
407
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-web",
3
- "version": "0.0.87",
3
+ "version": "0.0.89",
4
4
  "repository": "github:boycce/nitro-web",
5
5
  "homepage": "https://boycce.github.io/nitro-web/",
6
6
  "description": "Nitro is a battle-tested, modular base project to turbocharge your projects, styled using Tailwind 🚀",
@@ -24,6 +24,7 @@
24
24
  "files": [
25
25
  ".eslintrc.json",
26
26
  "client",
27
+ "components",
27
28
  "server",
28
29
  "types",
29
30
  "util.js",
@@ -16,7 +16,6 @@ declare global {
16
16
  const content: string
17
17
  export default content
18
18
  }
19
-
20
19
  }
21
20
 
22
21
  // Webpack: Twin.macro css extension