nitro-web 0.0.10 → 0.0.12
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/.eslintrc.json +4 -19
- package/_example/.env +1 -1
- package/_example/client/config.ts +2 -1
- package/_example/client/index.ts +6 -24
- package/_example/components/index.tsx +1 -1
- package/_example/package.json +0 -1
- package/_example/server/config.js +6 -7
- package/_example/tailwind.config.js +1 -1
- package/_example/tsconfig.json +5 -1
- package/_example/types.ts +1 -0
- package/client/{app.js → app.tsx} +101 -99
- package/client/globals.ts +42 -0
- package/client/index.ts +52 -0
- package/client/store.ts +31 -0
- package/components/auth/auth.api.js +3 -2
- package/components/auth/{reset.jsx → reset.tsx} +21 -23
- package/components/auth/{signin.jsx → signin.tsx} +14 -16
- package/components/auth/{signup.jsx → signup.tsx} +15 -17
- package/components/billing/stripe.api.js +2 -1
- package/components/dashboard/{dashboard.jsx → dashboard.tsx} +3 -3
- package/components/partials/element/{accordion.jsx → accordion.tsx} +21 -13
- package/components/partials/element/avatar.tsx +40 -0
- package/components/partials/element/{button.jsx → button.tsx} +20 -16
- package/components/partials/element/{dropdown.jsx → dropdown.tsx} +32 -30
- package/components/partials/element/github-link.tsx +16 -0
- package/components/partials/element/{initials.jsx → initials.tsx} +11 -2
- package/components/partials/element/{message.jsx → message.tsx} +22 -23
- package/components/partials/element/{modal.jsx → modal.tsx} +4 -3
- package/components/partials/element/{sidebar.jsx → sidebar.tsx} +14 -7
- package/components/partials/element/{tooltip.jsx → tooltip.tsx} +11 -3
- package/components/partials/element/{topbar.jsx → topbar.tsx} +9 -7
- package/components/partials/form/{checkbox.jsx → checkbox.tsx} +13 -13
- package/components/partials/form/drop-handler.tsx +68 -0
- package/components/partials/form/{drop.jsx → drop.tsx} +51 -33
- package/components/partials/form/form-error.tsx +27 -0
- package/components/partials/form/{input-color.jsx → input-color.tsx} +27 -15
- package/components/partials/form/{input-currency.jsx → input-currency.tsx} +37 -32
- package/components/partials/form/{input-date.jsx → input-date.tsx} +4 -3
- package/components/partials/form/{input.jsx → input.tsx} +35 -19
- package/components/partials/form/{location.jsx → location.tsx} +21 -8
- package/components/partials/form/{select.jsx → select.tsx} +142 -143
- package/components/partials/form/{toggle.jsx → toggle.tsx} +10 -2
- package/components/partials/{is-first-render.js → is-first-render.ts} +1 -2
- package/components/partials/layout/layout1.tsx +29 -0
- package/components/partials/layout/{layout2.jsx → layout2.tsx} +3 -3
- package/components/partials/{styleguide.jsx → styleguide.tsx} +16 -19
- package/components/settings/{settings-account.jsx → settings-account.tsx} +9 -13
- package/components/settings/{settings-business.jsx → settings-business.tsx} +7 -8
- package/components/settings/{settings-team--member.jsx → settings-team--member.tsx} +4 -11
- package/components/settings/{settings-team.jsx → settings-team.tsx} +4 -8
- package/components/settings/settings.api.js +1 -0
- package/package.json +14 -28
- package/readme.md +1 -1
- package/server/email/index.js +2 -1
- package/server/index.js +1 -0
- package/server/models/company.js +2 -1
- package/server/models/user.js +2 -1
- package/server/router.js +3 -2
- package/tsconfig.json +31 -0
- package/types/required-globals.d.ts +39 -0
- package/types/util.d.ts +12 -2
- package/types/util.d.ts.map +1 -1
- package/types.ts +43 -0
- package/util.js +14 -34
- package/webpack.config.js +23 -4
- package/_example/types/index.d.ts +0 -13
- package/_example/types/twin.d.ts +0 -19
- package/client/index.js +0 -44
- package/components/partials/element/avatar.jsx +0 -31
- package/components/partials/element/github-link.jsx +0 -14
- package/components/partials/form/drop-handler.jsx +0 -62
- package/components/partials/form/form-error.jsx +0 -21
- package/components/partials/layout/layout1.jsx +0 -38
- package/types/client/app.d.ts +0 -2
- package/types/client/app.d.ts.map +0 -1
- package/types/client/index.d.ts +0 -29
- package/types/client/index.d.ts.map +0 -1
- package/types/components/auth/reset.d.ts +0 -3
- package/types/components/auth/reset.d.ts.map +0 -1
- package/types/components/auth/signin.d.ts +0 -4
- package/types/components/auth/signin.d.ts.map +0 -1
- package/types/components/auth/signup.d.ts +0 -4
- package/types/components/auth/signup.d.ts.map +0 -1
- package/types/components/dashboard/dashboard.d.ts +0 -4
- package/types/components/dashboard/dashboard.d.ts.map +0 -1
- package/types/components/partials/element/accordion.d.ts +0 -7
- package/types/components/partials/element/accordion.d.ts.map +0 -1
- package/types/components/partials/element/avatar.d.ts +0 -8
- package/types/components/partials/element/avatar.d.ts.map +0 -1
- package/types/components/partials/element/button.d.ts +0 -11
- package/types/components/partials/element/button.d.ts.map +0 -1
- package/types/components/partials/element/dropdown.d.ts +0 -17
- package/types/components/partials/element/dropdown.d.ts.map +0 -1
- package/types/components/partials/element/initials.d.ts +0 -9
- package/types/components/partials/element/initials.d.ts.map +0 -1
- package/types/components/partials/element/message.d.ts +0 -2
- package/types/components/partials/element/message.d.ts.map +0 -1
- package/types/components/partials/element/modal.d.ts +0 -10
- package/types/components/partials/element/modal.d.ts.map +0 -1
- package/types/components/partials/element/sidebar.d.ts +0 -6
- package/types/components/partials/element/sidebar.d.ts.map +0 -1
- package/types/components/partials/element/tooltip.d.ts +0 -8
- package/types/components/partials/element/tooltip.d.ts.map +0 -1
- package/types/components/partials/element/topbar.d.ts +0 -8
- package/types/components/partials/element/topbar.d.ts.map +0 -1
- package/types/components/partials/form/checkbox.d.ts +0 -14
- package/types/components/partials/form/checkbox.d.ts.map +0 -1
- package/types/components/partials/form/drop-handler.d.ts +0 -6
- package/types/components/partials/form/drop-handler.d.ts.map +0 -1
- package/types/components/partials/form/drop.d.ts +0 -11
- package/types/components/partials/form/drop.d.ts.map +0 -1
- package/types/components/partials/form/form-error.d.ts +0 -6
- package/types/components/partials/form/form-error.d.ts.map +0 -1
- package/types/components/partials/form/input-color.d.ts +0 -10
- package/types/components/partials/form/input-color.d.ts.map +0 -1
- package/types/components/partials/form/input-currency.d.ts +0 -10
- package/types/components/partials/form/input-currency.d.ts.map +0 -1
- package/types/components/partials/form/input.d.ts +0 -9
- package/types/components/partials/form/input.d.ts.map +0 -1
- package/types/components/partials/form/location.d.ts +0 -12
- package/types/components/partials/form/location.d.ts.map +0 -1
- package/types/components/partials/form/select.d.ts +0 -27
- package/types/components/partials/form/select.d.ts.map +0 -1
- package/types/components/partials/form/toggle.d.ts +0 -9
- package/types/components/partials/form/toggle.d.ts.map +0 -1
- package/types/components/partials/is-first-render.d.ts +0 -2
- package/types/components/partials/is-first-render.d.ts.map +0 -1
- package/types/components/partials/layout/layout1.d.ts +0 -13
- package/types/components/partials/layout/layout1.d.ts.map +0 -1
- package/types/components/partials/layout/layout2.d.ts +0 -4
- package/types/components/partials/layout/layout2.d.ts.map +0 -1
- package/types/components/partials/not-found.d.ts +0 -2
- package/types/components/partials/not-found.d.ts.map +0 -1
- package/types/components/partials/styleguide.d.ts +0 -4
- package/types/components/partials/styleguide.d.ts.map +0 -1
- package/types/components/settings/settings-account.d.ts +0 -6
- package/types/components/settings/settings-account.d.ts.map +0 -1
- package/types/components/settings/settings-business.d.ts +0 -4
- package/types/components/settings/settings-business.d.ts.map +0 -1
- package/types/components/settings/settings-team--member.d.ts +0 -5
- package/types/components/settings/settings-team--member.d.ts.map +0 -1
- package/types/components/settings/settings-team.d.ts +0 -4
- package/types/components/settings/settings-team.d.ts.map +0 -1
- /package/components/partials/{not-found.jsx → not-found.tsx} +0 -0
|
@@ -1,166 +1,102 @@
|
|
|
1
|
-
/*eslint-disable*/
|
|
2
1
|
import { css } from 'twin.macro'
|
|
3
2
|
import { twMerge } from 'tailwind-merge'
|
|
4
|
-
import ReactSelect, { components, createFilter } from 'react-select'
|
|
3
|
+
import ReactSelect, { components, ControlProps, createFilter, OptionProps, SingleValueProps } from 'react-select'
|
|
4
|
+
import { ClearIndicatorProps, DropdownIndicatorProps, MultiValueRemoveProps } from 'react-select'
|
|
5
5
|
import { ChevronDownIcon, CheckCircleIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
|
6
|
-
import
|
|
6
|
+
import { util } from 'nitro-web'
|
|
7
|
+
import { Errors } from 'types'
|
|
8
|
+
|
|
7
9
|
const filterFn = createFilter()
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
error: `outline-danger`,
|
|
16
|
-
},
|
|
17
|
-
valueContainer: 'py-2 px-3 gap-1',
|
|
18
|
-
// Input container objects
|
|
19
|
-
input: {
|
|
20
|
-
base: 'text-input',
|
|
21
|
-
error: 'text-red-900',
|
|
22
|
-
},
|
|
23
|
-
multiValue: 'bg-primary text-white rounded items-center pl-2 pr-1.5 gap-1.5',
|
|
24
|
-
multiValueLabel: '',
|
|
25
|
-
multiValueRemove: `border border-primary-dark bg-white rounded-md text-dark hover:bg-red-50`,
|
|
26
|
-
placeholder: 'text-input-placeholder',
|
|
27
|
-
singleValue: {
|
|
28
|
-
base: 'text-input',
|
|
29
|
-
error: 'text-red-900',
|
|
30
|
-
},
|
|
31
|
-
// Icon indicators
|
|
32
|
-
clearIndicator: 'text-gray-500 p-1 rounded-md hover:bg-red-50 hover:text-red-800',
|
|
33
|
-
dropdownIndicator: 'p-1 hover:bg-gray-100 text-gray-500 rounded-md hover:text-black',
|
|
34
|
-
indicatorsContainer: 'p-1 px-2 gap-1',
|
|
35
|
-
indicatorSeparator: 'py-0.5 before:content-[""] before:block before:bg-gray-100 before:w-px before:h-full',
|
|
36
|
-
// Dropdown menu
|
|
37
|
-
menu: 'mt-1.5 border border-dropdown-ul-border bg-white rounded-md text-sm overflow-hidden shadow-dropdown-ul',
|
|
38
|
-
groupHeading: 'ml-3 mt-2 mb-1 text-gray-500 text-sm',
|
|
39
|
-
noOptionsMessage: 'm-1 text-gray-500 p-2 bg-gray-50 border border-dashed border-gray-200 rounded-sm',
|
|
40
|
-
option: {
|
|
41
|
-
base: 'relative px-3 py-2 !flex items-center gap-2 cursor-default',
|
|
42
|
-
hover: 'bg-gray-50',
|
|
43
|
-
selected: '!bg-gray-100 text-primary-dark',
|
|
44
|
-
}
|
|
11
|
+
type GetSelectStyle = {
|
|
12
|
+
name: string
|
|
13
|
+
isFocused?: boolean
|
|
14
|
+
isSelected?: boolean
|
|
15
|
+
hasError?: boolean
|
|
16
|
+
usePrefixes?: boolean
|
|
45
17
|
}
|
|
46
18
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
19
|
+
/** Select (all other props are passed to react-select) **/
|
|
20
|
+
type SelectProps = {
|
|
21
|
+
/** field name or path on state (used to match errors), e.g. 'date', 'company.email' **/
|
|
22
|
+
name: string
|
|
23
|
+
/** name used if not provided **/
|
|
24
|
+
inputId?: string
|
|
25
|
+
/** The minimum width of the dropdown menu **/
|
|
26
|
+
minMenuWidth?: number
|
|
27
|
+
/** The prefix to add to the input **/
|
|
28
|
+
prefix?: string
|
|
29
|
+
/** The onChange handler **/
|
|
30
|
+
onChange?: (event: { target: { id: string, value: unknown } }) => void
|
|
31
|
+
/** The options to display in the dropdown **/
|
|
32
|
+
options: { value: unknown, label: string | React.ReactNode, fixed?: boolean, [key: string]: unknown }[]
|
|
33
|
+
/** The state object to get the value and check errors from **/
|
|
34
|
+
state?: { errors: Errors, [key: string]: unknown }
|
|
35
|
+
/** Select variations **/
|
|
36
|
+
type?: 'country'|'customer'|''
|
|
37
|
+
/** All other props are passed to react-select **/
|
|
38
|
+
[key: string]: unknown
|
|
64
39
|
}
|
|
65
40
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* @param {string} [inputId] - name used if not provided
|
|
70
|
-
* @param {function} [onChange] - e.g. (event) => onInputChange(event)
|
|
71
|
-
* @param {object} [state] - object to get value from, and check errors against
|
|
72
|
-
* @param {string} [type] - speical types: 'country', 'customer', 'customer-big'
|
|
73
|
-
*
|
|
74
|
-
* react-select prop quick reference (https://react-select.com/props#api):
|
|
75
|
-
* isDisabled={false}
|
|
76
|
-
* isMulti={false}
|
|
77
|
-
* isSearchable={true}
|
|
78
|
-
* options={[{ value: 'chocolate', label: 'Chocolate' }]}
|
|
79
|
-
* placeholder="Select a color"
|
|
80
|
-
* value={options.find(o => o.code == state.color)} // to clear you need to set to null, not undefined
|
|
81
|
-
* isClearable={false}
|
|
82
|
-
* menuIsOpen={false}
|
|
83
|
-
*/
|
|
84
|
-
export function Select({ inputId, minMenuWidth, name, prefix='', onChange, options, state, type, ...props }) {
|
|
41
|
+
export function Select({ inputId, minMenuWidth, name, prefix='', onChange, options, state, type='', ...props }: SelectProps) {
|
|
42
|
+
let value: unknown
|
|
43
|
+
let hasError: { title: string, detail: string } | null = null
|
|
85
44
|
if (!name) throw new Error('Select component requires a `name` and `options` prop')
|
|
86
45
|
|
|
87
46
|
// Input is always controlled if state is passed in
|
|
88
47
|
if (props.value) {
|
|
89
|
-
|
|
48
|
+
value = props.value
|
|
90
49
|
} else if (typeof state == 'object') {
|
|
91
50
|
value = options.find(o => o.value == util.deepFind(state, name))
|
|
92
51
|
if (typeof value == 'undefined') value = ''
|
|
93
52
|
}
|
|
94
53
|
|
|
95
54
|
// An error matches this input path
|
|
96
|
-
for (
|
|
97
|
-
if (util.isRegex(name) && (item.title||'').match(name))
|
|
55
|
+
for (const item of (state?.errors || [])) {
|
|
56
|
+
if (util.isRegex(name) && (item.title||'').match(name)) hasError = item
|
|
98
57
|
else if (item.title == name) hasError = item
|
|
99
58
|
}
|
|
100
59
|
|
|
101
|
-
|
|
102
|
-
// classNames={{
|
|
103
|
-
// // *Same classes as input.jsx*
|
|
104
|
-
// // Based off https://www.jussivirtanen.fi/writing/styling-react-select-with-tailwind
|
|
105
|
-
// //
|
|
106
|
-
// // Input container
|
|
107
|
-
// control: ({ isFocused }) => `rounded-md bg-white hover:cursor-pointer text-sm sm:text-sm/6
|
|
108
|
-
// ${isFocused
|
|
109
|
-
// ? `outline outline-2 -outline-offset-2 ${error ? 'outline-danger' : 'outline-primary'}`
|
|
110
|
-
// : `outline outline-1 -outline-offset-1 ${error ? 'outline-danger' : 'outline-input-border'}`}`,
|
|
111
|
-
// valueContainer: () => 'py-2 px-3 gap-1',
|
|
112
|
-
// // Input container objects
|
|
113
|
-
// input: () => `${error ? 'text-red-900' : 'text-input'}`,
|
|
114
|
-
// multiValue: () => 'bg-primary text-white rounded items-center pl-2 pr-1.5 gap-1.5',
|
|
115
|
-
// multiValueLabel: () => '',
|
|
116
|
-
// multiValueRemove: () => `border border-primary-dark bg-white rounded-md text-dark hover:bg-red-50`,
|
|
117
|
-
// placeholder: () => 'text-input-placeholder',
|
|
118
|
-
// singleValue: () => `${error ? 'text-red-900' : 'text-input'}`,
|
|
119
|
-
// // Indicators
|
|
120
|
-
// clearIndicator: () =>'text-gray-500 p-1 rounded-md hover:bg-red-50 hover:text-red-800',
|
|
121
|
-
// dropdownIndicator: () => 'p-1 hover:bg-gray-100 text-gray-500 rounded-md hover:text-black',
|
|
122
|
-
// indicatorsContainer: () => 'p-1 px-2 gap-1',
|
|
123
|
-
// indicatorSeparator: () => 'py-0.5 before:content-[""] before:block before:bg-gray-100 before:w-px before:h-full',
|
|
124
|
-
// // Dropmenu
|
|
125
|
-
// menu: () => 'mt-1.5 border border-dropdown-ul-border bg-white rounded-md text-sm overflow-hidden shadow-dropdown-ul',
|
|
126
|
-
// groupHeading: () => 'ml-3 mt-2 mb-1 text-gray-500 text-sm',
|
|
127
|
-
// noOptionsMessage: () => 'm-1 text-gray-500 p-2 bg-gray-50 border border-dashed border-gray-200 rounded-sm',
|
|
128
|
-
// option: ({ isFocused, isSelected }) => `hover:cursor-pointer px-3 py-2 !flex items-center gap-2
|
|
129
|
-
// ${isFocused ? 'bg-gray-50 active:bg-gray-200' : ''}
|
|
130
|
-
// ${isSelected ? 'bg-gray-100 text-primary-dark' : ''}`,
|
|
131
|
-
// }}
|
|
132
|
-
|
|
133
|
-
|
|
134
60
|
return (
|
|
135
61
|
<div css={style} class="mt-input-before mb-input-after">
|
|
136
62
|
<ReactSelect
|
|
63
|
+
/**
|
|
64
|
+
* react-select prop quick reference (https://react-select.com/props#api):
|
|
65
|
+
* isDisabled={false}
|
|
66
|
+
* isMulti={false}
|
|
67
|
+
* isSearchable={true}
|
|
68
|
+
* options={[{ value: 'chocolate', label: 'Chocolate' }]}
|
|
69
|
+
* placeholder="Select a color"
|
|
70
|
+
* value={options.find(o => o.code == state.color)} // to clear you need to set to null, not undefined
|
|
71
|
+
* isClearable={false}
|
|
72
|
+
* menuIsOpen={false}
|
|
73
|
+
*/
|
|
137
74
|
{...props}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
key={value}
|
|
75
|
+
// @ts-expect-error
|
|
76
|
+
_nitro={{ prefix, type }}
|
|
77
|
+
key={value as string}
|
|
142
78
|
unstyled={true}
|
|
143
79
|
inputId={inputId || name}
|
|
144
80
|
filterOption={(option, searchText) => {
|
|
145
|
-
if (option.data
|
|
81
|
+
if ((option.data as {fixed?: boolean}).fixed) return true
|
|
146
82
|
return filterFn(option, searchText)
|
|
147
83
|
}}
|
|
148
84
|
menuPlacement="auto"
|
|
149
85
|
minMenuHeight={250}
|
|
150
|
-
onChange={!onChange ? undefined : (o) => onChange({ target: { id: inputId || name, value: o?.value || o }})}
|
|
86
|
+
onChange={!onChange ? undefined : (o) => onChange({ target: { id: inputId || name, value: (o as {value?: unknown})?.value || o }})}
|
|
151
87
|
options={options}
|
|
152
88
|
value={value}
|
|
153
89
|
classNames={{
|
|
154
90
|
// Input container
|
|
155
|
-
control: (p) => getSelectStyle({ name: 'control', hasError, ...p }),
|
|
91
|
+
control: (p) => getSelectStyle({ name: 'control', hasError: !!hasError, ...p }),
|
|
156
92
|
valueContainer: () => getSelectStyle({ name: 'valueContainer' }),
|
|
157
93
|
// Input container objects
|
|
158
|
-
input: () => getSelectStyle({ name: 'input', hasError }),
|
|
94
|
+
input: () => getSelectStyle({ name: 'input', hasError: !!hasError }),
|
|
159
95
|
multiValue: () => getSelectStyle({ name: 'multiValue' }),
|
|
160
96
|
multiValueLabel: () => '',
|
|
161
97
|
multiValueRemove: () => getSelectStyle({ name: 'multiValueRemove' }),
|
|
162
98
|
placeholder: () => getSelectStyle({ name: 'placeholder' }),
|
|
163
|
-
singleValue: () => getSelectStyle({ name: 'singleValue', hasError }),
|
|
99
|
+
singleValue: () => getSelectStyle({ name: 'singleValue', hasError: !!hasError }),
|
|
164
100
|
// Indicators
|
|
165
101
|
clearIndicator: () => getSelectStyle({ name: 'clearIndicator' }),
|
|
166
102
|
dropdownIndicator: () => getSelectStyle({ name: 'dropdownIndicator' }),
|
|
@@ -176,23 +112,25 @@ export function Select({ inputId, minMenuWidth, name, prefix='', onChange, optio
|
|
|
176
112
|
Control,
|
|
177
113
|
SingleValue,
|
|
178
114
|
Option,
|
|
179
|
-
DropdownIndicator,
|
|
115
|
+
DropdownIndicator,
|
|
116
|
+
ClearIndicator,
|
|
117
|
+
MultiValueRemove,
|
|
180
118
|
}}
|
|
181
119
|
styles={{
|
|
182
120
|
menu: (base) => ({
|
|
183
|
-
...base, minWidth: minMenuWidth
|
|
121
|
+
...base, minWidth: minMenuWidth,
|
|
184
122
|
}),
|
|
185
123
|
// On mobile, the label will truncate automatically, so we want to
|
|
186
124
|
// override that behaviour.
|
|
187
125
|
multiValueLabel: (base) => ({
|
|
188
126
|
...base,
|
|
189
|
-
whiteSpace:
|
|
190
|
-
overflow:
|
|
127
|
+
whiteSpace: 'normal',
|
|
128
|
+
overflow: 'visible',
|
|
191
129
|
}),
|
|
192
130
|
control: (base) => ({
|
|
193
131
|
...base,
|
|
194
132
|
outline: undefined,
|
|
195
|
-
transition:
|
|
133
|
+
transition: 'none',
|
|
196
134
|
}),
|
|
197
135
|
}}
|
|
198
136
|
// menuIsOpen={true}
|
|
@@ -207,25 +145,27 @@ export function Select({ inputId, minMenuWidth, name, prefix='', onChange, optio
|
|
|
207
145
|
)
|
|
208
146
|
}
|
|
209
147
|
|
|
210
|
-
function Control({ children, ...props }) {
|
|
148
|
+
function Control({ children, ...props }: ControlProps) {
|
|
211
149
|
// Add flag and prefix to the input (control)
|
|
212
150
|
// todo: check that the flag/prefix looks okay
|
|
213
|
-
const selectedOption = props.getValue()[0]
|
|
151
|
+
const selectedOption = props.getValue()[0]
|
|
152
|
+
const optionFlag = (selectedOption as { flag?: string })?.flag
|
|
153
|
+
const _nitro = (props.selectProps as { _nitro?: { prefix?: string, type?: string } })?._nitro
|
|
214
154
|
return (
|
|
215
155
|
<components.Control {...props}>
|
|
216
156
|
{
|
|
217
157
|
(() => {
|
|
218
|
-
if (
|
|
158
|
+
if (_nitro?.prefix) {
|
|
219
159
|
return (
|
|
220
160
|
<>
|
|
221
|
-
<span class="relative right-[2px]">{
|
|
161
|
+
<span class="relative right-[2px]">{_nitro?.prefix}</span>
|
|
222
162
|
{children}
|
|
223
163
|
</>
|
|
224
164
|
)
|
|
225
|
-
} else if (
|
|
165
|
+
} else if (_nitro?.type == 'country') {
|
|
226
166
|
return (
|
|
227
167
|
<>
|
|
228
|
-
<Flag flag={
|
|
168
|
+
{ optionFlag && <Flag flag={optionFlag} /> }
|
|
229
169
|
{children}
|
|
230
170
|
</>
|
|
231
171
|
)
|
|
@@ -238,27 +178,29 @@ function Control({ children, ...props }) {
|
|
|
238
178
|
)
|
|
239
179
|
}
|
|
240
180
|
|
|
241
|
-
function SingleValue(props) {
|
|
242
|
-
const selectedOption = props.getValue()[0]
|
|
181
|
+
function SingleValue(props: SingleValueProps) {
|
|
182
|
+
const selectedOption = props.getValue()[0] as { labelControl?: string }
|
|
243
183
|
return (
|
|
244
184
|
<components.SingleValue {...props}>
|
|
245
|
-
<>{selectedOption
|
|
185
|
+
<>{selectedOption?.labelControl || props.children}</>
|
|
246
186
|
</components.SingleValue>
|
|
247
187
|
)
|
|
248
188
|
}
|
|
249
189
|
|
|
250
|
-
function Option(props) {
|
|
190
|
+
function Option(props: OptionProps) {
|
|
251
191
|
// todo: check that the flag looks okay
|
|
192
|
+
const data = props.data as { className?: string, flag?: string }
|
|
193
|
+
const _nitro = (props.selectProps as { _nitro?: { type?: string } })?._nitro
|
|
252
194
|
return (
|
|
253
|
-
<components.Option className={
|
|
254
|
-
{
|
|
195
|
+
<components.Option className={data.className} {...props}>
|
|
196
|
+
{ _nitro?.type == 'country' && <Flag flag={data.flag} /> }
|
|
255
197
|
<span class="flex-auto">{props.label}</span>
|
|
256
198
|
{props.isSelected && <CheckCircleIcon className="size-[22px] text-primary -my-1 -mx-1" />}
|
|
257
199
|
</components.Option>
|
|
258
200
|
)
|
|
259
201
|
}
|
|
260
202
|
|
|
261
|
-
const DropdownIndicator = (props) => {
|
|
203
|
+
const DropdownIndicator = (props: DropdownIndicatorProps) => {
|
|
262
204
|
return (
|
|
263
205
|
<components.DropdownIndicator {...props}>
|
|
264
206
|
<ChevronDownIcon className="size-6 -my-0.5 -mx-1" />
|
|
@@ -266,7 +208,7 @@ const DropdownIndicator = (props) => {
|
|
|
266
208
|
)
|
|
267
209
|
}
|
|
268
210
|
|
|
269
|
-
const ClearIndicator = (props) => {
|
|
211
|
+
const ClearIndicator = (props: ClearIndicatorProps) => {
|
|
270
212
|
return (
|
|
271
213
|
<components.ClearIndicator {...props}>
|
|
272
214
|
<XMarkIcon className="size-4 my-0.5" />
|
|
@@ -274,7 +216,7 @@ const ClearIndicator = (props) => {
|
|
|
274
216
|
)
|
|
275
217
|
}
|
|
276
218
|
|
|
277
|
-
const MultiValueRemove = (props) => {
|
|
219
|
+
const MultiValueRemove = (props: MultiValueRemoveProps) => {
|
|
278
220
|
return (
|
|
279
221
|
<components.MultiValueRemove {...props}>
|
|
280
222
|
<XMarkIcon className="size-4 p-[1px]" />
|
|
@@ -282,8 +224,7 @@ const MultiValueRemove = (props) => {
|
|
|
282
224
|
)
|
|
283
225
|
}
|
|
284
226
|
|
|
285
|
-
|
|
286
|
-
function Flag({ flag }) {
|
|
227
|
+
function Flag({ flag }: { flag?: string }) {
|
|
287
228
|
if (!flag) return null
|
|
288
229
|
// todo: public needs to come from webpack
|
|
289
230
|
const publicPath = '/'
|
|
@@ -292,7 +233,65 @@ function Flag({ flag }) {
|
|
|
292
233
|
)
|
|
293
234
|
}
|
|
294
235
|
|
|
295
|
-
const
|
|
236
|
+
const selectStyles = {
|
|
237
|
+
// Based off https://www.jussivirtanen.fi/writing/styling-react-select-with-tailwind
|
|
238
|
+
// Input container
|
|
239
|
+
control: {
|
|
240
|
+
base: 'rounded-md bg-white hover:cursor-pointer text-sm sm:text-sm/6 outline outline-1 -outline-offset-1 outline-input-border',
|
|
241
|
+
focus: 'outline-2 -outline-offset-2 outline-primary',
|
|
242
|
+
error: 'outline-danger',
|
|
243
|
+
},
|
|
244
|
+
valueContainer: 'py-2 px-3 gap-1',
|
|
245
|
+
// Input container objects
|
|
246
|
+
input: {
|
|
247
|
+
base: 'text-input',
|
|
248
|
+
error: 'text-red-900',
|
|
249
|
+
},
|
|
250
|
+
multiValue: 'bg-primary text-white rounded items-center pl-2 pr-1.5 gap-1.5',
|
|
251
|
+
multiValueLabel: '',
|
|
252
|
+
multiValueRemove: 'border border-primary-dark bg-white rounded-md text-dark hover:bg-red-50',
|
|
253
|
+
placeholder: 'text-input-placeholder',
|
|
254
|
+
singleValue: {
|
|
255
|
+
base: 'text-input',
|
|
256
|
+
error: 'text-red-900',
|
|
257
|
+
},
|
|
258
|
+
// Icon indicators
|
|
259
|
+
clearIndicator: 'text-gray-500 p-1 rounded-md hover:bg-red-50 hover:text-red-800',
|
|
260
|
+
dropdownIndicator: 'p-1 hover:bg-gray-100 text-gray-500 rounded-md hover:text-black',
|
|
261
|
+
indicatorsContainer: 'p-1 px-2 gap-1',
|
|
262
|
+
indicatorSeparator: 'py-0.5 before:content-[""] before:block before:bg-gray-100 before:w-px before:h-full',
|
|
263
|
+
// Dropdown menu
|
|
264
|
+
menu: 'mt-1.5 border border-dropdown-ul-border bg-white rounded-md text-sm overflow-hidden shadow-dropdown-ul',
|
|
265
|
+
groupHeading: 'ml-3 mt-2 mb-1 text-gray-500 text-sm',
|
|
266
|
+
noOptionsMessage: 'm-1 text-gray-500 p-2 bg-gray-50 border border-dashed border-gray-200 rounded-sm',
|
|
267
|
+
option: {
|
|
268
|
+
base: 'relative px-3 py-2 !flex items-center gap-2 cursor-default',
|
|
269
|
+
hover: 'bg-gray-50',
|
|
270
|
+
selected: '!bg-gray-100 text-primary-dark',
|
|
271
|
+
},
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function getSelectStyle({ name, isFocused, isSelected, hasError, usePrefixes }: GetSelectStyle) {
|
|
275
|
+
// Returns a class list that conditionally includes hover/focus modifier classes, or uses CSS modifiers, e.g. hover:, focus:
|
|
276
|
+
// @ts-expect-error
|
|
277
|
+
const obj = selectStyles[name]
|
|
278
|
+
let output = obj?.base
|
|
279
|
+
if (typeof obj == 'string') return obj // no modifiers
|
|
280
|
+
|
|
281
|
+
if (usePrefixes) {
|
|
282
|
+
if (obj.focus) output += ' ' + obj.focus.split(' ').map((part: string) => `focus:${part}`).join(' ')
|
|
283
|
+
if (obj.hover) output += ' ' + obj.hover.split(' ').map((part: string) => `hover:${part}`).join(' ')
|
|
284
|
+
} else {
|
|
285
|
+
if (obj.focus && isFocused) output += ` ${obj.focus}`
|
|
286
|
+
if (obj.hover && isFocused) output += ` ${obj.hover}`
|
|
287
|
+
}
|
|
288
|
+
if (obj.error && hasError) output += ` ${obj.error}`
|
|
289
|
+
if (obj.selected && isSelected) output += ` ${obj.selected}`
|
|
290
|
+
|
|
291
|
+
return twMerge(output)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const style = css`
|
|
296
295
|
/*
|
|
297
296
|
todo: add these as tailwind classes
|
|
298
297
|
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
type ToggleProps = {
|
|
2
|
+
className?: string
|
|
3
|
+
id?: string
|
|
4
|
+
name: string
|
|
5
|
+
subtext?: string
|
|
6
|
+
text?: string
|
|
7
|
+
type?: 'checkbox'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Toggle({ name, id, subtext, text, type='checkbox', ...props }: ToggleProps) {
|
|
3
11
|
id = id || name
|
|
4
12
|
// https://tailwindui.com/components/application-ui/forms/checkboxes#component-744ed4fa65ba36b925701eb4da5c6e31
|
|
5
13
|
return (
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export function IsFirstRender(delay) {
|
|
1
|
+
export function IsFirstRender(delay?: number) {
|
|
2
2
|
/*
|
|
3
3
|
* Checks if the current render of a react component is the first
|
|
4
4
|
* E.g. const isFirst = isFirstRender()
|
|
5
|
-
* @param {boolean} delay
|
|
6
5
|
* @link https://stackoverflow.com/a/56267719/1900648
|
|
7
6
|
* @return boolean
|
|
8
7
|
*/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { css } from 'twin.macro'
|
|
2
|
+
import { Outlet } from 'react-router-dom'
|
|
3
|
+
import { Message, Sidebar, SidebarProps } from 'nitro-web'
|
|
4
|
+
|
|
5
|
+
export function Layout1({ Logo, menu, links }: SidebarProps) {
|
|
6
|
+
// Dashboard, app screens (only the <Outlet/> receives `params` and `location`)
|
|
7
|
+
return (
|
|
8
|
+
<div css={style} class="bg-[#F3F3F3]">
|
|
9
|
+
<Message />
|
|
10
|
+
<div class="flex-1">
|
|
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} />
|
|
13
|
+
<div class="py-10 px-14 flex-1">
|
|
14
|
+
<Outlet />
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const style = css`
|
|
23
|
+
.wrapper {
|
|
24
|
+
position: relative;
|
|
25
|
+
max-width: 1800px;
|
|
26
|
+
margin: 0 auto;
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { css } from 'twin.macro'
|
|
2
2
|
import { Outlet } from 'react-router-dom'
|
|
3
|
-
import { Message } from '
|
|
3
|
+
import { Message } from 'nitro-web'
|
|
4
4
|
|
|
5
5
|
// Signin, reset password, etc
|
|
6
|
-
export function Layout2({ Logo }) {
|
|
6
|
+
export function Layout2({ Logo }: { Logo: React.ComponentType<{ alt: string; width: string }> }) {
|
|
7
7
|
return (
|
|
8
8
|
<div css={style} class="bg-[#F3F3F3]">
|
|
9
9
|
<Message />
|
|
@@ -33,7 +33,7 @@ export function Layout2({ Logo }) {
|
|
|
33
33
|
)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const style =
|
|
36
|
+
const style = css`
|
|
37
37
|
.wrapper {
|
|
38
38
|
position: relative;
|
|
39
39
|
max-width: 1800px;
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Drop, Dropdown, Input, Select, Button, Checkbox, GithubLink } from 'nitro-web'
|
|
2
|
+
import { getCountryOptions, getCurrencyOptions, ucFirst } from 'nitro-web/util'
|
|
2
3
|
import { CheckIcon } from '@heroicons/react/20/solid'
|
|
3
|
-
import {
|
|
4
|
-
import { Drop } from './form/drop.jsx'
|
|
5
|
-
import { Select } from './form/select.jsx'
|
|
6
|
-
import { Dropdown } from './element/dropdown.jsx'
|
|
7
|
-
import { Button } from './element/button.jsx'
|
|
8
|
-
import { Checkbox } from './form/checkbox.jsx'
|
|
9
|
-
import { GithubLink } from './element/github-link.jsx'
|
|
4
|
+
import { Config } from 'types'
|
|
10
5
|
|
|
11
|
-
export function Styleguide({ config }) {
|
|
6
|
+
export function Styleguide({ config }: { config: Config }) {
|
|
12
7
|
const [customerSearch, setCustomerSearch] = useState('')
|
|
13
8
|
const [state, setState] = useState({
|
|
14
9
|
address: '',
|
|
@@ -41,7 +36,7 @@ export function Styleguide({ config }) {
|
|
|
41
36
|
{ label: 'Delete' },
|
|
42
37
|
]
|
|
43
38
|
|
|
44
|
-
function onInputChange (e) {
|
|
39
|
+
function onInputChange (e: { target: { id: string, value: unknown } }) {
|
|
45
40
|
if ((e.target.id == 'customer' || e.target.id == 'customer2') && e.target.value == '') {
|
|
46
41
|
setCustomerSearch('')
|
|
47
42
|
e.target.value = null // clear the selected value
|
|
@@ -49,7 +44,7 @@ export function Styleguide({ config }) {
|
|
|
49
44
|
setState(s => ({ ...s, [e.target.id]: e.target.value }))
|
|
50
45
|
}
|
|
51
46
|
|
|
52
|
-
function onCustomerSearch (search) {
|
|
47
|
+
function onCustomerSearch (search: string) {
|
|
53
48
|
setCustomerSearch(search || '')
|
|
54
49
|
}
|
|
55
50
|
|
|
@@ -114,10 +109,10 @@ export function Styleguide({ config }) {
|
|
|
114
109
|
<div><Button color="primary">primary (default)</Button></div>
|
|
115
110
|
<div><Button color="secondary">secondary button</Button></div>
|
|
116
111
|
<div><Button color="white">white button</Button></div>
|
|
117
|
-
<div><Button color="primary
|
|
118
|
-
<div><Button color="primary
|
|
119
|
-
<div><Button color="primary
|
|
120
|
-
<div><Button color="primary
|
|
112
|
+
<div><Button color="primary" size="xs">*-xs button</Button></div>
|
|
113
|
+
<div><Button color="primary" size="sm">*-sm button</Button></div>
|
|
114
|
+
<div><Button color="primary">*-md (default)</Button></div>
|
|
115
|
+
<div><Button color="primary" size="lg">*-lg button</Button></div>
|
|
121
116
|
<div><Button IconLeft={<CheckIcon class="size-5 -my-5 -mx-0.5" />}>IconLeft=Element</Button></div>
|
|
122
117
|
<div><Button IconRight="v">IconRight="v"</Button></div>
|
|
123
118
|
<div><Button IconRight2="v" className="w-[200px]">IconRight2="v"</Button></div>
|
|
@@ -176,10 +171,12 @@ export function Styleguide({ config }) {
|
|
|
176
171
|
className: 'bb',
|
|
177
172
|
fixed: true,
|
|
178
173
|
value: '',
|
|
179
|
-
label:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
174
|
+
label: (
|
|
175
|
+
<>
|
|
176
|
+
<b>New Customer</b>
|
|
177
|
+
{customerSearch ? <> / Add <b>{ucFirst(customerSearch)}</b></> : ''}
|
|
178
|
+
</>
|
|
179
|
+
),
|
|
183
180
|
},
|
|
184
181
|
{ value: '1', label: 'Iron Man Industries' },
|
|
185
182
|
{ value: '2', label: 'Captain America' },
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
// todo: finish tailwind conversion
|
|
2
|
-
import * as util from '
|
|
3
|
-
import SvgTick from '
|
|
4
|
-
import { Button } from '
|
|
5
|
-
import { FormError } from '../partials/form/form-error.jsx'
|
|
6
|
-
import { Input } from '../partials/form/input.jsx'
|
|
7
|
-
import { Modal } from '../partials/element/modal.jsx'
|
|
8
|
-
import { Topbar } from '../partials/element/topbar.jsx'
|
|
9
|
-
import { Tabbar } from '../partials/element/tabbar.jsx'
|
|
3
|
+
import * as util from 'nitro-web/util'
|
|
4
|
+
import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
|
|
5
|
+
import { Button, FormError, Input, Modal, Topbar, Tabbar } from 'nitro-web'
|
|
10
6
|
|
|
11
7
|
export function SettingsAccount() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
const isLoading = useState('')
|
|
9
|
+
const [removeModal, setRemoveModal] = useState()
|
|
10
|
+
const [{user}, setStore] = sharedStore.useTracked()
|
|
11
|
+
const [state, setState] = useState({
|
|
16
12
|
avatar: user.avatar || '',
|
|
17
13
|
email: user.email || '',
|
|
18
14
|
firstName: user.firstName || '',
|
|
@@ -131,7 +127,7 @@ export function RemoveModal ({ show, setShow }) {
|
|
|
131
127
|
}
|
|
132
128
|
|
|
133
129
|
import { css } from 'twin.macro'
|
|
134
|
-
const style =
|
|
130
|
+
const style = css`
|
|
135
131
|
/* input[type='file'] {
|
|
136
132
|
padding: 8px 18px;
|
|
137
133
|
font-size: 12px;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import { Topbar } from '
|
|
8
|
-
import { Tabbar } from '../partials/element/tabbar.jsx'
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// todo: finish tailwind conversio
|
|
3
|
+
|
|
4
|
+
////// look at the select type error below
|
|
5
|
+
import * as util from 'nitro-web/util'
|
|
6
|
+
import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
|
|
7
|
+
import { Button, Input, Select, Topbar, Tabbar } from 'nitro-web'
|
|
9
8
|
|
|
10
9
|
export function SettingsBusiness({ config }) {
|
|
11
10
|
const isLoading = useState('')
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
// todo: finish tailwind conversion
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import { Modal } from '../partials/element/modal.jsx'
|
|
5
|
-
import { FormError } from '../partials/form/form-error.jsx'
|
|
6
|
-
import { Input } from '../partials/form/input.jsx'
|
|
7
|
-
import { Select } from '../partials/form/select.jsx'
|
|
8
|
-
import SvgTick from '../../client/imgs/icons/tick.svg'
|
|
3
|
+
import { Button, FormError, Input, Modal, Select } from 'nitro-web'
|
|
4
|
+
import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
|
|
9
5
|
|
|
10
6
|
export function SettingsTeamMember ({ showModal, setShowModal }) {
|
|
11
7
|
// @param {object} showModal - user
|
|
@@ -29,7 +25,7 @@ export function SettingsTeamMember ({ showModal, setShowModal }) {
|
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
return (
|
|
32
|
-
<Modal show={showModal} setShow={setShowModal}
|
|
28
|
+
<Modal show={showModal} setShow={setShowModal} class="p-modal">
|
|
33
29
|
|
|
34
30
|
<h2 class="h2"><em>Add</em> Team Member</h2>
|
|
35
31
|
<p class="subtitle">Invite a new team member to collaborate with you on Nitro.</p>
|
|
@@ -103,6 +99,3 @@ export function SettingsTeamMember ({ showModal, setShowModal }) {
|
|
|
103
99
|
</Modal>
|
|
104
100
|
)
|
|
105
101
|
}
|
|
106
|
-
|
|
107
|
-
const style = (_theme) => css`
|
|
108
|
-
`
|