nitro-web 0.0.50 → 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 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
- white: 'bg-white ring-1 ring-inset ring-gray-300 hover:bg-gray-50 text-gray-900 [&>.loader]:border-black',
40
- clear: 'ring-1 ring-inset ring-gray-300 hover:bg-gray-50 text-foreground [&>.loader]:border-foreground !shadow-none',
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-2 py-1 px-button-x-xs py-button-y-xs text-xs rounded',
46
- sm: 'px-2.5 py-1.5 px-button-x-sm py-button-y-sm text-sm text-sm-button rounded-md',
47
- md: 'px-3 py-[0.58rem] px-button-x-md py-button-y-md text-sm text-sm-button rounded-md', // default
48
- lg: 'px-3.5 py-2.5 px-button-x-lg py-button-y-lg text-sm text-sm-button rounded-md',
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} ${colors[color]} ${sizes[size]} ${contentLayout} ${loading} nitro-button ${className||''}`)} {...props}>
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>
@@ -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-xs text-white bg-primary w-[19px] h-[19px] ${buttonCounterClassName || ''}`)}
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 { isRegex, deepFind, s3Image } from 'nitro-web/util'
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'
@@ -29,7 +29,7 @@ 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
- let error: Error | unknown
32
+ const error = getErrorFromState(state, name)
33
33
  const inputId = id || name
34
34
  const [urls, setUrls] = useState([])
35
35
  const stateRef = useRef(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])
@@ -149,7 +149,7 @@ export function FieldCurrency({ config, currency='nzd', onChange, value, default
149
149
  defaultValue={defaultValue}
150
150
  />
151
151
  <span
152
- class={`absolute top-0 bottom-0 left-3 inline-flex items-center select-none text-gray-500 text-sm text-sm-input ${dollars !== null && settings.prefix == '$' ? 'text-foreground' : ''}`}
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' : ''}`}
153
153
  >
154
154
  {settings.prefix || settings.suffix}
155
155
  </span>
@@ -135,7 +135,7 @@ export function FieldDate({
135
135
  {
136
136
  prefix &&
137
137
  // Similar classNames to the input.tsx:IconWrapper()
138
- <span className="z-[0] col-start-1 row-start-1 self-center select-none justify-self-start text-sm text-sm-input ml-3">
138
+ <span className="z-[0] col-start-1 row-start-1 self-center select-none justify-self-start text-input-size ml-3">
139
139
  {prefix}
140
140
  </span>
141
141
  }
@@ -1,15 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { css } from 'twin.macro'
3
- import { util, FieldCurrency, FieldCurrencyProps, FieldColor, FieldColorProps, FieldDate, FieldDateProps, twMerge } from 'nitro-web'
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
- EnvelopeIcon,
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>
@@ -24,6 +19,8 @@ type FieldExtraProps = {
24
19
  type?: 'text' | 'password' | 'email' | 'filter' | 'search' | 'textarea' | 'currency' | 'date' | 'color'
25
20
  icon?: React.ReactNode
26
21
  iconPos?: 'left' | 'right'
22
+ /** Pass dependencies to break memoization, handy for onChange/onInputChange **/
23
+ deps?: unknown[]
27
24
  }
28
25
  type IconWrapperProps = {
29
26
  iconPos: string
@@ -38,12 +35,21 @@ export type FieldProps = (
38
35
  | ({ type: 'color' } & FieldColorProps & FieldExtraProps)
39
36
  | ({ type: 'date' } & FieldDateProps & FieldExtraProps)
40
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
+ })
41
47
 
42
- export function Field({ state, icon, iconPos: ip, ...props }: FieldProps) {
48
+ function FieldBase({ state, icon, iconPos: ip, ...props }: FieldProps) {
43
49
  // type must be kept as props.type for TS to be happy and follow the conditions below
44
- let error!: Error
45
50
  let value!: string
46
51
  let Icon!: React.ReactNode
52
+ const error = getErrorFromState(state, props.name)
47
53
  const type = props.type
48
54
  const iconPos = ip == 'left' || (type == 'color' && !ip) ? 'left' : 'right'
49
55
 
@@ -59,16 +65,10 @@ export function Field({ state, icon, iconPos: ip, ...props }: FieldProps) {
59
65
  // Value: Input is always controlled if state is passed in
60
66
  if (props.value) value = props.value as string
61
67
  else if (typeof state == 'object') {
62
- const v = util.deepFind(state, props.name) as string | undefined
68
+ const v = deepFind(state, props.name) as string | undefined
63
69
  value = v ?? ''
64
70
  }
65
71
 
66
- // Errors: find any that match this field path
67
- for (const item of (state?.errors || [])) {
68
- if (util.isRegex(props.name) && (item.title || '').match(props.name)) error = item
69
- else if (item.title == props.name) error = item
70
- }
71
-
72
72
  // Icon
73
73
  if (type == 'password') {
74
74
  Icon = <IconWrapper
@@ -138,14 +138,14 @@ function FieldContainer({ children, className, error }: { children: React.ReactN
138
138
  )
139
139
  }
140
140
 
141
- function getInputClasses({ error, Icon, iconPos, type }: { error: Error, Icon?: React.ReactNode, iconPos: string, type?: string }) {
142
- const pl = 'pl-3 pl-input-x'
143
- const pr = 'pr-3 pr-input-x'
144
- const py = 'py-[0.58rem] py-input-y'
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'
145
145
  const plWithIcon = type == 'color' ? 'pl-9' : 'pl-8' // was sm:pl-8 pl-8, etc
146
146
  const prWithIcon = type == 'color' ? 'pr-9' : 'pr-8'
147
147
  return (
148
- `block ${py} col-start-1 row-start-1 w-full rounded-md bg-white text-sm text-sm-input outline outline-1 -outline-offset-1 ` +
148
+ `block ${py} col-start-1 row-start-1 w-full rounded-md bg-white text-input-size outline outline-1 -outline-offset-1 ` +
149
149
  'placeholder:text-input-placeholder focus:outline focus:outline-2 focus:-outline-offset-2 ' +
150
150
  (iconPos == 'right' && Icon ? `${pl} ${prWithIcon} ` : (Icon ? `${plWithIcon} ${pr} ` : `${pl} ${pr} `)) +
151
151
  (error
@@ -176,6 +176,36 @@ function ColorSvg({ hex }: { hex?: string }) {
176
176
  )
177
177
  }
178
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
+
179
209
  const style = css`
180
210
  input {
181
211
  appearance: textfield;
@@ -1,7 +1,7 @@
1
1
  import { Errors } from 'nitro-web/types'
2
2
 
3
3
  type FormError = {
4
- state: { errors: Errors },
4
+ state: { errors?: Errors },
5
5
  // display all errors except these field titles, e.g. ['name', 'address']
6
6
  fields?: Array<string>,
7
7
  className?: string,
@@ -1,9 +1,11 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import { css } from 'twin.macro'
2
- import { twMerge } from 'nitro-web'
3
- import ReactSelect, { components, ControlProps, createFilter, OptionProps, SingleValueProps } from 'react-select'
4
- import { ClearIndicatorProps, DropdownIndicatorProps, MultiValueRemoveProps } from 'react-select'
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 { util } from 'nitro-web'
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()
@@ -31,31 +33,34 @@ type SelectProps = {
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]: unknown }
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 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
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 (props.value) {
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) => onChange({ target: { name: name, value: (o as {value?: unknown})?.value || 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: !!hasError, ...p }),
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: !!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: !!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
- {hasError && <div class="mt-1.5 text-xs text-danger">{hasError.detail}</div>}
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 my-0.5" />
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-4 p-[1px]" />
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-sm text-sm-input outline outline-1 -outline-offset-1 outline-input-border',
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-[0.58rem] px-3 py-input-y px-input-x gap-1',
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-sm text-sm-input overflow-hidden shadow-dropdown-ul',
265
- groupHeading: 'ml-3 mt-2 mb-1 text-gray-500 text-sm text-sm-input',
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
- export function Styleguide({ className }: { className?: string }) {
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
- // Example of updating state
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,7 +64,9 @@ export function Styleguide({ className }: { className?: string }) {
63
64
  { label: 'Edit' },
64
65
  { label: 'Copy' },
65
66
  { label: 'Delete' },
66
- ]
67
+ ], [])
68
+
69
+ const Button = elements?.Button || ButtonNitro
67
70
 
68
71
  function onCustomerInputChange (e: { target: { name: string, value: unknown } }) {
69
72
  if (e.target.name == 'customer' && e.target.value == '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} />
@@ -155,7 +165,10 @@ export function Styleguide({ className }: { className?: string }) {
155
165
  <div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
156
166
  <div><Button color="primary">primary (default)</Button></div>
157
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>
158
170
  <div><Button color="white">white button</Button></div>
171
+ <div><Button color="clear">clear button</Button></div>
159
172
  <div><Button color="primary" size="xs">*-xs button</Button></div>
160
173
  <div><Button color="primary" size="sm">*-sm button</Button></div>
161
174
  <div><Button color="primary">*-md (default)</Button></div>
@@ -197,22 +210,31 @@ export function Styleguide({ className }: { className?: string }) {
197
210
  // menuIsOpen={true}
198
211
  name="action"
199
212
  isSearchable={false}
200
- options={[
213
+ options={useMemo(() => [
201
214
  { value: 'edit', label: 'Edit' },
202
215
  { value: 'delete', label: 'Delete' },
203
- ]}
216
+ ], [])}
204
217
  />
205
218
  </div>
206
219
  <div>
207
- <label for="multi">Mutli Select</label>
220
+ <label for="colorsMulti">Mutli Select</label>
208
221
  <Select
209
- name="multi"
222
+ name="colorsMulti"
210
223
  isMulti={true}
211
- options={[
224
+ state={state}
225
+ options={useMemo(() => [
212
226
  { value: 'blue', label: 'Blue' },
213
227
  { value: 'green', label: 'Green' },
214
228
  { value: 'yellow', label: 'Yellow' },
215
- ]}
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)}
216
238
  />
217
239
  </div>
218
240
  <div>
@@ -236,7 +258,7 @@ export function Styleguide({ className }: { className?: string }) {
236
258
  state={state}
237
259
  onChange={onCustomerInputChange}
238
260
  onInputChange={onCustomerSearch}
239
- options={[
261
+ options={useMemo(() => [
240
262
  {
241
263
  className: 'bb',
242
264
  fixed: true,
@@ -251,7 +273,7 @@ export function Styleguide({ className }: { className?: string }) {
251
273
  { value: '1', label: 'Iron Man Industries' },
252
274
  { value: '2', label: 'Captain America' },
253
275
  { value: '3', label: 'Thor Limited' },
254
- ]}
276
+ ], [customerSearch])}
255
277
  />
256
278
  </div>
257
279
  <div>
@@ -325,22 +347,6 @@ export function Styleguide({ className }: { className?: string }) {
325
347
  </div>
326
348
  </div>
327
349
 
328
- <h2 class="h3">File Inputs & Calendar</h2>
329
- <div class="grid grid-cols-3 gap-x-6">
330
- <div>
331
- <label for="avatar">Avatar</label>
332
- <Drop class="is-small" name="avatar" state={state} onChange={(e) => onChange(setState, e)} awsUrl={injectedConfig.awsUrl} />
333
- </div>
334
- <div>
335
- <label for="calendar">Calendar</label>
336
- <Calendar mode="range" value={state.calendar} numberOfMonths={1}
337
- onChange={(mode, value) => {
338
- onChange(setState, { target: { name: 'calendar', value: value } })
339
- }}
340
- />
341
- </div>
342
- </div>
343
-
344
350
  <Modal show={showModal1} setShow={setShowModal1} class="p-9">
345
351
  <h3 class="h3">Edit Profile</h3>
346
352
  <p class="mb-5">An example modal containing a basic form for editing profiles.</p>
@@ -358,6 +364,24 @@ export function Styleguide({ className }: { className?: string }) {
358
364
  <Button color="primary" onClick={() => setShowModal1(false)}>Save</Button>
359
365
  </div>
360
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}
361
385
  </div>
362
386
  )
363
387
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-web",
3
- "version": "0.0.50",
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
@@ -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
@@ -31,7 +31,7 @@ export type User = {
31
31
  }
32
32
 
33
33
  export type Error = { title: string, detail: string }
34
- export type Errors = Array<Error> | null
34
+ export type Errors = Error[]
35
35
 
36
36
  export type MonasteryImage = {
37
37
  url: string
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
@@ -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 = ['text-sm-button', 'text-sm-input']
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 => {