nitro-web 0.0.49 → 0.0.51
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/client/index.ts +1 -1
- package/components/auth/signin.tsx +0 -1
- package/components/partials/element/button.tsx +19 -15
- package/components/partials/element/filters.tsx +3 -3
- package/components/partials/form/checkbox.tsx +1 -1
- package/components/partials/form/drop.tsx +8 -14
- package/components/partials/form/field-color.tsx +7 -6
- package/components/partials/form/field-currency.tsx +4 -3
- package/components/partials/form/field-date.tsx +10 -9
- package/components/partials/form/field.tsx +55 -24
- package/components/partials/form/form-error.tsx +1 -1
- package/components/partials/form/select.tsx +45 -33
- package/components/partials/styleguide.tsx +69 -44
- package/package.json +1 -1
- package/types/util.d.ts +17 -2
- package/types/util.d.ts.map +1 -1
- package/types.ts +1 -1
- package/util.js +30 -3
package/client/index.ts
CHANGED
|
@@ -42,7 +42,7 @@ export { Checkbox } from '../components/partials/form/checkbox'
|
|
|
42
42
|
export { Drop } from '../components/partials/form/drop'
|
|
43
43
|
export { DropHandler } from '../components/partials/form/drop-handler'
|
|
44
44
|
export { FormError } from '../components/partials/form/form-error'
|
|
45
|
-
export { Field } from '../components/partials/form/field'
|
|
45
|
+
export { Field, isFieldCached } from '../components/partials/form/field'
|
|
46
46
|
export { FieldColor, type FieldColorProps } from '../components/partials/form/field-color'
|
|
47
47
|
export { FieldCurrency, type FieldCurrencyProps } from '../components/partials/form/field-currency'
|
|
48
48
|
export { FieldDate, type FieldDateProps } from '../components/partials/form/field-date'
|
|
@@ -7,7 +7,6 @@ export function Signin() {
|
|
|
7
7
|
const isSignout = location.pathname == '/signout'
|
|
8
8
|
const isLoading = useState(isSignout)
|
|
9
9
|
const [, setStore] = useTracked()
|
|
10
|
-
|
|
11
10
|
const [state, setState] = useState({
|
|
12
11
|
email: injectedConfig.env == 'development' ? (injectedConfig.placeholderEmail || '') : '',
|
|
13
12
|
password: injectedConfig.env == 'development' ? '1234' : '',
|
|
@@ -2,8 +2,9 @@ import { twMerge } from 'nitro-web'
|
|
|
2
2
|
import { ChevronDown, ChevronUp } from 'lucide-react'
|
|
3
3
|
|
|
4
4
|
type Button = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
5
|
-
color?: 'primary'|'secondary'|'black'|'white'|'clear'
|
|
5
|
+
color?: 'primary'|'secondary'|'black'|'dark'|'white'|'clear'|'custom'
|
|
6
6
|
size?: 'xs'|'sm'|'md'|'lg'
|
|
7
|
+
customColor?: string
|
|
7
8
|
className?: string
|
|
8
9
|
isLoading?: boolean
|
|
9
10
|
IconLeft?: React.ReactNode|'v'
|
|
@@ -13,9 +14,10 @@ type Button = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
|
13
14
|
children?: React.ReactNode|'v'
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export function Button({
|
|
17
|
+
export function Button({
|
|
17
18
|
size='md',
|
|
18
19
|
color='primary',
|
|
20
|
+
customColor,
|
|
19
21
|
className,
|
|
20
22
|
isLoading,
|
|
21
23
|
IconLeft,
|
|
@@ -29,25 +31,27 @@ export function Button({
|
|
|
29
31
|
const iconPosition = IconLeft ? 'left' : IconLeftEnd ? 'leftEnd' : IconRight ? 'right' : IconRightEnd ? 'rightEnd' : 'none'
|
|
30
32
|
const base =
|
|
31
33
|
'relative inline-block text-center font-medium shadow-sm focus-visible:outline focus-visible:outline-2 ' +
|
|
32
|
-
'focus-visible:outline-offset-2 text-white [&>.loader]:border-white'
|
|
34
|
+
'focus-visible:outline-offset-2 text-white [&>.loader]:border-white ring-inset ring-1'
|
|
33
35
|
|
|
34
36
|
// Button colors, you can use custom colors by using className instead
|
|
35
37
|
const colors = {
|
|
36
|
-
primary: 'bg-primary hover:bg-primary-hover',
|
|
37
|
-
secondary: 'bg-secondary hover:bg-secondary-hover',
|
|
38
|
-
black: 'bg-black hover:bg-gray-700',
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
'primary': 'bg-primary hover:bg-primary-hover ring-transparent !ring-button-primary-ring',
|
|
39
|
+
'secondary': 'bg-secondary hover:bg-secondary-hover ring-transparent !ring-button-secondary-ring',
|
|
40
|
+
'black': 'bg-black hover:bg-gray-700 ring-transparent !ring-button-black-ring',
|
|
41
|
+
'dark': 'bg-gray-800 !bg-button-dark hover:bg-gray-700 hover:!bg-button-dark-hover ring-transparent !ring-button-dark-ring',
|
|
42
|
+
'white': 'bg-white ring-gray-300 !ring-button-white-ring hover:bg-gray-50 text-gray-900 [&>.loader]:border-black',
|
|
43
|
+
'clear': 'ring-gray-300 !ring-button-clear-ring hover:bg-gray-50 text-foreground [&>.loader]:border-foreground !shadow-none',
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
// Button sizes
|
|
46
|
+
// Button sizes (px is better for height consistency)
|
|
44
47
|
const sizes = {
|
|
45
|
-
xs: 'px-
|
|
46
|
-
sm: 'px-
|
|
47
|
-
md: 'px-
|
|
48
|
-
lg: 'px-
|
|
48
|
+
xs: 'px-[6px] py-[3px] px-button-x-xs py-button-y-xs text-xs rounded',
|
|
49
|
+
sm: 'px-[10px] py-[6px] px-button-x-sm py-button-y-sm text-button-size rounded-md',
|
|
50
|
+
md: 'px-[12px] py-[9px] px-button-x-md py-button-y-md text-button-size rounded-md', // default
|
|
51
|
+
lg: 'px-[18px] py-[11px] px-button-x-lg py-button-y-lg text-button-size rounded-md',
|
|
49
52
|
}
|
|
50
|
-
|
|
53
|
+
|
|
54
|
+
const appliedColor = color === 'custom' ? customColor : colors[color]
|
|
51
55
|
const contentLayout = `gap-x-1.5 ${iconPosition == 'none' ? '' : 'inline-flex items-center justify-center'}`
|
|
52
56
|
const loading = isLoading ? '[&>*]:opacity-0 text-opacity-0' : ''
|
|
53
57
|
|
|
@@ -58,7 +62,7 @@ export function Button({
|
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
return (
|
|
61
|
-
<button class={twMerge(`${base} ${
|
|
65
|
+
<button class={twMerge(`${base} ${sizes[size]} ${appliedColor} ${contentLayout} ${loading} nitro-button ${className||''}`)} {...props}>
|
|
62
66
|
{IconLeft && getIcon(IconLeft)}
|
|
63
67
|
{IconLeftEnd && getIcon(IconLeftEnd)}
|
|
64
68
|
<span class={`${iconPosition == 'leftEnd' || iconPosition == 'rightEnd' ? 'flex-1' : ''}`}>{children}</span>
|
|
@@ -84,7 +84,7 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
84
84
|
debouncedSubmit()
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
async function _onChange(e: {target: {
|
|
87
|
+
async function _onChange(e: {target: {name: string, value: unknown}}) {
|
|
88
88
|
await onChange(setState, e)
|
|
89
89
|
debouncedSubmit()
|
|
90
90
|
}
|
|
@@ -169,9 +169,9 @@ export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
|
169
169
|
{
|
|
170
170
|
!!count &&
|
|
171
171
|
<span
|
|
172
|
-
class={twMerge(`inline-flex items-center justify-center rounded-full text-
|
|
172
|
+
class={twMerge(`inline-flex items-center justify-center rounded-full text-white bg-primary box-content w-[1em] h-[1em] p-[2px] ${buttonCounterClassName || ''}`)}
|
|
173
173
|
>
|
|
174
|
-
{count}
|
|
174
|
+
<span class="text-xs">{count}</span>
|
|
175
175
|
</span>
|
|
176
176
|
}
|
|
177
177
|
</span>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
import {
|
|
2
|
+
import { deepFind, s3Image, getErrorFromState } from 'nitro-web/util'
|
|
3
3
|
import { DropHandler } from 'nitro-web'
|
|
4
4
|
import noImage from 'nitro-web/client/imgs/no-image.svg'
|
|
5
5
|
import { Errors, MonasteryImage } from 'nitro-web/types'
|
|
@@ -7,12 +7,12 @@ import { Errors, MonasteryImage } from 'nitro-web/types'
|
|
|
7
7
|
type DropProps = {
|
|
8
8
|
awsUrl?: string
|
|
9
9
|
className?: string
|
|
10
|
-
/** Optional ID for the input element. Defaults to name if not provided */
|
|
11
|
-
id?: string
|
|
12
10
|
/** Field name or path on state (used to match errors), e.g. 'avatar', 'company.avatar' */
|
|
13
11
|
name: string
|
|
12
|
+
/** Optional ID for the input element. Defaults to name if not provided */
|
|
13
|
+
id?: string
|
|
14
14
|
/** Called when file is selected or dropped */
|
|
15
|
-
onChange?: (event: { target: {
|
|
15
|
+
onChange?: (event: { target: { name: string, value: File|FileList } }) => void
|
|
16
16
|
/** Whether to allow multiple file selection */
|
|
17
17
|
multiple?: boolean
|
|
18
18
|
/** State object to get the value and check errors against */
|
|
@@ -29,8 +29,8 @@ type Image = File | FileList | MonasteryImage | null
|
|
|
29
29
|
export function Drop({ awsUrl, className, id, name, onChange, multiple, state, ...props }: DropProps) {
|
|
30
30
|
if (!name) throw new Error('Drop component requires a `name` prop')
|
|
31
31
|
let value: Image = null
|
|
32
|
-
|
|
33
|
-
const inputId = id ||name
|
|
32
|
+
const error = getErrorFromState(state, name)
|
|
33
|
+
const inputId = id || name
|
|
34
34
|
const [urls, setUrls] = useState([])
|
|
35
35
|
const stateRef = useRef(state)
|
|
36
36
|
stateRef.current = state
|
|
@@ -40,12 +40,6 @@ export function Drop({ awsUrl, className, id, name, onChange, multiple, state, .
|
|
|
40
40
|
else if (typeof state == 'object') value = deepFind(state, name) as Image
|
|
41
41
|
if (typeof value == 'undefined') value = null
|
|
42
42
|
|
|
43
|
-
// An error matches this input path
|
|
44
|
-
for (const item of (state?.errors as Errors[] || [])) {
|
|
45
|
-
if (isRegex(name) && (item.title||'').match(name)) error = item
|
|
46
|
-
else if (item.title == name) error = item
|
|
47
|
-
}
|
|
48
|
-
|
|
49
43
|
useEffect(() => {
|
|
50
44
|
(async () => setUrls(await getUrls(value as File | FileList | MonasteryImage | null)))()
|
|
51
45
|
}, [value])
|
|
@@ -59,7 +53,7 @@ export function Drop({ awsUrl, className, id, name, onChange, multiple, state, .
|
|
|
59
53
|
const errors = (stateRef?.current?.errors || []).filter((e: Errors[]) => e?.title != name)
|
|
60
54
|
onChange({
|
|
61
55
|
// remove file from state
|
|
62
|
-
target: {
|
|
56
|
+
target: { name: name, value: null },
|
|
63
57
|
// reset (server) errors
|
|
64
58
|
errors: errors.length ? errors : undefined,
|
|
65
59
|
})
|
|
@@ -68,7 +62,7 @@ export function Drop({ awsUrl, className, id, name, onChange, multiple, state, .
|
|
|
68
62
|
|
|
69
63
|
async function onFileAttach (files: FileList) {
|
|
70
64
|
// files is a FileList object
|
|
71
|
-
if (onChange) onChange({ target: {
|
|
65
|
+
if (onChange) onChange({ target: { name: name, value: multiple ? files : files[0] } })
|
|
72
66
|
}
|
|
73
67
|
|
|
74
68
|
async function getUrls(objectOrFileListItem: File | FileList | MonasteryImage | null) {
|
|
@@ -5,10 +5,11 @@ import { Dropdown, util } from 'nitro-web'
|
|
|
5
5
|
|
|
6
6
|
export type FieldColorProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
|
7
7
|
name: string
|
|
8
|
+
/** name is applied if id is not provided */
|
|
8
9
|
id?: string
|
|
9
10
|
defaultColor?: string
|
|
10
11
|
Icon?: React.ReactNode
|
|
11
|
-
onChange?: (event: { target: {
|
|
12
|
+
onChange?: (event: { target: { name: string, value: string|null } }) => void
|
|
12
13
|
value?: string|null
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -17,7 +18,7 @@ export function FieldColor({ defaultColor='#333', Icon, onChange, value, ...prop
|
|
|
17
18
|
const isInvalid = props.className?.includes('is-invalid') ? 'is-invalid' : ''
|
|
18
19
|
const id = props.id || props.name
|
|
19
20
|
|
|
20
|
-
function onInputChange(e: { target: {
|
|
21
|
+
function onInputChange(e: { target: { name: string, value: string|null } }) {
|
|
21
22
|
setLastChanged(`ic-${Date.now()}`)
|
|
22
23
|
if (onChange) onChange(e)
|
|
23
24
|
}
|
|
@@ -27,7 +28,7 @@ export function FieldColor({ defaultColor='#333', Icon, onChange, value, ...prop
|
|
|
27
28
|
dir="bottom-left"
|
|
28
29
|
menuToggles={false}
|
|
29
30
|
menuContent={
|
|
30
|
-
<ColorPicker key={lastChanged} defaultColor={defaultColor}
|
|
31
|
+
<ColorPicker key={lastChanged} defaultColor={defaultColor} name={props.name} value={value} onChange={onChange} />
|
|
31
32
|
}
|
|
32
33
|
>
|
|
33
34
|
<div className="grid grid-cols-1">
|
|
@@ -38,7 +39,7 @@ export function FieldColor({ defaultColor='#333', Icon, onChange, value, ...prop
|
|
|
38
39
|
id={id}
|
|
39
40
|
value={value}
|
|
40
41
|
onChange={onInputChange}
|
|
41
|
-
onBlur={() => !validHex(value||'') && onInputChange({ target: {
|
|
42
|
+
onBlur={() => !validHex(value||'') && onInputChange({ target: { name: props.name, value: '' }})}
|
|
42
43
|
autoComplete="off"
|
|
43
44
|
type="text"
|
|
44
45
|
/>
|
|
@@ -47,12 +48,12 @@ export function FieldColor({ defaultColor='#333', Icon, onChange, value, ...prop
|
|
|
47
48
|
)
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
function ColorPicker({
|
|
51
|
+
function ColorPicker({ name='', onChange, value='', defaultColor='' }: FieldColorProps) {
|
|
51
52
|
const [hsva, setHsva] = useState(() => hexToHsva(validHex(value) ? value : defaultColor))
|
|
52
53
|
const [debounce] = useState(() => util.throttle(callOnChange, 50))
|
|
53
54
|
|
|
54
55
|
function callOnChange(newHsva: HsvaColor) {
|
|
55
|
-
if (onChange) onChange({ target: {
|
|
56
|
+
if (onChange) onChange({ target: { name: name, value: hsvaToHex(newHsva) }})
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
return (
|
|
@@ -17,6 +17,7 @@ type NumericFormatProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
|
|
17
17
|
|
|
18
18
|
export type FieldCurrencyProps = NumericFormatProps & {
|
|
19
19
|
name: string
|
|
20
|
+
/** name is applied if id is not provided */
|
|
20
21
|
id?: string
|
|
21
22
|
/** e.g. { currencies: { nzd: { symbol: '$', digits: 2 } } } (check out the nitro example for more info) */
|
|
22
23
|
config: {
|
|
@@ -25,7 +26,7 @@ export type FieldCurrencyProps = NumericFormatProps & {
|
|
|
25
26
|
}
|
|
26
27
|
/** currency iso, e.g. 'nzd' */
|
|
27
28
|
currency: string
|
|
28
|
-
onChange?: (event: { target: {
|
|
29
|
+
onChange?: (event: { target: { name: string, value: string|number|null } }) => void
|
|
29
30
|
/** value should be in cents */
|
|
30
31
|
value?: string|number|null
|
|
31
32
|
defaultValue?: number | string | null
|
|
@@ -138,7 +139,7 @@ export function FieldCurrency({ config, currency='nzd', onChange, value, default
|
|
|
138
139
|
onValueChange={!onChange ? undefined : ({ floatValue }, e) => {
|
|
139
140
|
// console.log('onValueChange', floatValue, e)
|
|
140
141
|
if (e.source === 'event') setDontFix(true)
|
|
141
|
-
onChange({ target: {
|
|
142
|
+
onChange({ target: { name: props.name, value: toCents(floatValue) }})
|
|
142
143
|
}}
|
|
143
144
|
onBlur={() => { setDollars(toDollars(value, true))}}
|
|
144
145
|
placeholder={props.placeholder || '0.00'}
|
|
@@ -148,7 +149,7 @@ export function FieldCurrency({ config, currency='nzd', onChange, value, default
|
|
|
148
149
|
defaultValue={defaultValue}
|
|
149
150
|
/>
|
|
150
151
|
<span
|
|
151
|
-
class={`absolute top-0 bottom-0 left-3 inline-flex items-center select-none text-gray-500 text-
|
|
152
|
+
class={`absolute top-0 bottom-0 left-3 inline-flex items-center select-none text-gray-500 text-input-size ${dollars !== null && settings.prefix == '$' ? 'text-foreground' : ''}`}
|
|
152
153
|
>
|
|
153
154
|
{settings.prefix || settings.suffix}
|
|
154
155
|
</span>
|
|
@@ -11,7 +11,8 @@ type DropdownRef = {
|
|
|
11
11
|
type PreFieldDateProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> & {
|
|
12
12
|
name: string
|
|
13
13
|
mode: Mode
|
|
14
|
-
id
|
|
14
|
+
// name is applied if id is not provided
|
|
15
|
+
id?: string
|
|
15
16
|
showTime?: boolean
|
|
16
17
|
prefix?: string
|
|
17
18
|
numberOfMonths?: number
|
|
@@ -22,11 +23,11 @@ type PreFieldDateProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onCh
|
|
|
22
23
|
// An array is returned for mode = 'multiple' or 'range'
|
|
23
24
|
export type FieldDateProps = (
|
|
24
25
|
| ({ mode: 'single' } & PreFieldDateProps & {
|
|
25
|
-
onChange?: (e: { target: {
|
|
26
|
+
onChange?: (e: { target: { name: string, value: null|number } }) => void
|
|
26
27
|
value?: null|number|string
|
|
27
28
|
})
|
|
28
29
|
| ({ mode: 'multiple' | 'range' } & PreFieldDateProps & {
|
|
29
|
-
onChange: (e: { target: {
|
|
30
|
+
onChange: (e: { target: { name: string, value: (null|number)[] } }) => void
|
|
30
31
|
value?: null|number|string|(null|number|string)[]
|
|
31
32
|
})
|
|
32
33
|
)
|
|
@@ -42,9 +43,9 @@ export function FieldDate({
|
|
|
42
43
|
const localePattern = `d MMM yyyy${showTime && mode == 'single' ? ' hh:mmaa' : ''}`
|
|
43
44
|
const [prefixWidth, setPrefixWidth] = useState(0)
|
|
44
45
|
const dropdownRef = useRef<DropdownRef>(null)
|
|
45
|
-
const id = props.id || props.name
|
|
46
46
|
const [month, setMonth] = useState<number|undefined>()
|
|
47
47
|
const [lastUpdated, setLastUpdated] = useState(0)
|
|
48
|
+
const id = props.id || props.name
|
|
48
49
|
|
|
49
50
|
// Convert the value to an array of valid* dates
|
|
50
51
|
const dates = useMemo(() => {
|
|
@@ -70,7 +71,7 @@ export function FieldDate({
|
|
|
70
71
|
setInputValue(getInputValue(value))
|
|
71
72
|
// Update the value
|
|
72
73
|
if (onChange) {
|
|
73
|
-
onChange({ target: {
|
|
74
|
+
onChange({ target: { name: props.name, value: value as any } })
|
|
74
75
|
setLastUpdated(new Date().getTime())
|
|
75
76
|
}
|
|
76
77
|
}
|
|
@@ -104,7 +105,7 @@ export function FieldDate({
|
|
|
104
105
|
// Update the value
|
|
105
106
|
const value = mode == 'single' ? split[0]?.getTime() ?? null : split.map(d => d?.getTime() ?? null)
|
|
106
107
|
if (onChange) {
|
|
107
|
-
onChange({ target: {
|
|
108
|
+
onChange({ target: { name: props.name, value: value as any }})
|
|
108
109
|
setLastUpdated(new Date().getTime())
|
|
109
110
|
}
|
|
110
111
|
}
|
|
@@ -134,7 +135,7 @@ export function FieldDate({
|
|
|
134
135
|
{
|
|
135
136
|
prefix &&
|
|
136
137
|
// Similar classNames to the input.tsx:IconWrapper()
|
|
137
|
-
<span className="z-[0] col-start-1 row-start-1 self-center select-none justify-self-start text-
|
|
138
|
+
<span className="z-[0] col-start-1 row-start-1 self-center select-none justify-self-start text-input-size ml-3">
|
|
138
139
|
{prefix}
|
|
139
140
|
</span>
|
|
140
141
|
}
|
|
@@ -144,11 +145,11 @@ export function FieldDate({
|
|
|
144
145
|
id={id}
|
|
145
146
|
autoComplete="off"
|
|
146
147
|
className={(props.className||'')}// + props.className?.includes('is-invalid') ? ' is-invalid' : ''}
|
|
147
|
-
value={inputValue}
|
|
148
|
-
onChange={onInputChange}
|
|
149
148
|
onBlur={() => setInputValue(getInputValue(dates))}
|
|
149
|
+
onChange={onInputChange}
|
|
150
150
|
style={{ textIndent: prefixWidth + 'px' }}
|
|
151
151
|
type="text"
|
|
152
|
+
value={inputValue}
|
|
152
153
|
/>
|
|
153
154
|
</div>
|
|
154
155
|
</Dropdown>
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { css } from 'twin.macro'
|
|
3
|
-
import {
|
|
3
|
+
import { FieldCurrency, FieldCurrencyProps, FieldColor, FieldColorProps, FieldDate, FieldDateProps } from 'nitro-web'
|
|
4
|
+
import { twMerge, getErrorFromState, deepFind } from 'nitro-web/util'
|
|
4
5
|
import { Errors, type Error } from 'nitro-web/types'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
CalendarIcon,
|
|
8
|
-
FunnelIcon,
|
|
9
|
-
MagnifyingGlassIcon,
|
|
10
|
-
EyeIcon,
|
|
11
|
-
EyeSlashIcon,
|
|
12
|
-
} from '@heroicons/react/20/solid'
|
|
6
|
+
import { EnvelopeIcon, CalendarIcon, FunnelIcon, MagnifyingGlassIcon, EyeIcon, EyeSlashIcon } from '@heroicons/react/20/solid'
|
|
7
|
+
import { memo } from 'react'
|
|
13
8
|
// Maybe use fill-current tw class for lucide icons (https://github.com/lucide-icons/lucide/discussions/458)
|
|
14
9
|
|
|
15
10
|
type InputProps = React.InputHTMLAttributes<HTMLInputElement>
|
|
@@ -17,12 +12,15 @@ type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>
|
|
|
17
12
|
type FieldExtraProps = {
|
|
18
13
|
// field name or path on state (used to match errors), e.g. 'date', 'company.email'
|
|
19
14
|
name: string
|
|
15
|
+
// name is applied if id is not provided
|
|
20
16
|
id?: string
|
|
21
17
|
// state object to get the value, and check errors against
|
|
22
18
|
state?: { errors?: Errors, [key: string]: any }
|
|
23
19
|
type?: 'text' | 'password' | 'email' | 'filter' | 'search' | 'textarea' | 'currency' | 'date' | 'color'
|
|
24
20
|
icon?: React.ReactNode
|
|
25
21
|
iconPos?: 'left' | 'right'
|
|
22
|
+
/** Pass dependencies to break memoization, handy for onChange/onInputChange **/
|
|
23
|
+
deps?: unknown[]
|
|
26
24
|
}
|
|
27
25
|
type IconWrapperProps = {
|
|
28
26
|
iconPos: string
|
|
@@ -37,12 +35,21 @@ export type FieldProps = (
|
|
|
37
35
|
| ({ type: 'color' } & FieldColorProps & FieldExtraProps)
|
|
38
36
|
| ({ type: 'date' } & FieldDateProps & FieldExtraProps)
|
|
39
37
|
)
|
|
38
|
+
type IsFieldCachedProps = {
|
|
39
|
+
name: string
|
|
40
|
+
state?: FieldProps['state']
|
|
41
|
+
deps?: FieldProps['deps']
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Field = memo(FieldBase, (prev, next) => {
|
|
45
|
+
return isFieldCached(prev, next)
|
|
46
|
+
})
|
|
40
47
|
|
|
41
|
-
|
|
48
|
+
function FieldBase({ state, icon, iconPos: ip, ...props }: FieldProps) {
|
|
42
49
|
// type must be kept as props.type for TS to be happy and follow the conditions below
|
|
43
|
-
let error!: Error
|
|
44
50
|
let value!: string
|
|
45
51
|
let Icon!: React.ReactNode
|
|
52
|
+
const error = getErrorFromState(state, props.name)
|
|
46
53
|
const type = props.type
|
|
47
54
|
const iconPos = ip == 'left' || (type == 'color' && !ip) ? 'left' : 'right'
|
|
48
55
|
|
|
@@ -58,16 +65,10 @@ export function Field({ state, icon, iconPos: ip, ...props }: FieldProps) {
|
|
|
58
65
|
// Value: Input is always controlled if state is passed in
|
|
59
66
|
if (props.value) value = props.value as string
|
|
60
67
|
else if (typeof state == 'object') {
|
|
61
|
-
const v =
|
|
68
|
+
const v = deepFind(state, props.name) as string | undefined
|
|
62
69
|
value = v ?? ''
|
|
63
70
|
}
|
|
64
71
|
|
|
65
|
-
// Errors: find any that match this field path
|
|
66
|
-
for (const item of (state?.errors || [])) {
|
|
67
|
-
if (util.isRegex(props.name) && (item.title || '').match(props.name)) error = item
|
|
68
|
-
else if (item.title == props.name) error = item
|
|
69
|
-
}
|
|
70
|
-
|
|
71
72
|
// Icon
|
|
72
73
|
if (type == 'password') {
|
|
73
74
|
Icon = <IconWrapper
|
|
@@ -92,7 +93,7 @@ export function Field({ state, icon, iconPos: ip, ...props }: FieldProps) {
|
|
|
92
93
|
|
|
93
94
|
// Classname
|
|
94
95
|
const inputClassName = getInputClasses({ error, Icon, iconPos, type })
|
|
95
|
-
const commonProps = { id: props.
|
|
96
|
+
const commonProps = { id: props.id || props.name, value: value, className: inputClassName }
|
|
96
97
|
|
|
97
98
|
// Type has to be referenced as props.type for TS to be happy
|
|
98
99
|
if (!type || type == 'text' || type == 'password' || type == 'email' || type == 'filter' || type == 'search') {
|
|
@@ -137,14 +138,14 @@ function FieldContainer({ children, className, error }: { children: React.ReactN
|
|
|
137
138
|
)
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
function getInputClasses({ error, Icon, iconPos, type }: { error
|
|
141
|
-
const pl = 'pl-
|
|
142
|
-
const pr = 'pr-
|
|
143
|
-
const py = 'py-[
|
|
141
|
+
function getInputClasses({ error, Icon, iconPos, type }: { error?: Error, Icon?: React.ReactNode, iconPos: string, type?: string }) {
|
|
142
|
+
const pl = 'pl-[12px] pl-input-x'
|
|
143
|
+
const pr = 'pr-[12px] pr-input-x'
|
|
144
|
+
const py = 'py-[9px] py-input-y'
|
|
144
145
|
const plWithIcon = type == 'color' ? 'pl-9' : 'pl-8' // was sm:pl-8 pl-8, etc
|
|
145
146
|
const prWithIcon = type == 'color' ? 'pr-9' : 'pr-8'
|
|
146
147
|
return (
|
|
147
|
-
`block ${py} col-start-1 row-start-1 w-full rounded-md bg-white text-
|
|
148
|
+
`block ${py} col-start-1 row-start-1 w-full rounded-md bg-white text-input-size outline outline-1 -outline-offset-1 ` +
|
|
148
149
|
'placeholder:text-input-placeholder focus:outline focus:outline-2 focus:-outline-offset-2 ' +
|
|
149
150
|
(iconPos == 'right' && Icon ? `${pl} ${prWithIcon} ` : (Icon ? `${plWithIcon} ${pr} ` : `${pl} ${pr} `)) +
|
|
150
151
|
(error
|
|
@@ -175,6 +176,36 @@ function ColorSvg({ hex }: { hex?: string }) {
|
|
|
175
176
|
)
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
export function isFieldCached(prev: IsFieldCachedProps, next: IsFieldCachedProps) {
|
|
180
|
+
const path = prev.name
|
|
181
|
+
const state = prev.state || {}
|
|
182
|
+
// If state/satte-error values have changed, re-render!
|
|
183
|
+
if (deepFind(state, path) !== deepFind(next.state || {}, path)) {
|
|
184
|
+
// console.log(1, 'state changed', path)
|
|
185
|
+
return false
|
|
186
|
+
}
|
|
187
|
+
if (getErrorFromState(state, path) !== getErrorFromState(next.state || {}, path)) {
|
|
188
|
+
// console.log(2, 'error changed', path)
|
|
189
|
+
return false
|
|
190
|
+
}
|
|
191
|
+
// If deps have changed, re-render!
|
|
192
|
+
if ((next.deps?.length !== prev.deps?.length) || next.deps?.some((v, i) => v !== prev.deps?.[i])) {
|
|
193
|
+
// console.log(3, 'deps changed', path)
|
|
194
|
+
return false
|
|
195
|
+
}
|
|
196
|
+
// If any other props have changed, except onChange/onInputChange, re-render!
|
|
197
|
+
// In most cases, onChange/onInputChange remain the same and reference the same function...
|
|
198
|
+
for (const k in prev) {
|
|
199
|
+
if (k === 'state' || k === 'onChange' || k === 'onInputChange') continue
|
|
200
|
+
if (prev[k as keyof typeof prev] !== next[k as keyof typeof next]) {
|
|
201
|
+
// console.log(4, 'changed', path, k)
|
|
202
|
+
return false
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// All good, use cached version
|
|
206
|
+
return true
|
|
207
|
+
}
|
|
208
|
+
|
|
178
209
|
const style = css`
|
|
179
210
|
input {
|
|
180
211
|
appearance: textfield;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import { css } from 'twin.macro'
|
|
2
|
-
import {
|
|
3
|
-
import ReactSelect, { components, ControlProps, createFilter, OptionProps, SingleValueProps
|
|
4
|
-
|
|
3
|
+
import { memo } from 'react'
|
|
4
|
+
import ReactSelect, { components, ControlProps, createFilter, OptionProps, SingleValueProps, ClearIndicatorProps,
|
|
5
|
+
DropdownIndicatorProps, MultiValueRemoveProps } from 'react-select'
|
|
5
6
|
import { ChevronUpDownIcon, CheckCircleIcon, XMarkIcon } from '@heroicons/react/20/solid'
|
|
6
|
-
import {
|
|
7
|
+
import { isFieldCached } from 'nitro-web'
|
|
8
|
+
import { getErrorFromState, deepFind, twMerge } from 'nitro-web/util'
|
|
7
9
|
import { Errors } from 'nitro-web/types'
|
|
8
10
|
|
|
9
11
|
const filterFn = createFilter()
|
|
@@ -27,35 +29,38 @@ type SelectProps = {
|
|
|
27
29
|
/** The prefix to add to the input **/
|
|
28
30
|
prefix?: string
|
|
29
31
|
/** The onChange handler **/
|
|
30
|
-
onChange?: (event: { target: {
|
|
32
|
+
onChange?: (event: { target: { name: string, value: unknown } }) => void
|
|
31
33
|
/** The options to display in the dropdown **/
|
|
32
34
|
options: { value: unknown, label: string | React.ReactNode, fixed?: boolean, [key: string]: unknown }[]
|
|
33
35
|
/** The state object to get the value and check errors from **/
|
|
34
|
-
state?: { errors?: Errors, [key: string]:
|
|
36
|
+
state?: { errors?: Errors, [key: string]: any } // was unknown|unknown[]
|
|
35
37
|
/** Select variations **/
|
|
36
38
|
type?: 'country'|'customer'|''
|
|
39
|
+
/** Pass dependencies to break memoization, handy for onChange/onInputChange **/
|
|
40
|
+
deps?: unknown[]
|
|
37
41
|
/** All other props are passed to react-select **/
|
|
38
42
|
[key: string]: unknown
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
export const Select = memo(SelectBase, (prev, next) => {
|
|
46
|
+
return isFieldCached(prev, next)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
function SelectBase({ inputId, minMenuWidth, name, prefix='', onChange, options, state, type='', ...props }: SelectProps) {
|
|
50
|
+
let value: unknown|unknown[]
|
|
51
|
+
const error = getErrorFromState(state, name)
|
|
44
52
|
if (!name) throw new Error('Select component requires a `name` and `options` prop')
|
|
45
53
|
|
|
54
|
+
// Get value from value or state
|
|
55
|
+
if ('value' in props) value = props.value
|
|
56
|
+
else if (typeof state == 'object') value = deepFind(state, name)
|
|
57
|
+
|
|
58
|
+
// If multi-select, filter options by value
|
|
59
|
+
if (Array.isArray(value)) value = options.filter(o => (value as unknown[]).includes(o.value))
|
|
60
|
+
else value = options.find(o => value === o.value)
|
|
61
|
+
|
|
46
62
|
// Input is always controlled if state is passed in
|
|
47
|
-
if (
|
|
48
|
-
value = props.value
|
|
49
|
-
} else if (typeof state == 'object') {
|
|
50
|
-
value = options.find(o => o.value == util.deepFind(state, name))
|
|
51
|
-
if (typeof value == 'undefined') value = ''
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// An error matches this input path
|
|
55
|
-
for (const item of (state?.errors || [])) {
|
|
56
|
-
if (util.isRegex(name) && (item.title||'').match(name)) hasError = item
|
|
57
|
-
else if (item.title == name) hasError = item
|
|
58
|
-
}
|
|
63
|
+
if (typeof state == 'object' && typeof value == 'undefined') value = ''
|
|
59
64
|
|
|
60
65
|
return (
|
|
61
66
|
<div css={style} class={twMerge(`mt-2.5 mb-6 mt-input-before mb-input-after nitro-select ${props.className||''}`)}>
|
|
@@ -83,20 +88,26 @@ export function Select({ inputId, minMenuWidth, name, prefix='', onChange, optio
|
|
|
83
88
|
}}
|
|
84
89
|
menuPlacement="auto"
|
|
85
90
|
minMenuHeight={250}
|
|
86
|
-
onChange={!onChange ? undefined : (o) =>
|
|
91
|
+
onChange={!onChange ? undefined : (o) => {
|
|
92
|
+
// Array returned for multi-select
|
|
93
|
+
const value = Array.isArray(o)
|
|
94
|
+
? o.map(v => typeof v == 'object' && v !== null && 'value' in v ? v.value : v)
|
|
95
|
+
: (typeof o == 'object' && o !== null && 'value' in o ? o.value : o)
|
|
96
|
+
return onChange({ target: { name: name, value: value }})
|
|
97
|
+
}}
|
|
87
98
|
options={options}
|
|
88
99
|
value={value}
|
|
89
100
|
classNames={{
|
|
90
101
|
// Input container
|
|
91
|
-
control: (p) => getSelectStyle({ name: 'control', hasError: !!
|
|
102
|
+
control: (p) => getSelectStyle({ name: 'control', hasError: !!error, ...p }),
|
|
92
103
|
valueContainer: () => getSelectStyle({ name: 'valueContainer' }),
|
|
93
104
|
// Input container objects
|
|
94
|
-
input: () => getSelectStyle({ name: 'input', hasError: !!
|
|
105
|
+
input: () => getSelectStyle({ name: 'input', hasError: !!error }),
|
|
95
106
|
multiValue: () => getSelectStyle({ name: 'multiValue' }),
|
|
96
107
|
multiValueLabel: () => '',
|
|
97
108
|
multiValueRemove: () => getSelectStyle({ name: 'multiValueRemove' }),
|
|
98
109
|
placeholder: () => getSelectStyle({ name: 'placeholder' }),
|
|
99
|
-
singleValue: () => getSelectStyle({ name: 'singleValue', hasError: !!
|
|
110
|
+
singleValue: () => getSelectStyle({ name: 'singleValue', hasError: !!error }),
|
|
100
111
|
// Indicators
|
|
101
112
|
clearIndicator: () => getSelectStyle({ name: 'clearIndicator' }),
|
|
102
113
|
dropdownIndicator: () => getSelectStyle({ name: 'dropdownIndicator' }),
|
|
@@ -140,7 +151,7 @@ export function Select({ inputId, minMenuWidth, name, prefix='', onChange, optio
|
|
|
140
151
|
// isDisabled={true}
|
|
141
152
|
// maxMenuHeight={200}
|
|
142
153
|
/>
|
|
143
|
-
{
|
|
154
|
+
{error && <div class="mt-1.5 text-xs text-danger">{error.detail}</div>}
|
|
144
155
|
</div>
|
|
145
156
|
)
|
|
146
157
|
}
|
|
@@ -211,7 +222,7 @@ const DropdownIndicator = (props: DropdownIndicatorProps) => {
|
|
|
211
222
|
const ClearIndicator = (props: ClearIndicatorProps) => {
|
|
212
223
|
return (
|
|
213
224
|
<components.ClearIndicator {...props}>
|
|
214
|
-
<XMarkIcon className="size-4
|
|
225
|
+
<XMarkIcon className="size-4" />
|
|
215
226
|
</components.ClearIndicator>
|
|
216
227
|
)
|
|
217
228
|
}
|
|
@@ -219,7 +230,7 @@ const ClearIndicator = (props: ClearIndicatorProps) => {
|
|
|
219
230
|
const MultiValueRemove = (props: MultiValueRemoveProps) => {
|
|
220
231
|
return (
|
|
221
232
|
<components.MultiValueRemove {...props}>
|
|
222
|
-
<XMarkIcon className="size-
|
|
233
|
+
<XMarkIcon className="size-[1em] p-[1px]" />
|
|
223
234
|
</components.MultiValueRemove>
|
|
224
235
|
)
|
|
225
236
|
}
|
|
@@ -237,18 +248,19 @@ const selectStyles = {
|
|
|
237
248
|
// Based off https://www.jussivirtanen.fi/writing/styling-react-select-with-tailwind
|
|
238
249
|
// Input container
|
|
239
250
|
control: {
|
|
240
|
-
base: 'rounded-md bg-white hover:cursor-pointer text-
|
|
251
|
+
base: 'rounded-md bg-white hover:cursor-pointer text-input-size outline outline-1 -outline-offset-1 '
|
|
252
|
+
+ '!min-h-0 outline-input-border',
|
|
241
253
|
focus: 'outline-2 -outline-offset-2 outline-input-border-focus',
|
|
242
254
|
error: 'outline-danger',
|
|
243
255
|
},
|
|
244
|
-
valueContainer: 'py-[
|
|
256
|
+
valueContainer: 'py-[9px] px-[12px] py-input-y px-input-x gap-1',
|
|
245
257
|
// Input container objects
|
|
246
258
|
input: {
|
|
247
259
|
base: 'text-input',
|
|
248
260
|
error: 'text-red-900',
|
|
249
261
|
},
|
|
250
262
|
multiValue: 'bg-primary text-white rounded items-center pl-2 pr-1.5 gap-1.5',
|
|
251
|
-
multiValueLabel: '',
|
|
263
|
+
multiValueLabel: 'text-xs',
|
|
252
264
|
multiValueRemove: 'border border-black/10 bg-clip-content bg-white rounded-md text-foreground hover:bg-red-50',
|
|
253
265
|
placeholder: 'text-input-placeholder',
|
|
254
266
|
singleValue: {
|
|
@@ -261,8 +273,8 @@ const selectStyles = {
|
|
|
261
273
|
indicatorsContainer: 'p-1 px-2 gap-1',
|
|
262
274
|
indicatorSeparator: 'py-0.5 before:content-[""] before:block before:bg-gray-100 before:w-px before:h-full',
|
|
263
275
|
// Dropdown menu
|
|
264
|
-
menu: 'mt-1.5 border border-dropdown-ul-border bg-white rounded-md text-
|
|
265
|
-
groupHeading: 'ml-3 mt-2 mb-1 text-gray-500 text-
|
|
276
|
+
menu: 'mt-1.5 border border-dropdown-ul-border bg-white rounded-md text-input-size overflow-hidden shadow-dropdown-ul',
|
|
277
|
+
groupHeading: 'ml-3 mt-2 mb-1 text-gray-500 text-input-size',
|
|
266
278
|
noOptionsMessage: 'm-1 text-gray-500 p-2 bg-gray-50 border border-dashed border-gray-200 rounded-sm',
|
|
267
279
|
option: {
|
|
268
280
|
base: 'relative px-3 py-2 !flex items-center gap-2 cursor-default',
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Drop, Dropdown, Field, Select, Button, Checkbox, GithubLink, Modal, Calendar, injectedConfig,
|
|
3
|
-
Filters, FiltersHandleType,
|
|
4
|
-
FilterType,
|
|
2
|
+
Drop, Dropdown, Field, Select, Button as ButtonNitro, Checkbox, GithubLink, Modal, Calendar, injectedConfig,
|
|
3
|
+
Filters, FiltersHandleType, FilterType,
|
|
5
4
|
} from 'nitro-web'
|
|
6
5
|
import { getCountryOptions, getCurrencyOptions, ucFirst } from 'nitro-web/util'
|
|
7
6
|
import { Check } from 'lucide-react'
|
|
8
7
|
|
|
9
|
-
|
|
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) {
|
|
10
17
|
const [customerSearch, setCustomerSearch] = useState('')
|
|
11
18
|
const [showModal1, setShowModal1] = useState(false)
|
|
12
19
|
const [state, setState] = useState({
|
|
13
20
|
address: '',
|
|
14
21
|
amount: 100,
|
|
15
22
|
brandColor: '#F3CA5F',
|
|
23
|
+
colorsMulti: ['blue', 'green'],
|
|
16
24
|
country: 'nz',
|
|
17
25
|
currency: 'nzd', // can be commented too
|
|
18
26
|
date: Date.now(),
|
|
@@ -26,7 +34,7 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
26
34
|
})
|
|
27
35
|
const [filterState, setFilterState] = useState({})
|
|
28
36
|
const filtersRef = useRef<FiltersHandleType>(null)
|
|
29
|
-
const filters: FilterType[] = [
|
|
37
|
+
const filters: FilterType[] = useMemo(() => [
|
|
30
38
|
{
|
|
31
39
|
name: 'dateRange',
|
|
32
40
|
type: 'date',
|
|
@@ -46,16 +54,9 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
46
54
|
{ label: 'Rejected', value: 'rejected' },
|
|
47
55
|
],
|
|
48
56
|
},
|
|
49
|
-
]
|
|
57
|
+
], [])
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
// useEffect(() => {
|
|
53
|
-
// setTimeout(() => {
|
|
54
|
-
// setState({ ...state, amount: 123456, currency: 'usd', brandColor: '#8656ED' })
|
|
55
|
-
// }, 2000)
|
|
56
|
-
// }, [])
|
|
57
|
-
|
|
58
|
-
const options = [
|
|
59
|
+
const options = useMemo(() => [
|
|
59
60
|
{ label: 'Open customer preview' },
|
|
60
61
|
{ label: 'Add a payment', isSelected: true },
|
|
61
62
|
{ label: 'Email invoice' },
|
|
@@ -63,10 +64,12 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
63
64
|
{ label: 'Edit' },
|
|
64
65
|
{ label: 'Copy' },
|
|
65
66
|
{ label: 'Delete' },
|
|
66
|
-
]
|
|
67
|
+
], [])
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
const Button = elements?.Button || ButtonNitro
|
|
70
|
+
|
|
71
|
+
function onCustomerInputChange (e: { target: { name: string, value: unknown } }) {
|
|
72
|
+
if (e.target.name == 'customer' && e.target.value == '0') {
|
|
70
73
|
setCustomerSearch('')
|
|
71
74
|
e.target.value = null // clear the select's selected value
|
|
72
75
|
setTimeout(() => alert('Adding new customer...'), 0)
|
|
@@ -78,6 +81,13 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
78
81
|
setCustomerSearch(search || '')
|
|
79
82
|
}
|
|
80
83
|
|
|
84
|
+
// Example of updating state
|
|
85
|
+
// useEffect(() => {
|
|
86
|
+
// setTimeout(() => {
|
|
87
|
+
// setState({ ...state, amount: 123456, currency: 'usd', brandColor: '#8656ED' })
|
|
88
|
+
// }, 2000)
|
|
89
|
+
// }, [])
|
|
90
|
+
|
|
81
91
|
return (
|
|
82
92
|
<div class={`text-left max-w-[1100px] ${className}`}>
|
|
83
93
|
<GithubLink filename={__filename} />
|
|
@@ -139,7 +149,8 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
139
149
|
<Field
|
|
140
150
|
class="!my-0 min-w-[242px]"
|
|
141
151
|
type="search"
|
|
142
|
-
name="search"
|
|
152
|
+
name="search"
|
|
153
|
+
id="search2"
|
|
143
154
|
iconPos="left"
|
|
144
155
|
state={filterState}
|
|
145
156
|
onChange={(e) => {
|
|
@@ -154,7 +165,10 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
154
165
|
<div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
|
|
155
166
|
<div><Button color="primary">primary (default)</Button></div>
|
|
156
167
|
<div><Button color="secondary">secondary button</Button></div>
|
|
168
|
+
<div><Button color="black">black button</Button></div>
|
|
169
|
+
<div><Button color="dark">dark button</Button></div>
|
|
157
170
|
<div><Button color="white">white button</Button></div>
|
|
171
|
+
<div><Button color="clear">clear button</Button></div>
|
|
158
172
|
<div><Button color="primary" size="xs">*-xs button</Button></div>
|
|
159
173
|
<div><Button color="primary" size="sm">*-sm button</Button></div>
|
|
160
174
|
<div><Button color="primary">*-md (default)</Button></div>
|
|
@@ -196,22 +210,31 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
196
210
|
// menuIsOpen={true}
|
|
197
211
|
name="action"
|
|
198
212
|
isSearchable={false}
|
|
199
|
-
options={[
|
|
213
|
+
options={useMemo(() => [
|
|
200
214
|
{ value: 'edit', label: 'Edit' },
|
|
201
215
|
{ value: 'delete', label: 'Delete' },
|
|
202
|
-
]}
|
|
216
|
+
], [])}
|
|
203
217
|
/>
|
|
204
218
|
</div>
|
|
205
219
|
<div>
|
|
206
|
-
<label for="
|
|
220
|
+
<label for="colorsMulti">Mutli Select</label>
|
|
207
221
|
<Select
|
|
208
|
-
name="
|
|
222
|
+
name="colorsMulti"
|
|
209
223
|
isMulti={true}
|
|
210
|
-
|
|
224
|
+
state={state}
|
|
225
|
+
options={useMemo(() => [
|
|
211
226
|
{ value: 'blue', label: 'Blue' },
|
|
212
227
|
{ value: 'green', label: 'Green' },
|
|
213
228
|
{ value: 'yellow', label: 'Yellow' },
|
|
214
|
-
|
|
229
|
+
{ value: 'red', label: 'Red' },
|
|
230
|
+
{ value: 'orange', label: 'Orange' },
|
|
231
|
+
{ value: 'purple', label: 'Purple' },
|
|
232
|
+
{ value: 'pink', label: 'Pink' },
|
|
233
|
+
{ value: 'gray', label: 'Gray' },
|
|
234
|
+
{ value: 'black', label: 'Black' },
|
|
235
|
+
{ value: 'white', label: 'White' },
|
|
236
|
+
], [])}
|
|
237
|
+
onChange={(e) => onChange(setState, e)}
|
|
215
238
|
/>
|
|
216
239
|
</div>
|
|
217
240
|
<div>
|
|
@@ -235,7 +258,7 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
235
258
|
state={state}
|
|
236
259
|
onChange={onCustomerInputChange}
|
|
237
260
|
onInputChange={onCustomerSearch}
|
|
238
|
-
options={[
|
|
261
|
+
options={useMemo(() => [
|
|
239
262
|
{
|
|
240
263
|
className: 'bb',
|
|
241
264
|
fixed: true,
|
|
@@ -250,7 +273,7 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
250
273
|
{ value: '1', label: 'Iron Man Industries' },
|
|
251
274
|
{ value: '2', label: 'Captain America' },
|
|
252
275
|
{ value: '3', label: 'Thor Limited' },
|
|
253
|
-
]}
|
|
276
|
+
], [customerSearch])}
|
|
254
277
|
/>
|
|
255
278
|
</div>
|
|
256
279
|
<div>
|
|
@@ -282,8 +305,8 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
282
305
|
<Field name="password" type="password"/>
|
|
283
306
|
</div>
|
|
284
307
|
<div>
|
|
285
|
-
<label for="
|
|
286
|
-
<Field name="search" type="search" placeholder="Search..." />
|
|
308
|
+
<label for="search3">Search</label>
|
|
309
|
+
<Field name="search" id="search3" type="search" placeholder="Search..." />
|
|
287
310
|
</div>
|
|
288
311
|
<div>
|
|
289
312
|
<label for="filter">Filter by Code</label>
|
|
@@ -324,22 +347,6 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
324
347
|
</div>
|
|
325
348
|
</div>
|
|
326
349
|
|
|
327
|
-
<h2 class="h3">File Inputs & Calendar</h2>
|
|
328
|
-
<div class="grid grid-cols-3 gap-x-6">
|
|
329
|
-
<div>
|
|
330
|
-
<label for="avatar">Avatar</label>
|
|
331
|
-
<Drop class="is-small" name="avatar" state={state} onChange={(e) => onChange(setState, e)} awsUrl={injectedConfig.awsUrl} />
|
|
332
|
-
</div>
|
|
333
|
-
<div>
|
|
334
|
-
<label for="calendar">Calendar</label>
|
|
335
|
-
<Calendar mode="range" value={state.calendar} numberOfMonths={1}
|
|
336
|
-
onChange={(mode, value) => {
|
|
337
|
-
onChange(setState, { target: { id: 'calendar', value: value } })
|
|
338
|
-
}}
|
|
339
|
-
/>
|
|
340
|
-
</div>
|
|
341
|
-
</div>
|
|
342
|
-
|
|
343
350
|
<Modal show={showModal1} setShow={setShowModal1} class="p-9">
|
|
344
351
|
<h3 class="h3">Edit Profile</h3>
|
|
345
352
|
<p class="mb-5">An example modal containing a basic form for editing profiles.</p>
|
|
@@ -357,6 +364,24 @@ export function Styleguide({ className }: { className?: string }) {
|
|
|
357
364
|
<Button color="primary" onClick={() => setShowModal1(false)}>Save</Button>
|
|
358
365
|
</div>
|
|
359
366
|
</Modal>
|
|
367
|
+
|
|
368
|
+
<h2 class="h3">File Inputs & Calendar</h2>
|
|
369
|
+
<div class="grid grid-cols-3 gap-x-6 mb-4 last:mb-0">
|
|
370
|
+
<div>
|
|
371
|
+
<label for="avatar">Avatar</label>
|
|
372
|
+
<Drop class="is-small" name="avatar" state={state} onChange={(e) => onChange(setState, e)} awsUrl={injectedConfig.awsUrl} />
|
|
373
|
+
</div>
|
|
374
|
+
<div>
|
|
375
|
+
<label for="calendar">Calendar</label>
|
|
376
|
+
<Calendar mode="range" value={state.calendar} numberOfMonths={1}
|
|
377
|
+
onChange={(mode, value) => {
|
|
378
|
+
onChange(setState, { target: { name: 'calendar', value: value } })
|
|
379
|
+
}}
|
|
380
|
+
/>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
{children}
|
|
360
385
|
</div>
|
|
361
386
|
)
|
|
362
387
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.51",
|
|
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 🚀",
|
package/types/util.d.ts
CHANGED
|
@@ -257,6 +257,21 @@ export function getCurrencyOptions(currencies: {
|
|
|
257
257
|
value: string;
|
|
258
258
|
label: string;
|
|
259
259
|
}[];
|
|
260
|
+
/**
|
|
261
|
+
* Returns an error from a state object matching the path
|
|
262
|
+
* @param {{ errors?: { title: string, detail: string }[] }|undefined} state
|
|
263
|
+
* @param {string} path
|
|
264
|
+
* @returns {{ title: string, detail: string }|undefined}
|
|
265
|
+
*/
|
|
266
|
+
export function getErrorFromState(state: {
|
|
267
|
+
errors?: {
|
|
268
|
+
title: string;
|
|
269
|
+
detail: string;
|
|
270
|
+
}[];
|
|
271
|
+
} | undefined, path: string): {
|
|
272
|
+
title: string;
|
|
273
|
+
detail: string;
|
|
274
|
+
} | undefined;
|
|
260
275
|
/**
|
|
261
276
|
* Get the width of a prefix
|
|
262
277
|
* @param {string} prefix
|
|
@@ -466,7 +481,7 @@ export function omit(obj: {
|
|
|
466
481
|
*
|
|
467
482
|
* @template T
|
|
468
483
|
* @param {React.Dispatch<React.SetStateAction<T>>} setState
|
|
469
|
-
* @param {{target: {
|
|
484
|
+
* @param {{target: {name: string, value: unknown}}|[string, function|unknown]} eventOrPathValue
|
|
470
485
|
* @param {Function} [beforeSetState] - optional function to run before setting the state
|
|
471
486
|
* @returns {Promise<T>}
|
|
472
487
|
*
|
|
@@ -476,7 +491,7 @@ export function omit(obj: {
|
|
|
476
491
|
*/
|
|
477
492
|
export function onChange<T>(setState: React.Dispatch<React.SetStateAction<T>>, eventOrPathValue: {
|
|
478
493
|
target: {
|
|
479
|
-
|
|
494
|
+
name: string;
|
|
480
495
|
value: unknown;
|
|
481
496
|
};
|
|
482
497
|
} | [string, Function | unknown], beforeSetState?: Function): Promise<T>;
|
package/types/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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"}
|
package/types.ts
CHANGED
package/util.js
CHANGED
|
@@ -681,6 +681,20 @@ export function getCurrencyOptions (currencies) {
|
|
|
681
681
|
return output
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
+
/**
|
|
685
|
+
* Returns an error from a state object matching the path
|
|
686
|
+
* @param {{ errors?: { title: string, detail: string }[] }|undefined} state
|
|
687
|
+
* @param {string} path
|
|
688
|
+
* @returns {{ title: string, detail: string }|undefined}
|
|
689
|
+
*/
|
|
690
|
+
export function getErrorFromState (state, path) {
|
|
691
|
+
if (!state || !state.errors) return undefined
|
|
692
|
+
for (const item of state.errors) {
|
|
693
|
+
if (isRegex(path) && (item.title || '').match(path)) return item
|
|
694
|
+
else if (item.title == path) return item
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
684
698
|
/**
|
|
685
699
|
* Get the width of a prefix
|
|
686
700
|
* @param {string} prefix
|
|
@@ -1106,7 +1120,7 @@ export function omit (obj, fields) {
|
|
|
1106
1120
|
*
|
|
1107
1121
|
* @template T
|
|
1108
1122
|
* @param {React.Dispatch<React.SetStateAction<T>>} setState
|
|
1109
|
-
* @param {{target: {
|
|
1123
|
+
* @param {{target: {name: string, value: unknown}}|[string, function|unknown]} eventOrPathValue
|
|
1110
1124
|
* @param {Function} [beforeSetState] - optional function to run before setting the state
|
|
1111
1125
|
* @returns {Promise<T>}
|
|
1112
1126
|
*
|
|
@@ -1124,7 +1138,7 @@ export function onChange (setState, eventOrPathValue, beforeSetState) {
|
|
|
1124
1138
|
|
|
1125
1139
|
if (typeof eventOrPathValue === 'object' && 'target' in eventOrPathValue) {
|
|
1126
1140
|
const element = /** @type {HTMLInputElement & {_value?: unknown}} */(eventOrPathValue.target) // we need to assume this is an input
|
|
1127
|
-
chunks = (element.
|
|
1141
|
+
chunks = (element.name || element.id).split('.')
|
|
1128
1142
|
hasFiles = !!element.files
|
|
1129
1143
|
value = element.files
|
|
1130
1144
|
? element.files[0]
|
|
@@ -1561,7 +1575,20 @@ export function trim (string) {
|
|
|
1561
1575
|
*/
|
|
1562
1576
|
export function twMerge(...args) {
|
|
1563
1577
|
const ignoredClasses = /** @type {string[]} */([])
|
|
1564
|
-
const ignoreClasses = [
|
|
1578
|
+
const ignoreClasses = [
|
|
1579
|
+
'text-button-size',
|
|
1580
|
+
'text-input-size',
|
|
1581
|
+
|
|
1582
|
+
'ring-button-primary-ring',
|
|
1583
|
+
'ring-button-secondary-ring',
|
|
1584
|
+
'ring-button-black-ring',
|
|
1585
|
+
'ring-button-dark-ring',
|
|
1586
|
+
'ring-button-white-ring',
|
|
1587
|
+
'ring-button-clear-ring',
|
|
1588
|
+
|
|
1589
|
+
'bg-button-dark',
|
|
1590
|
+
'bg-button-dark-hover',
|
|
1591
|
+
]
|
|
1565
1592
|
const classes = args.filter(Boolean).join(' ').split(' ')
|
|
1566
1593
|
|
|
1567
1594
|
const filteredClasses = classes.filter(c => {
|