nitro-web 0.0.16 → 0.0.18
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/_example/client/css/index.css +1 -1
- package/_example/client/index.ts +1 -2
- package/_example/tailwind.config.js +14 -14
- package/client/index.ts +6 -5
- package/components/auth/reset.tsx +4 -4
- package/components/auth/signin.tsx +3 -3
- package/components/auth/signup.tsx +5 -5
- package/components/partials/element/button.tsx +4 -3
- package/components/partials/element/calendar.tsx +123 -0
- package/components/partials/element/dropdown.tsx +6 -2
- package/components/partials/element/modal.tsx +54 -196
- package/components/partials/element/sidebar.tsx +2 -2
- package/components/partials/form/checkbox.tsx +1 -1
- package/components/partials/form/input-color.tsx +21 -20
- package/components/partials/form/input-currency.tsx +51 -35
- package/components/partials/form/input-date.tsx +137 -166
- package/components/partials/form/input.tsx +123 -92
- package/components/partials/form/select.tsx +4 -4
- package/components/partials/styleguide.tsx +89 -42
- package/components/settings/settings-account.tsx +6 -6
- package/components/settings/settings-business.tsx +12 -12
- package/components/settings/settings-team--member.tsx +8 -8
- package/package.json +8 -4
- package/readme.md +11 -7
- package/types/util.d.ts +19 -10
- package/types/util.d.ts.map +1 -1
- package/types.ts +5 -3
- package/util.js +22 -14
|
@@ -1,134 +1,165 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
1
|
import { css } from 'twin.macro'
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { twMerge } from 'tailwind-merge'
|
|
3
|
+
import { util, FieldCurrency, FieldCurrencyProps, FieldColor, FieldColorProps, FieldDate, FieldDateProps } from 'nitro-web'
|
|
4
|
+
import { Errors, type Error } from 'types'
|
|
5
5
|
import {
|
|
6
6
|
EnvelopeIcon,
|
|
7
|
-
|
|
7
|
+
CalendarIcon,
|
|
8
8
|
FunnelIcon,
|
|
9
9
|
MagnifyingGlassIcon,
|
|
10
10
|
EyeIcon,
|
|
11
11
|
EyeSlashIcon,
|
|
12
12
|
} from '@heroicons/react/20/solid'
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
type InputProps = React.InputHTMLAttributes<HTMLInputElement>
|
|
15
|
+
type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>
|
|
16
|
+
type FieldExtraProps = {
|
|
17
|
+
// field name or path on state (used to match errors), e.g. 'date', 'company.email'
|
|
15
18
|
name: string
|
|
16
|
-
state?: any
|
|
17
19
|
id?: string
|
|
18
|
-
|
|
19
|
-
[key: string]:
|
|
20
|
+
// state object to get the value, and check errors against
|
|
21
|
+
state?: { errors: Errors, [key: string]: unknown }
|
|
22
|
+
type?: 'text' | 'password' | 'email' | 'filter' | 'search' | 'textarea' | 'currency' | 'date' | 'color'
|
|
23
|
+
icon?: React.ReactNode
|
|
24
|
+
iconPos?: 'left' | 'right'
|
|
25
|
+
}
|
|
26
|
+
type IconWrapperProps = {
|
|
27
|
+
iconPos: string
|
|
28
|
+
icon?: React.ReactNode
|
|
29
|
+
[key: string]: unknown
|
|
20
30
|
}
|
|
31
|
+
// Discriminated union (https://stackoverflow.com/a/77351290/1900648)
|
|
32
|
+
export type FieldProps = (
|
|
33
|
+
| ({ type?: 'text' | 'password' | 'email' | 'filter' | 'search' } & InputProps & FieldExtraProps)
|
|
34
|
+
| ({ type: 'textarea' } & TextareaProps & FieldExtraProps)
|
|
35
|
+
| ({ type: 'currency' } & FieldCurrencyProps & FieldExtraProps)
|
|
36
|
+
| ({ type: 'color' } & FieldColorProps & FieldExtraProps)
|
|
37
|
+
| ({ type: 'date' } & FieldDateProps & FieldExtraProps)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
export function Field({ state, icon, iconPos: ip, ...props }: FieldProps) {
|
|
41
|
+
// type must be kept as props.type for TS to be happy and follow the conditions below
|
|
42
|
+
let error!: Error
|
|
43
|
+
let value!: string
|
|
44
|
+
let Icon!: React.ReactNode
|
|
45
|
+
const type = props.type
|
|
46
|
+
const iconPos = ip == 'left' || (type == 'color' && !ip) ? 'left' : 'right'
|
|
21
47
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* @param {string} [id] - not required, name used if not provided
|
|
28
|
-
* @param {('password'|'email'|'text'|'date'|'filter'|'search'|'color'|'textarea'|'currency')} [type='text']
|
|
29
|
-
*/
|
|
30
|
-
let IconSvg: React.ReactNode
|
|
31
|
-
let onClick: () => void
|
|
32
|
-
let iconDir = 'right'
|
|
33
|
-
let InputEl = 'input'
|
|
48
|
+
if (!props.name) {
|
|
49
|
+
throw new Error('Input component requires a `name` prop')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Input type
|
|
34
53
|
const [inputType, setInputType] = useState(() => { // eslint-disable-line
|
|
35
|
-
return type == 'password' ? 'password' : (type == 'textarea' ?
|
|
54
|
+
return type == 'password' ? 'password' : (type == 'textarea' ? 'textarea' : 'text')
|
|
36
55
|
})
|
|
37
|
-
|
|
38
|
-
if (!name) throw new Error('Input component requires a `name` prop')
|
|
39
56
|
|
|
40
|
-
// Input is always controlled if state is passed in
|
|
41
|
-
if (props.value)
|
|
42
|
-
|
|
43
|
-
} else if (typeof state == 'object') {
|
|
44
|
-
value = util.deepFind(state, name)
|
|
45
|
-
if (typeof value == 'undefined') value = ''
|
|
46
|
-
}
|
|
57
|
+
// Value: Input is always controlled if state is passed in
|
|
58
|
+
if (props.value) value = props.value as string
|
|
59
|
+
else if (typeof state == 'object') value = util.deepFind(state, props.name) ?? ''
|
|
47
60
|
|
|
48
|
-
//
|
|
61
|
+
// Errors: find any that match this input path
|
|
49
62
|
for (const item of (state?.errors || [])) {
|
|
50
|
-
if (util.isRegex(name) && (item.title||'').match(name))
|
|
51
|
-
else if (item.title == name) error = item
|
|
63
|
+
if (util.isRegex(props.name) && (item.title || '').match(props.name)) error = item
|
|
64
|
+
else if (item.title == props.name) error = item
|
|
52
65
|
}
|
|
53
66
|
|
|
54
|
-
//
|
|
67
|
+
// Icon
|
|
55
68
|
if (type == 'password') {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
Icon = <IconWrapper
|
|
70
|
+
iconPos={iconPos}
|
|
71
|
+
icon={icon || inputType == 'password' ? <EyeSlashIcon /> : <EyeIcon />}
|
|
72
|
+
onClick={() => setInputType(o => o == 'password' ? 'text' : 'password')}
|
|
73
|
+
className="pointer-events-auto"
|
|
74
|
+
/>
|
|
75
|
+
} else if (type == 'email') {
|
|
76
|
+
Icon = <IconWrapper iconPos={iconPos} icon={icon || <EnvelopeIcon />} />
|
|
77
|
+
} else if (type == 'filter') {
|
|
78
|
+
Icon = <IconWrapper iconPos={iconPos} icon={icon || <FunnelIcon />} className="size-3" />
|
|
79
|
+
} else if (type == 'search') {
|
|
80
|
+
Icon = <IconWrapper iconPos={iconPos} icon={icon || <MagnifyingGlassIcon />} className="size-4" />
|
|
67
81
|
} else if (type == 'color') {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
} else
|
|
72
|
-
|
|
73
|
-
} else if (type == 'currency') {
|
|
74
|
-
if (!props.config) throw new Error('Input: `config` is required when type=currency')
|
|
75
|
-
InputEl = InputCurrency
|
|
82
|
+
Icon = <IconWrapper iconPos={iconPos} icon={icon || <ColorSvg hex={value}/>} className="size-[17px]" />
|
|
83
|
+
} else if (type == 'date') {
|
|
84
|
+
Icon = <IconWrapper iconPos={iconPos} icon={icon || <CalendarIcon />} className="size-4" />
|
|
85
|
+
} else {
|
|
86
|
+
Icon = <IconWrapper iconPos={iconPos} icon={icon} />
|
|
76
87
|
}
|
|
77
88
|
|
|
78
|
-
//
|
|
79
|
-
const
|
|
89
|
+
// Classname
|
|
90
|
+
const inputClassName = getInputClasses({ error, Icon, iconPos, type })
|
|
91
|
+
const commonProps = { id: props.name || props.id, value: value, className: inputClassName }
|
|
80
92
|
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
// Type has to be referenced as props.type for TS to be happy
|
|
94
|
+
if (!type || type == 'text' || type == 'password' || type == 'email' || type == 'filter' || type == 'search') {
|
|
95
|
+
return (
|
|
96
|
+
<FieldContainer error={error} className={props.className}>
|
|
97
|
+
{Icon}<input {...props} {...commonProps} type={inputType} />
|
|
98
|
+
</FieldContainer>
|
|
99
|
+
)
|
|
100
|
+
} else if (type == 'textarea') {
|
|
101
|
+
return (
|
|
102
|
+
<FieldContainer error={error} className={props.className}>
|
|
103
|
+
{Icon}<textarea {...props} {...commonProps} />
|
|
104
|
+
</FieldContainer>
|
|
105
|
+
)
|
|
106
|
+
} else if (type == 'currency') {
|
|
107
|
+
return (
|
|
108
|
+
<FieldContainer error={error} className={props.className}>
|
|
109
|
+
{Icon}<FieldCurrency {...props} {...commonProps} />
|
|
110
|
+
</FieldContainer>
|
|
111
|
+
)
|
|
112
|
+
} else if (type == 'color') {
|
|
113
|
+
return (
|
|
114
|
+
<FieldContainer error={error} className={props.className}>
|
|
115
|
+
<FieldColor {...props} {...commonProps} Icon={Icon} />
|
|
116
|
+
</FieldContainer>
|
|
117
|
+
)
|
|
118
|
+
} else if (type == 'date') {
|
|
119
|
+
return (
|
|
120
|
+
<FieldContainer error={error} className={props.className}>
|
|
121
|
+
<FieldDate {...props} {...commonProps} Icon={Icon} />
|
|
122
|
+
</FieldContainer>
|
|
123
|
+
)
|
|
95
124
|
}
|
|
125
|
+
}
|
|
96
126
|
|
|
97
|
-
|
|
98
|
-
if (!['color', 'date'].includes(type)) delete inputProps.iconEl
|
|
99
|
-
|
|
127
|
+
function FieldContainer({ children, className, error }: { children: React.ReactNode, className?: string, error?: Error }) {
|
|
100
128
|
return (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
{ !inputProps.iconEl && iconEl }
|
|
104
|
-
<InputEl {...inputProps} />
|
|
129
|
+
<div css={style} className={`mt-input-before mb-input-after grid grid-cols-1 ${className || ''}`}>
|
|
130
|
+
{children}
|
|
105
131
|
{error && <div class="mt-1.5 text-xs text-danger">{error.detail}</div>}
|
|
106
132
|
</div>
|
|
107
133
|
)
|
|
108
134
|
}
|
|
109
135
|
|
|
110
|
-
type
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
136
|
+
function getInputClasses({ error, Icon, iconPos, type }: { error: Error, Icon?: React.ReactNode, iconPos: string, type?: string }) {
|
|
137
|
+
const paddingLeft = type == 'color' ? 'sm:pl-9 pl-9' : 'sm:pl-8 pl-8'
|
|
138
|
+
const paddingRight = type == 'color' ? 'sm:pr-9 pr-9' : 'sm:pr-8 pr-8'
|
|
139
|
+
return (
|
|
140
|
+
'col-start-1 row-start-1 block w-full rounded-md bg-white py-2 text-sm leading-[1.65] outline outline-1 -outline-offset-1 ' +
|
|
141
|
+
'placeholder:text-input-placeholder focus:outline focus:outline-2 focus:-outline-offset-2 ' +
|
|
142
|
+
(iconPos == 'right' && Icon ? `${paddingRight} pl-3 ` : (Icon ? `${paddingLeft} pr-3 ` : 'px-3 ')) +
|
|
143
|
+
(error ? 'text-red-900 outline-danger focus:outline-danger ' : 'text-input outline-input-border focus:outline-primary ') +
|
|
144
|
+
(iconPos == 'right' ? 'justify-self-start ' : 'justify-self-end ')
|
|
145
|
+
)
|
|
115
146
|
}
|
|
116
147
|
|
|
117
|
-
function
|
|
118
|
-
const iconSize = type == 'color' ? 'size-[18px]' : 'size-4'
|
|
148
|
+
function IconWrapper({ icon, iconPos, ...props }: IconWrapperProps) {
|
|
119
149
|
return (
|
|
120
|
-
!!
|
|
121
|
-
<div
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
150
|
+
!!icon &&
|
|
151
|
+
<div
|
|
152
|
+
{...props}
|
|
153
|
+
className={twMerge(
|
|
154
|
+
'relative size-[14px] col-start-1 row-start-1 self-center text-[#c6c8ce] select-none z-[1] ' +
|
|
155
|
+
(iconPos == 'right' ? 'justify-self-end mr-3 ' : 'justify-self-start ml-3 ') +
|
|
156
|
+
props.className || ''
|
|
157
|
+
)}
|
|
158
|
+
>{icon}</div>
|
|
128
159
|
)
|
|
129
160
|
}
|
|
130
161
|
|
|
131
|
-
function
|
|
162
|
+
function ColorSvg({ hex }: { hex?: string }) {
|
|
132
163
|
return (
|
|
133
164
|
<span class="block size-full rounded-md" style={{ backgroundColor: hex ? hex : '#f1f1f1' }}></span>
|
|
134
165
|
)
|
|
@@ -2,7 +2,7 @@ import { css } from 'twin.macro'
|
|
|
2
2
|
import { twMerge } from 'tailwind-merge'
|
|
3
3
|
import ReactSelect, { components, ControlProps, createFilter, OptionProps, SingleValueProps } from 'react-select'
|
|
4
4
|
import { ClearIndicatorProps, DropdownIndicatorProps, MultiValueRemoveProps } from 'react-select'
|
|
5
|
-
import {
|
|
5
|
+
import { ChevronUpDownIcon, CheckCircleIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
|
6
6
|
import { util } from 'nitro-web'
|
|
7
7
|
import { Errors } from 'types'
|
|
8
8
|
|
|
@@ -203,7 +203,7 @@ function Option(props: OptionProps) {
|
|
|
203
203
|
const DropdownIndicator = (props: DropdownIndicatorProps) => {
|
|
204
204
|
return (
|
|
205
205
|
<components.DropdownIndicator {...props}>
|
|
206
|
-
<
|
|
206
|
+
<ChevronUpDownIcon className="text-gray-400 size-[17px] -my-0.5 -mx-0.5" />
|
|
207
207
|
</components.DropdownIndicator>
|
|
208
208
|
)
|
|
209
209
|
}
|
|
@@ -237,7 +237,7 @@ const selectStyles = {
|
|
|
237
237
|
// Based off https://www.jussivirtanen.fi/writing/styling-react-select-with-tailwind
|
|
238
238
|
// Input container
|
|
239
239
|
control: {
|
|
240
|
-
base: 'rounded-md bg-white hover:cursor-pointer text-sm
|
|
240
|
+
base: 'rounded-md bg-white hover:cursor-pointer text-sm leading-[1.65] outline outline-1 -outline-offset-1 outline-input-border',
|
|
241
241
|
focus: 'outline-2 -outline-offset-2 outline-primary',
|
|
242
242
|
error: 'outline-danger',
|
|
243
243
|
},
|
|
@@ -249,7 +249,7 @@ const selectStyles = {
|
|
|
249
249
|
},
|
|
250
250
|
multiValue: 'bg-primary text-white rounded items-center pl-2 pr-1.5 gap-1.5',
|
|
251
251
|
multiValueLabel: '',
|
|
252
|
-
multiValueRemove: 'border border-
|
|
252
|
+
multiValueRemove: 'border border-black/10 bg-clip-content bg-white rounded-md text-dark hover:bg-red-50',
|
|
253
253
|
placeholder: 'text-input-placeholder',
|
|
254
254
|
singleValue: {
|
|
255
255
|
base: 'text-input',
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import { Drop, Dropdown,
|
|
1
|
+
import { Drop, Dropdown, Field, Select, Button, Checkbox, GithubLink, isDemo, Modal, Calendar } from 'nitro-web'
|
|
2
2
|
import { getCountryOptions, getCurrencyOptions, ucFirst } from 'nitro-web/util'
|
|
3
3
|
import { CheckIcon } from '@heroicons/react/20/solid'
|
|
4
4
|
import { Config } from 'types'
|
|
5
5
|
|
|
6
6
|
export function Styleguide({ config }: { config: Config }) {
|
|
7
7
|
const [customerSearch, setCustomerSearch] = useState('')
|
|
8
|
+
const [showModal1, setShowModal1] = useState(false)
|
|
8
9
|
const [state, setState] = useState({
|
|
9
10
|
address: '',
|
|
10
|
-
country: 'us',
|
|
11
|
-
currency: 'nzd', // can be commented too
|
|
12
11
|
amount: 100,
|
|
13
12
|
brandColor: '#F3CA5F',
|
|
14
|
-
|
|
13
|
+
country: 'us',
|
|
14
|
+
currency: 'nzd', // can be commented too
|
|
15
15
|
date: Date.now(),
|
|
16
|
+
'date-range': [Date.now(), Date.now() + 1000 * 60 * 60 * 24 * 33],
|
|
17
|
+
'date-time': Date.now(),
|
|
18
|
+
calendar: [Date.now(), Date.now() + 1000 * 60 * 60 * 24 * 8],
|
|
19
|
+
firstName: 'Bruce',
|
|
16
20
|
errors: [
|
|
17
21
|
{ title: 'address', detail: 'Address is required' },
|
|
18
|
-
{ title: 'currency', detail: 'Currency is required' },
|
|
19
22
|
],
|
|
20
23
|
})
|
|
21
24
|
|
|
@@ -60,33 +63,19 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
60
63
|
</div>
|
|
61
64
|
|
|
62
65
|
<h2 class="h3">Links</h2>
|
|
63
|
-
<div class="mb-
|
|
66
|
+
<div class="mb-10">
|
|
64
67
|
<a class="mr-2" href="#">Default</a>
|
|
65
68
|
<a class="underline1 is-active mr-2" href="#">Underline1</a>
|
|
66
69
|
<a class="underline2 is-active mr-2" href="#">Underline2</a>
|
|
67
70
|
</div>
|
|
68
71
|
|
|
69
|
-
<h2 class="h3">
|
|
70
|
-
<div class="
|
|
71
|
-
<div>
|
|
72
|
-
<label for="input0">Label</label>
|
|
73
|
-
<Checkbox name="input0" type="checkbox" text="Checkbox" subtext="some additional text here." defaultChecked />
|
|
74
|
-
</div>
|
|
75
|
-
<div>
|
|
76
|
-
<label for="input1">Label</label>
|
|
77
|
-
<Checkbox name="input1" type="radio" text="Radio 1" subtext="some additional text here 1." id="input1-1" class="!mb-0"
|
|
78
|
-
defaultChecked />
|
|
79
|
-
<Checkbox name="input1" type="radio" text="Radio 2" subtext="some additional text here 2." id="input1-2" class="!mt-0" />
|
|
80
|
-
</div>
|
|
81
|
-
<div>
|
|
82
|
-
<label for="input2">Label</label>
|
|
83
|
-
<Checkbox name="input2" type="toggle" text="Toggle sm" subtext="some additional text here." class="!mb-0" defaultChecked />
|
|
84
|
-
<Checkbox name="input3" type="toggle" text="Toggle md" size="md" subtext="some additional text here." />
|
|
85
|
-
</div>
|
|
72
|
+
<h2 class="h3">Modals</h2>
|
|
73
|
+
<div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
|
|
74
|
+
<div><Button color="primary" onClick={() => setShowModal1(true)}>Modal (default)</Button></div>
|
|
86
75
|
</div>
|
|
87
76
|
|
|
88
77
|
<h2 class="h3">Dropdowns</h2>
|
|
89
|
-
<div class="flex flex-wrap gap-x-6 gap-y-4 mb-
|
|
78
|
+
<div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
|
|
90
79
|
<div>
|
|
91
80
|
<Dropdown options={options} minWidth="250px">
|
|
92
81
|
<Button IconRight="v" class="gap-x-3">Dropdown</Button>
|
|
@@ -110,7 +99,7 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
110
99
|
</div>
|
|
111
100
|
|
|
112
101
|
<h2 class="h3">Buttons</h2>
|
|
113
|
-
<div class="flex flex-wrap gap-x-6 gap-y-4 mb-
|
|
102
|
+
<div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
|
|
114
103
|
<div><Button color="primary">primary (default)</Button></div>
|
|
115
104
|
<div><Button color="secondary">secondary button</Button></div>
|
|
116
105
|
<div><Button color="white">white button</Button></div>
|
|
@@ -124,8 +113,27 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
124
113
|
<div><Button color="primary" IconRight="v" isLoading>primary isLoading</Button></div>
|
|
125
114
|
</div>
|
|
126
115
|
|
|
116
|
+
<h2 class="h3">Checkboxes</h2>
|
|
117
|
+
<div class="grid grid-cols-3 gap-x-6 mb-4">
|
|
118
|
+
<div>
|
|
119
|
+
<label for="input2">Label</label>
|
|
120
|
+
<Checkbox name="input2" type="toggle" text="Toggle sm" subtext="some additional text here." class="!mb-0" defaultChecked />
|
|
121
|
+
<Checkbox name="input3" type="toggle" text="Toggle md" size="md" subtext="some additional text here." />
|
|
122
|
+
</div>
|
|
123
|
+
<div>
|
|
124
|
+
<label for="input1">Label</label>
|
|
125
|
+
<Checkbox name="input1" type="radio" text="Radio 1" subtext="some additional text here 1." id="input1-1" class="!mb-0"
|
|
126
|
+
defaultChecked />
|
|
127
|
+
<Checkbox name="input1" type="radio" text="Radio 2" subtext="some additional text here 2." id="input1-2" class="!mt-0" />
|
|
128
|
+
</div>
|
|
129
|
+
<div>
|
|
130
|
+
<label for="input0">Label</label>
|
|
131
|
+
<Checkbox name="input0" type="checkbox" text="Checkbox" subtext="some additional text here." defaultChecked />
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
127
135
|
<h2 class="h3">Selects</h2>
|
|
128
|
-
<div class="grid grid-cols-3 lg:grid-cols-3 gap-x-6">
|
|
136
|
+
<div class="grid grid-cols-3 lg:grid-cols-3 gap-x-6 mb-4">
|
|
129
137
|
<div>
|
|
130
138
|
<label for="action">Default</label>
|
|
131
139
|
<Select
|
|
@@ -204,53 +212,92 @@ export function Styleguide({ config }: { config: Config }) {
|
|
|
204
212
|
<div class="grid grid-cols-3 gap-x-6 mb-4">
|
|
205
213
|
<div>
|
|
206
214
|
<label for="firstName">First Name</label>
|
|
207
|
-
<
|
|
215
|
+
<Field name="firstName" state={state} onChange={onInputChange} />
|
|
208
216
|
</div>
|
|
209
217
|
<div>
|
|
210
218
|
<label for="email">Email Address</label>
|
|
211
|
-
<
|
|
219
|
+
<Field name="email" type="email" placeholder="Your email address..."/>
|
|
212
220
|
</div>
|
|
213
221
|
<div>
|
|
214
222
|
<div class="flex justify-between">
|
|
215
223
|
<label for="password">Password</label>
|
|
216
224
|
<a href="#" class="label">Forgot?</a>
|
|
217
225
|
</div>
|
|
218
|
-
<
|
|
226
|
+
<Field name="password" type="password"/>
|
|
219
227
|
</div>
|
|
220
228
|
<div>
|
|
221
229
|
<label for="search">Search</label>
|
|
222
|
-
<
|
|
230
|
+
<Field name="search" type="search" placeholder="Search..." />
|
|
223
231
|
</div>
|
|
224
232
|
<div>
|
|
225
|
-
<label for="filter">Filter</label>
|
|
226
|
-
<
|
|
233
|
+
<label for="filter">Filter by Code</label>
|
|
234
|
+
<Field name="filter" type="filter" iconPos="left" />
|
|
227
235
|
</div>
|
|
228
236
|
<div>
|
|
229
237
|
<label for="address">Input Error</label>
|
|
230
|
-
<
|
|
238
|
+
<Field name="address" placeholder="Address..." state={state} onChange={onInputChange} />
|
|
231
239
|
</div>
|
|
232
|
-
{/* <div>
|
|
233
|
-
<label for="date">Date</label>
|
|
234
|
-
<Input name="date" type="date" prefix="Date:" state={state} onChange={onInputChange} />
|
|
235
|
-
</div> */}
|
|
236
240
|
<div>
|
|
237
|
-
<label for="
|
|
238
|
-
<
|
|
241
|
+
<label for="description">Description</label>
|
|
242
|
+
<Field name="description" type="textarea" rows={2} />
|
|
239
243
|
</div>
|
|
240
244
|
<div>
|
|
241
|
-
<label for="
|
|
242
|
-
<
|
|
245
|
+
<label for="brandColor">Brand Color</label>
|
|
246
|
+
<Field name="brandColor" type="color" state={state} iconPos="left" onChange={onInputChange} />
|
|
243
247
|
</div>
|
|
244
248
|
<div>
|
|
245
249
|
<label for="amount">Amount ({state.amount})</label>
|
|
246
|
-
<
|
|
250
|
+
<Field name="amount" type="currency" state={state} currency={state.currency || 'nzd'} onChange={onInputChange} config={config} />
|
|
247
251
|
</div>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<h2 class="h3">Date Inputs</h2>
|
|
255
|
+
<div class="grid grid-cols-1 gap-x-6 mb-4 sm:grid-cols-3">
|
|
256
|
+
<div>
|
|
257
|
+
<label for="date">Date with time</label>
|
|
258
|
+
<Field name="date-time" type="date" showTime={true} state={state} onChange={onInputChange} />
|
|
259
|
+
</div>
|
|
260
|
+
<div>
|
|
261
|
+
<label for="date-range">Date range with prefix</label>
|
|
262
|
+
<Field name="date-range" type="date" mode="range" prefix="Date:" state={state} onChange={onInputChange} />
|
|
263
|
+
</div>
|
|
264
|
+
<div>
|
|
265
|
+
<label for="date">Date</label>
|
|
266
|
+
<Field name="date" type="date" state={state} onChange={onInputChange} />
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<h2 class="h3">File Inputs & Calendar</h2>
|
|
271
|
+
<div class="grid grid-cols-3 gap-x-6 mb-4">
|
|
248
272
|
<div>
|
|
249
273
|
<label for="avatar">Avatar</label>
|
|
250
274
|
<Drop class="is-small" name="avatar" state={state} onChange={onInputChange} awsUrl={config.awsUrl} />
|
|
251
275
|
</div>
|
|
276
|
+
<div>
|
|
277
|
+
<label for="calendar">Calendar</label>
|
|
278
|
+
<Calendar mode="range" value={state.calendar} numberOfMonths={1} onChange={(mode, value) => {
|
|
279
|
+
onInputChange({ target: { id: 'calendar', value: value } })
|
|
280
|
+
}} />
|
|
281
|
+
</div>
|
|
252
282
|
</div>
|
|
253
283
|
|
|
284
|
+
<Modal show={showModal1} setShow={setShowModal1} class="p-9">
|
|
285
|
+
<h3 class="h3">Edit Profile</h3>
|
|
286
|
+
<p class="mb-5">An example modal containing a basic form for editing profiles.</p>
|
|
287
|
+
<form class="mb-8 text-left">
|
|
288
|
+
<div>
|
|
289
|
+
<label for="firstName2">First Name</label>
|
|
290
|
+
<Field name="firstName2" state={state} onChange={onInputChange} />
|
|
291
|
+
</div>
|
|
292
|
+
<div>
|
|
293
|
+
<label for="email2">Email Address</label>
|
|
294
|
+
<Field name="email2" type="email" placeholder="Your email address..."/>
|
|
295
|
+
</div>
|
|
296
|
+
</form>
|
|
297
|
+
<div class="flex justify-end">
|
|
298
|
+
<Button color="primary" onClick={() => setShowModal1(false)}>Save</Button>
|
|
299
|
+
</div>
|
|
300
|
+
</Modal>
|
|
254
301
|
</div>
|
|
255
302
|
)
|
|
256
303
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// todo: finish tailwind conversion
|
|
3
3
|
import * as util from 'nitro-web/util'
|
|
4
4
|
import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
|
|
5
|
-
import { Button, FormError,
|
|
5
|
+
import { Button, FormError, Field, Modal, Topbar, Tabbar } from 'nitro-web'
|
|
6
6
|
|
|
7
7
|
export function SettingsAccount() {
|
|
8
8
|
const isLoading = useState('')
|
|
@@ -48,21 +48,21 @@ export function SettingsAccount() {
|
|
|
48
48
|
<div class="cols cols-6 cols-gap-3">
|
|
49
49
|
<div class="col">
|
|
50
50
|
<label for="firstName">First Name(s)</label>
|
|
51
|
-
<
|
|
51
|
+
<Field name="firstName" placeholder="E.g. Bruce" state={state} onChange={onChange.bind(setState)} />
|
|
52
52
|
</div>
|
|
53
53
|
<div class="col">
|
|
54
54
|
<label for="lastName">Last Name</label>
|
|
55
|
-
<
|
|
55
|
+
<Field name="lastName" placeholder="E.g. Wayne" state={state} onChange={onChange.bind(setState)} />
|
|
56
56
|
</div>
|
|
57
57
|
<div class="col">
|
|
58
58
|
<label for="email">Email Address</label>
|
|
59
|
-
<
|
|
60
|
-
onChange={onChange(setState)} />
|
|
59
|
+
<Field name="email" type="email" placeholder="Your email address..." state={state}
|
|
60
|
+
onChange={onChange.bind(setState)} />
|
|
61
61
|
</div>
|
|
62
62
|
<div class="col">
|
|
63
63
|
<Link to="/reset" class="label-right link2 underline2 is-active">Reset Password?</Link>
|
|
64
64
|
<label for="password">Password</label>
|
|
65
|
-
<
|
|
65
|
+
<Field name="password" placeholder="•••••••••••" disabled={true} />
|
|
66
66
|
</div>
|
|
67
67
|
</div>
|
|
68
68
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
//@ts-nocheck
|
|
2
2
|
// todo: finish tailwind conversio
|
|
3
3
|
|
|
4
4
|
////// look at the select type error below
|
|
5
5
|
import * as util from 'nitro-web/util'
|
|
6
6
|
import SvgTick from 'nitro-web/client/imgs/icons/tick.svg'
|
|
7
|
-
import { Button,
|
|
7
|
+
import { Button, Field, Select, Topbar, Tabbar } from 'nitro-web'
|
|
8
8
|
|
|
9
9
|
export function SettingsBusiness({ config }) {
|
|
10
10
|
const isLoading = useState('')
|
|
@@ -65,7 +65,7 @@ export function SettingsBusiness({ config }) {
|
|
|
65
65
|
type="country"
|
|
66
66
|
state={state}
|
|
67
67
|
options={useMemo(() => util.getCountryOptions(config.countries), [])}
|
|
68
|
-
onChange={onChange(setState)}
|
|
68
|
+
onChange={onChange.bind(setState)}
|
|
69
69
|
/>
|
|
70
70
|
</div>
|
|
71
71
|
<div class="col">
|
|
@@ -75,37 +75,37 @@ export function SettingsBusiness({ config }) {
|
|
|
75
75
|
type="country"
|
|
76
76
|
state={state}
|
|
77
77
|
options={useMemo(() => util.getCurrencyOptions(config.currencies), [])}
|
|
78
|
-
onChange={onChange(setState)}
|
|
78
|
+
onChange={onChange.bind(setState)}
|
|
79
79
|
/>
|
|
80
80
|
</div>
|
|
81
81
|
<div class="col">
|
|
82
82
|
<label for="business.name">Trading Name</label>
|
|
83
|
-
<
|
|
83
|
+
<Field name="business.name" placeholder="E.g. Wayne Enterprises" state={state} onChange={onChange.bind(setState)} />
|
|
84
84
|
</div>
|
|
85
85
|
<div class="col">
|
|
86
86
|
<Link to="#" class="label-right link2 underline2 is-active">Custom Address</Link>
|
|
87
87
|
<label for="business.address">Address (Start Typing...)</label>
|
|
88
|
-
<
|
|
88
|
+
<Field name="business.address.full" placeholder="" state={state} onChange={onChange.bind(setState)} />
|
|
89
89
|
</div>
|
|
90
90
|
<div class="col">
|
|
91
91
|
<label for="business.website">Website</label>
|
|
92
|
-
<
|
|
92
|
+
<Field name="business.website" placeholder="https://" state={state} onChange={onChange.bind(setState)} />
|
|
93
93
|
</div>
|
|
94
94
|
<div class="col">
|
|
95
95
|
<label for="business.phone">Mobile Number</label>
|
|
96
|
-
<
|
|
96
|
+
<Field name="business.phone" placeholder="" state={state} onChange={onChange.bind(setState)} />
|
|
97
97
|
</div>
|
|
98
98
|
<div class="col">
|
|
99
99
|
<Link to="#" class="label-right link2 underline2 is-active">What's this for?</Link>
|
|
100
100
|
<label for="tax.number">GST Number</label>
|
|
101
|
-
<
|
|
102
|
-
onChange={onChange(setState)} />
|
|
101
|
+
<Field class="mb-0" name="tax.number" placeholder="Appears on your documents" state={state}
|
|
102
|
+
onChange={onChange.bind(setState)} />
|
|
103
103
|
</div>
|
|
104
104
|
<div class="col">
|
|
105
105
|
<Link to="#" class="label-right link2 underline2 is-active">What's this for?</Link>
|
|
106
106
|
<label for="business.number">NZBN</label>
|
|
107
|
-
<
|
|
108
|
-
onChange={onChange(setState)} />
|
|
107
|
+
<Field class="mb-0" name="business.number" type="text" rows="23" placeholder="Appears on your documents" state={state}
|
|
108
|
+
onChange={onChange.bind(setState)} />
|
|
109
109
|
</div>
|
|
110
110
|
</div>
|
|
111
111
|
</form>
|