nitro-web 0.0.47 → 0.0.48
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/partials/element/button.tsx +2 -1
- package/components/partials/element/dropdown.tsx +19 -13
- package/components/partials/element/filters.tsx +183 -0
- package/components/partials/form/field-color.tsx +1 -1
- package/components/partials/form/field-date.tsx +19 -6
- package/components/partials/form/field.tsx +2 -2
- package/components/partials/form/select.tsx +2 -2
- package/components/partials/styleguide.tsx +56 -4
- package/package.json +1 -1
- package/types/util.d.ts +17 -10
- package/types/util.d.ts.map +1 -1
- package/util.js +19 -6
package/client/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ export { Avatar } from '../components/partials/element/avatar'
|
|
|
29
29
|
export { Button } from '../components/partials/element/button'
|
|
30
30
|
export { Calendar, type CalendarProps } from '../components/partials/element/calendar'
|
|
31
31
|
export { Dropdown } from '../components/partials/element/dropdown'
|
|
32
|
+
export { Filters, type FiltersHandleType, type FilterType } from '../components/partials/element/filters'
|
|
32
33
|
export { GithubLink } from '../components/partials/element/github-link'
|
|
33
34
|
export { Initials } from '../components/partials/element/initials'
|
|
34
35
|
export { Message } from '../components/partials/element/message'
|
|
@@ -36,7 +37,6 @@ export { Modal } from '../components/partials/element/modal'
|
|
|
36
37
|
export { Sidebar, type SidebarProps } from '../components/partials/element/sidebar'
|
|
37
38
|
export { Tooltip } from '../components/partials/element/tooltip'
|
|
38
39
|
export { Topbar } from '../components/partials/element/topbar'
|
|
39
|
-
|
|
40
40
|
// Component Form
|
|
41
41
|
export { Checkbox } from '../components/partials/form/checkbox'
|
|
42
42
|
export { Drop } from '../components/partials/form/drop'
|
|
@@ -2,7 +2,7 @@ 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'
|
|
5
|
+
color?: 'primary'|'secondary'|'black'|'white'|'clear'
|
|
6
6
|
size?: 'xs'|'sm'|'md'|'lg'
|
|
7
7
|
className?: string
|
|
8
8
|
isLoading?: boolean
|
|
@@ -37,6 +37,7 @@ export function Button({
|
|
|
37
37
|
secondary: 'bg-secondary hover:bg-secondary-hover',
|
|
38
38
|
black: 'bg-black hover:bg-gray-700',
|
|
39
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',
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// Button sizes
|
|
@@ -16,7 +16,8 @@ type DropdownProps = {
|
|
|
16
16
|
/** The minimum width of the menu **/
|
|
17
17
|
minWidth?: number | string
|
|
18
18
|
/** The content to render inside the top of the dropdown **/
|
|
19
|
-
|
|
19
|
+
menuContent?: React.ReactNode
|
|
20
|
+
menuClassName?: string
|
|
20
21
|
menuIsOpen?: boolean
|
|
21
22
|
menuToggles?: boolean
|
|
22
23
|
toggleCallback?: (isActive: boolean) => void
|
|
@@ -30,7 +31,8 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
30
31
|
options,
|
|
31
32
|
isHoverable,
|
|
32
33
|
minWidth,
|
|
33
|
-
|
|
34
|
+
menuClassName,
|
|
35
|
+
menuContent,
|
|
34
36
|
menuIsOpen,
|
|
35
37
|
menuToggles=true,
|
|
36
38
|
toggleCallback,
|
|
@@ -39,7 +41,7 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
39
41
|
isHoverable = isHoverable && !menuIsOpen
|
|
40
42
|
const dropdownRef = useRef<HTMLDivElement|null>(null)
|
|
41
43
|
const [isActive, setIsActive] = useState(!!menuIsOpen)
|
|
42
|
-
const menuStyle = getSelectStyle({ name: 'menu'
|
|
44
|
+
const menuStyle = getSelectStyle({ name: 'menu' })
|
|
43
45
|
|
|
44
46
|
// Expose the setIsActive function to the parent component
|
|
45
47
|
useImperativeHandle(ref, () => ({ setIsActive }))
|
|
@@ -106,9 +108,9 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
106
108
|
}
|
|
107
109
|
<ul
|
|
108
110
|
style={{ minWidth }}
|
|
109
|
-
class={`${menuStyle} absolute invisible opacity-0 select-none min-w-full z-[1]`}
|
|
111
|
+
class={`${menuStyle} absolute invisible opacity-0 select-none min-w-full z-[1] ${menuClassName}`}
|
|
110
112
|
>
|
|
111
|
-
{
|
|
113
|
+
{menuContent}
|
|
112
114
|
{
|
|
113
115
|
options && options.map((option, i) => {
|
|
114
116
|
const optionStyle = getSelectStyle({ name: 'option', usePrefixes: true, isSelected: option.isSelected })
|
|
@@ -131,10 +133,12 @@ export const Dropdown = forwardRef(function Dropdown({
|
|
|
131
133
|
})
|
|
132
134
|
|
|
133
135
|
const style = css`
|
|
134
|
-
ul {
|
|
136
|
+
&>ul {
|
|
135
137
|
transition: transform 0.15s ease, opacity 0.15s ease, visibility 0s 0.15s ease, max-width 0s 0.15s ease, max-height 0s 0.15s ease;
|
|
136
138
|
max-width: 0; // handy if the dropdown ul exceeds the viewport width
|
|
137
139
|
max-height: 0; // handy if the dropdown ul exceeds the viewport height
|
|
140
|
+
/* overflow: visible !important; // override menustyle */
|
|
141
|
+
pointer-events: none;
|
|
138
142
|
}
|
|
139
143
|
&.is-bottom-right,
|
|
140
144
|
&.is-top-right {
|
|
@@ -145,14 +149,14 @@ const style = css`
|
|
|
145
149
|
}
|
|
146
150
|
&.is-bottom-left,
|
|
147
151
|
&.is-bottom-right {
|
|
148
|
-
ul {
|
|
152
|
+
&>ul {
|
|
149
153
|
top: 100%;
|
|
150
154
|
transform: translateY(6px);
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
157
|
&.is-top-left,
|
|
154
158
|
&.is-top-right {
|
|
155
|
-
ul {
|
|
159
|
+
&>ul {
|
|
156
160
|
bottom: 100%;
|
|
157
161
|
transform: translateY(-10px);
|
|
158
162
|
}
|
|
@@ -161,15 +165,17 @@ const style = css`
|
|
|
161
165
|
&.is-hoverable:hover,
|
|
162
166
|
&:focus,
|
|
163
167
|
&.is-active,
|
|
164
|
-
li:hover,
|
|
165
|
-
li:focus,
|
|
166
|
-
li.is-active {
|
|
167
|
-
ul {
|
|
168
|
+
&>ul>li:hover,
|
|
169
|
+
&>ul>li:focus,
|
|
170
|
+
&>ul>li.is-active {
|
|
171
|
+
&>ul {
|
|
168
172
|
opacity: 1;
|
|
169
173
|
visibility: visible;
|
|
170
174
|
transition: transform 0.15s ease, opacity 0.15s ease;
|
|
171
175
|
max-width: 1000px;
|
|
172
176
|
max-height: 1000px;
|
|
177
|
+
pointer-events: auto;
|
|
178
|
+
overflow: visible;
|
|
173
179
|
}
|
|
174
180
|
&.is-bottom-left > ul,
|
|
175
181
|
&.is-bottom-right > ul {
|
|
@@ -182,7 +188,7 @@ const style = css`
|
|
|
182
188
|
}
|
|
183
189
|
// no animation
|
|
184
190
|
&.no-animation {
|
|
185
|
-
ul {
|
|
191
|
+
&>ul {
|
|
186
192
|
transition: none;
|
|
187
193
|
}
|
|
188
194
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { forwardRef, Dispatch, SetStateAction, useRef, useEffect, useImperativeHandle } from 'react'
|
|
2
|
+
import { Button, Dropdown, Field, Select, twMerge } from 'nitro-web'
|
|
3
|
+
import { camelCaseToTitle, debounce, omit, queryString, queryObject } from 'nitro-web/util'
|
|
4
|
+
import { ListFilterIcon } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
export type FilterType = {
|
|
7
|
+
name: string
|
|
8
|
+
type: 'text'|'date'|'search'|'select'
|
|
9
|
+
label?: string
|
|
10
|
+
enums?: { label: string, value: string }[]
|
|
11
|
+
placeholder?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type FilterState = {
|
|
15
|
+
[key: string]: string | true
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type FiltersProps = {
|
|
19
|
+
filters?: FilterType[]
|
|
20
|
+
state: FilterState
|
|
21
|
+
setState: Dispatch<SetStateAction<FilterState>>
|
|
22
|
+
elements?: {
|
|
23
|
+
Button?: typeof Button
|
|
24
|
+
Dropdown?: typeof Dropdown
|
|
25
|
+
Field?: typeof Field
|
|
26
|
+
Select?: typeof Select
|
|
27
|
+
FilterIcon?: typeof ListFilterIcon
|
|
28
|
+
}
|
|
29
|
+
dropdownProps?: Partial<React.ComponentProps<typeof Dropdown>>
|
|
30
|
+
buttonProps?: Partial<React.ComponentProps<typeof Button>>
|
|
31
|
+
buttonClassName?: string
|
|
32
|
+
buttonText?: string
|
|
33
|
+
buttonCounterClassName?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type FiltersHandleType = {
|
|
37
|
+
submit: (includePagination?: boolean) => void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Filters = forwardRef<FiltersHandleType, FiltersProps>(({
|
|
41
|
+
filters, state, setState, elements, dropdownProps, buttonProps, buttonClassName, buttonText, buttonCounterClassName,
|
|
42
|
+
}, ref) => {
|
|
43
|
+
const location = useLocation()
|
|
44
|
+
const stateRef = useRef(state)
|
|
45
|
+
const [debouncedSubmit] = useState(() => debounce(submit, 150))
|
|
46
|
+
const count = Object.keys(state).length - (Object.keys(state).includes('page') ? 1 : 0)
|
|
47
|
+
const Elements = {
|
|
48
|
+
Button: elements?.Button || Button,
|
|
49
|
+
Dropdown: elements?.Dropdown || Dropdown,
|
|
50
|
+
Field: elements?.Field || Field,
|
|
51
|
+
Select: elements?.Select || Select,
|
|
52
|
+
FilterIcon: elements?.FilterIcon || ListFilterIcon,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
useImperativeHandle(ref, () => ({
|
|
56
|
+
submit: debouncedSubmit,
|
|
57
|
+
}))
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
return () => debouncedSubmit.cancel()
|
|
61
|
+
}, [])
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
stateRef.current = state
|
|
65
|
+
}, [state])
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setState(() => ({
|
|
69
|
+
...queryObject(location.search),
|
|
70
|
+
}))
|
|
71
|
+
}, [location.search])
|
|
72
|
+
|
|
73
|
+
function reset(e: React.MouseEvent<HTMLAnchorElement>, filter: FilterType) {
|
|
74
|
+
e.preventDefault()
|
|
75
|
+
setState((s) => omit(s, [filter.name]) as FilterState)
|
|
76
|
+
debouncedSubmit()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resetAll(e: React.MouseEvent<HTMLButtonElement>) {
|
|
80
|
+
e.preventDefault()
|
|
81
|
+
setState((s) => ({
|
|
82
|
+
...(s.page ? { page: s.page } : {}), // keep pagination
|
|
83
|
+
} as FilterState))
|
|
84
|
+
debouncedSubmit()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function _onChange(e: {target: {id: string, value: unknown}}) {
|
|
88
|
+
await onChange(setState, e)
|
|
89
|
+
debouncedSubmit()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Submit the filters and update the URL
|
|
93
|
+
function submit(includePagination?: boolean) {
|
|
94
|
+
const queryStr = queryString(omit(stateRef.current, includePagination ? [] : ['page']))
|
|
95
|
+
history.pushState(null, '', location.pathname + queryStr)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!filters) return null
|
|
99
|
+
return (
|
|
100
|
+
<Elements.Dropdown
|
|
101
|
+
dir="bottom-right"
|
|
102
|
+
// menuIsOpen={true}
|
|
103
|
+
menuClassName="!rounded-lg"
|
|
104
|
+
menuContent={
|
|
105
|
+
<div class="w-[330px]">
|
|
106
|
+
<div class="flex justify-between items-center border-b p-4 py-3.5">
|
|
107
|
+
<div class="text-lg font-semibold">Filters</div>
|
|
108
|
+
<Button color="clear" size="sm" onClick={resetAll}>Reset All</Button>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="flex flex-col px-4 py-4 mb-[-6px]">
|
|
111
|
+
{
|
|
112
|
+
filters.map((filter) => (
|
|
113
|
+
<div key={filter.name}>
|
|
114
|
+
<div class="flex justify-between">
|
|
115
|
+
<label for={filter.name}>{filter.label || camelCaseToTitle(filter.name)}</label>
|
|
116
|
+
<a href="#" class="label font-normal text-secondary underline" onClick={(e) => reset(e, filter)}>Reset</a>
|
|
117
|
+
</div>
|
|
118
|
+
{
|
|
119
|
+
(filter.type === 'text' || filter.type === 'search') &&
|
|
120
|
+
<Elements.Field
|
|
121
|
+
class="mb-4"
|
|
122
|
+
name={filter.name}
|
|
123
|
+
type={filter.type}
|
|
124
|
+
placeholder={filter.placeholder}
|
|
125
|
+
state={state}
|
|
126
|
+
onChange={_onChange}
|
|
127
|
+
/>
|
|
128
|
+
}
|
|
129
|
+
{
|
|
130
|
+
filter.type === 'date' &&
|
|
131
|
+
<Elements.Field
|
|
132
|
+
class="mb-4"
|
|
133
|
+
name={filter.name}
|
|
134
|
+
type="date"
|
|
135
|
+
mode="range"
|
|
136
|
+
state={state}
|
|
137
|
+
onChange={_onChange}
|
|
138
|
+
placeholder={filter.placeholder || 'Select range...'}
|
|
139
|
+
/>
|
|
140
|
+
}
|
|
141
|
+
{
|
|
142
|
+
filter.type === 'select' &&
|
|
143
|
+
<Elements.Select
|
|
144
|
+
class="mb-4"
|
|
145
|
+
name={filter.name}
|
|
146
|
+
type="country"
|
|
147
|
+
state={state}
|
|
148
|
+
options={filter.enums || []}
|
|
149
|
+
onChange={_onChange}
|
|
150
|
+
placeholder={filter.placeholder}
|
|
151
|
+
/>
|
|
152
|
+
}
|
|
153
|
+
</div>
|
|
154
|
+
))
|
|
155
|
+
}
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
}
|
|
159
|
+
{...dropdownProps}
|
|
160
|
+
>
|
|
161
|
+
<Elements.Button
|
|
162
|
+
color="white"
|
|
163
|
+
IconLeft={<Elements.FilterIcon size={16} />}
|
|
164
|
+
className={twMerge(`flex gap-x-2.5 ${buttonClassName || ''}`)}
|
|
165
|
+
{...buttonProps}
|
|
166
|
+
>
|
|
167
|
+
<span class="flex items-center gap-x-2.5">
|
|
168
|
+
{ buttonText || 'Filter By' }
|
|
169
|
+
{
|
|
170
|
+
!!count &&
|
|
171
|
+
<span
|
|
172
|
+
class={twMerge(`inline-flex items-center justify-center rounded-full text-xs text-white bg-primary w-[19px] h-[19px] ${buttonCounterClassName || ''}`)}
|
|
173
|
+
>
|
|
174
|
+
{count}
|
|
175
|
+
</span>
|
|
176
|
+
}
|
|
177
|
+
</span>
|
|
178
|
+
</Elements.Button>
|
|
179
|
+
</Elements.Dropdown>
|
|
180
|
+
)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
Filters.displayName = 'Filters'
|
|
@@ -26,7 +26,7 @@ export function FieldColor({ defaultColor='#333', Icon, onChange, value, ...prop
|
|
|
26
26
|
<Dropdown
|
|
27
27
|
dir="bottom-left"
|
|
28
28
|
menuToggles={false}
|
|
29
|
-
|
|
29
|
+
menuContent={
|
|
30
30
|
<ColorPicker key={lastChanged} defaultColor={defaultColor} id={id} name={props.name} value={value} onChange={onChange} />
|
|
31
31
|
}
|
|
32
32
|
>
|
|
@@ -44,7 +44,8 @@ export function FieldDate({
|
|
|
44
44
|
const dropdownRef = useRef<DropdownRef>(null)
|
|
45
45
|
const id = props.id || props.name
|
|
46
46
|
const [month, setMonth] = useState<number|undefined>()
|
|
47
|
-
|
|
47
|
+
const [lastUpdated, setLastUpdated] = useState(0)
|
|
48
|
+
|
|
48
49
|
// Convert the value to an array of valid* dates
|
|
49
50
|
const dates = useMemo(() => {
|
|
50
51
|
const _dates = Array.isArray(value) ? value : [value]
|
|
@@ -54,6 +55,11 @@ export function FieldDate({
|
|
|
54
55
|
// Hold the input value in state
|
|
55
56
|
const [inputValue, setInputValue] = useState(() => getInputValue(dates))
|
|
56
57
|
|
|
58
|
+
// Update the date's inputValue (text) when the value changes outside of the component
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (new Date().getTime() > lastUpdated + 100) setInputValue(getInputValue(dates))
|
|
61
|
+
}, [value])
|
|
62
|
+
|
|
57
63
|
// Get the prefix content width
|
|
58
64
|
useEffect(() => {
|
|
59
65
|
setPrefixWidth(getPrefixWidth(prefix, 4))
|
|
@@ -62,7 +68,11 @@ export function FieldDate({
|
|
|
62
68
|
function onCalendarChange(mode: Mode, value: null|number|(null|number)[]) {
|
|
63
69
|
if (mode == 'single' && !showTime) dropdownRef.current?.setIsActive(false) // Close the dropdown
|
|
64
70
|
setInputValue(getInputValue(value))
|
|
65
|
-
|
|
71
|
+
// Update the value
|
|
72
|
+
if (onChange) {
|
|
73
|
+
onChange({ target: { id: id, value: value as any } })
|
|
74
|
+
setLastUpdated(new Date().getTime())
|
|
75
|
+
}
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
function getInputValue(dates: Date|number|null|(Date|number|null)[]) {
|
|
@@ -79,7 +89,7 @@ export function FieldDate({
|
|
|
79
89
|
})
|
|
80
90
|
|
|
81
91
|
// For single/range we need limit the array
|
|
82
|
-
if (mode == 'range') split.length = 2
|
|
92
|
+
if (mode == 'range' && split.length > 1) split.length = 2
|
|
83
93
|
else if (mode == 'multiple') split = split.filter(o => o) // remove invalid dates
|
|
84
94
|
|
|
85
95
|
// Swap dates if needed
|
|
@@ -91,9 +101,12 @@ export function FieldDate({
|
|
|
91
101
|
break
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
// Update
|
|
104
|
+
// Update the value
|
|
95
105
|
const value = mode == 'single' ? split[0]?.getTime() ?? null : split.map(d => d?.getTime() ?? null)
|
|
96
|
-
if (onChange)
|
|
106
|
+
if (onChange) {
|
|
107
|
+
onChange({ target: { id: id, value: value as any }})
|
|
108
|
+
setLastUpdated(new Date().getTime())
|
|
109
|
+
}
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
return (
|
|
@@ -103,7 +116,7 @@ export function FieldDate({
|
|
|
103
116
|
// animate={false}
|
|
104
117
|
// menuIsOpen={true}
|
|
105
118
|
minWidth={0}
|
|
106
|
-
|
|
119
|
+
menuContent={
|
|
107
120
|
<div className="flex">
|
|
108
121
|
<Calendar
|
|
109
122
|
{...{ mode, value, numberOfMonths, month }}
|
|
@@ -1,6 +1,6 @@
|
|
|
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 } from 'nitro-web'
|
|
3
|
+
import { util, FieldCurrency, FieldCurrencyProps, FieldColor, FieldColorProps, FieldDate, FieldDateProps, twMerge } from 'nitro-web'
|
|
4
4
|
import { Errors, type Error } from 'nitro-web/types'
|
|
5
5
|
import {
|
|
6
6
|
EnvelopeIcon,
|
|
@@ -130,7 +130,7 @@ export function Field({ state, icon, iconPos: ip, ...props }: FieldProps) {
|
|
|
130
130
|
|
|
131
131
|
function FieldContainer({ children, className, error }: { children: React.ReactNode, className?: string, error?: Error }) {
|
|
132
132
|
return (
|
|
133
|
-
<div css={style} className={`mt-2.5 mb-6 mt-input-before mb-input-after grid grid-cols-1 nitro-field ${className || ''}`}>
|
|
133
|
+
<div css={style} className={twMerge(`mt-2.5 mb-6 mt-input-before mb-input-after grid grid-cols-1 nitro-field ${className || ''}`)}>
|
|
134
134
|
{children}
|
|
135
135
|
{error && <div class="mt-1.5 text-xs text-danger nitro-error">{error.detail}</div>}
|
|
136
136
|
</div>
|
|
@@ -31,7 +31,7 @@ type SelectProps = {
|
|
|
31
31
|
/** The options to display in the dropdown **/
|
|
32
32
|
options: { value: unknown, label: string | React.ReactNode, fixed?: boolean, [key: string]: unknown }[]
|
|
33
33
|
/** The state object to get the value and check errors from **/
|
|
34
|
-
state?: { errors
|
|
34
|
+
state?: { errors?: Errors, [key: string]: unknown }
|
|
35
35
|
/** Select variations **/
|
|
36
36
|
type?: 'country'|'customer'|''
|
|
37
37
|
/** All other props are passed to react-select **/
|
|
@@ -58,7 +58,7 @@ export function Select({ inputId, minMenuWidth, name, prefix='', onChange, optio
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
|
-
<div css={style} class=
|
|
61
|
+
<div css={style} class={twMerge(`mt-2.5 mb-6 mt-input-before mb-input-after nitro-select ${props.className||''}`)}>
|
|
62
62
|
<ReactSelect
|
|
63
63
|
/**
|
|
64
64
|
* react-select prop quick reference (https://react-select.com/props#api):
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Drop, Dropdown, Field, Select, Button, Checkbox, GithubLink, Modal, Calendar, injectedConfig,
|
|
3
|
+
Filters, FiltersHandleType,
|
|
4
|
+
FilterType,
|
|
5
|
+
} from 'nitro-web'
|
|
2
6
|
import { getCountryOptions, getCurrencyOptions, ucFirst } from 'nitro-web/util'
|
|
3
7
|
import { Check } from 'lucide-react'
|
|
4
8
|
|
|
5
|
-
export function Styleguide() {
|
|
9
|
+
export function Styleguide({ className }: { className?: string }) {
|
|
6
10
|
const [customerSearch, setCustomerSearch] = useState('')
|
|
7
11
|
const [showModal1, setShowModal1] = useState(false)
|
|
8
12
|
const [state, setState] = useState({
|
|
@@ -20,6 +24,29 @@ export function Styleguide() {
|
|
|
20
24
|
{ title: 'address', detail: 'Address is required' },
|
|
21
25
|
],
|
|
22
26
|
})
|
|
27
|
+
const [filterState, setFilterState] = useState({})
|
|
28
|
+
const filtersRef = useRef<FiltersHandleType>(null)
|
|
29
|
+
const filters: FilterType[] = [
|
|
30
|
+
{
|
|
31
|
+
name: 'dateRange',
|
|
32
|
+
type: 'date',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'search',
|
|
36
|
+
type: 'search',
|
|
37
|
+
label: 'Keyword Search',
|
|
38
|
+
placeholder: 'Job, employee name...',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'status',
|
|
42
|
+
type: 'select',
|
|
43
|
+
enums: [
|
|
44
|
+
{ label: 'Pending', value: 'pending' },
|
|
45
|
+
{ label: 'Approved', value: 'approved' },
|
|
46
|
+
{ label: 'Rejected', value: 'rejected' },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
]
|
|
23
50
|
|
|
24
51
|
// Example of updating state
|
|
25
52
|
// useEffect(() => {
|
|
@@ -52,7 +79,7 @@ export function Styleguide() {
|
|
|
52
79
|
}
|
|
53
80
|
|
|
54
81
|
return (
|
|
55
|
-
<div class=
|
|
82
|
+
<div class={`text-left max-w-[1100px] ${className}`}>
|
|
56
83
|
<GithubLink filename={__filename} />
|
|
57
84
|
<div class="mb-7">
|
|
58
85
|
<h1 class="h1">{injectedConfig.isDemo ? 'Design System' : 'Style Guide'}</h1>
|
|
@@ -98,6 +125,31 @@ export function Styleguide() {
|
|
|
98
125
|
</div>
|
|
99
126
|
</div>
|
|
100
127
|
|
|
128
|
+
<h2 class="h3">Filters</h2>
|
|
129
|
+
<div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
|
|
130
|
+
{/* Filter dropdown */}
|
|
131
|
+
<Filters
|
|
132
|
+
ref={filtersRef}
|
|
133
|
+
filters={filters}
|
|
134
|
+
state={filterState}
|
|
135
|
+
setState={setFilterState}
|
|
136
|
+
dropdownProps={{ dir: 'bottom-left' }}
|
|
137
|
+
/>
|
|
138
|
+
{/* Search bar */}
|
|
139
|
+
<Field
|
|
140
|
+
class="!my-0 min-w-[242px]"
|
|
141
|
+
type="search"
|
|
142
|
+
name="search"
|
|
143
|
+
iconPos="left"
|
|
144
|
+
state={filterState}
|
|
145
|
+
onChange={(e) => {
|
|
146
|
+
onChange(setFilterState, e)
|
|
147
|
+
filtersRef.current?.submit()
|
|
148
|
+
}}
|
|
149
|
+
placeholder="Linked search bar..."
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
101
153
|
<h2 class="h3">Buttons</h2>
|
|
102
154
|
<div class="flex flex-wrap gap-x-6 gap-y-4 mb-10">
|
|
103
155
|
<div><Button color="primary">primary (default)</Button></div>
|
|
@@ -273,7 +325,7 @@ export function Styleguide() {
|
|
|
273
325
|
</div>
|
|
274
326
|
|
|
275
327
|
<h2 class="h3">File Inputs & Calendar</h2>
|
|
276
|
-
<div class="grid grid-cols-3 gap-x-6
|
|
328
|
+
<div class="grid grid-cols-3 gap-x-6">
|
|
277
329
|
<div>
|
|
278
330
|
<label for="avatar">Avatar</label>
|
|
279
331
|
<Drop class="is-small" name="avatar" state={state} onChange={(e) => onChange(setState, e)} awsUrl={injectedConfig.awsUrl} />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nitro-web",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.48",
|
|
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
|
@@ -117,30 +117,31 @@ export function currencyToCents(currency: string): string;
|
|
|
117
117
|
*/
|
|
118
118
|
export function date(date: number | Date, format?: string, timezone?: string): string;
|
|
119
119
|
/**
|
|
120
|
+
* @template {(...args: any[]) => any} T
|
|
120
121
|
* Creates a debounced function that delays invoking `func` until after `wait`
|
|
121
122
|
* milliseconds have elapsed since the last time the debounced function was invoked.
|
|
122
123
|
*
|
|
123
|
-
* @param {
|
|
124
|
+
* @param {T} func - The function to debounce.
|
|
124
125
|
* @param {number} [wait=0] - Number of milliseconds to delay.
|
|
125
126
|
* @param {{
|
|
126
127
|
* leading?: boolean, // invoke on the leading edge of the timeout (default: false)
|
|
127
128
|
* maxWait?: number, // maximum time `func` is allowed to be delayed before it's invoked
|
|
128
129
|
* trailing?: boolean, // invoke on the trailing edge of the timeout (default: true)
|
|
129
130
|
* }} [options] - Options to control behavior.
|
|
130
|
-
* @returns {(...args:
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* }}
|
|
131
|
+
* @returns {((...args: Parameters<T>) => ReturnType<T>) & {
|
|
132
|
+
* cancel: () => void;
|
|
133
|
+
* flush: () => ReturnType<T>
|
|
134
|
+
* }}
|
|
134
135
|
*
|
|
135
136
|
* @see https://lodash.com/docs/4.17.15#debounce
|
|
136
137
|
*/
|
|
137
|
-
export function debounce(func:
|
|
138
|
+
export function debounce<T extends (...args: any[]) => any>(func: T, wait?: number, options?: {
|
|
138
139
|
leading?: boolean;
|
|
139
140
|
maxWait?: number;
|
|
140
141
|
trailing?: boolean;
|
|
141
|
-
}): (...args:
|
|
142
|
+
}): ((...args: Parameters<T>) => ReturnType<T>) & {
|
|
142
143
|
cancel: () => void;
|
|
143
|
-
flush: () =>
|
|
144
|
+
flush: () => ReturnType<T>;
|
|
144
145
|
};
|
|
145
146
|
/**
|
|
146
147
|
* Deep clones an object or array, preserving its type
|
|
@@ -508,6 +509,12 @@ export function pick(obj: {
|
|
|
508
509
|
export function queryObject(searchString: string, trueDefaults?: boolean): {
|
|
509
510
|
[key: string]: string | true;
|
|
510
511
|
};
|
|
512
|
+
/**
|
|
513
|
+
* Parses a query string into an array of objects
|
|
514
|
+
* @param {string} searchString - location.search or location.href, e.g. '?page=1', 'https://...co.nz?page=1'
|
|
515
|
+
* @returns {object[]} - e.g. [{ page: '1' }]
|
|
516
|
+
*/
|
|
517
|
+
export function queryArray(searchString: string): object[];
|
|
511
518
|
/**
|
|
512
519
|
* Parses an object and returns a query string
|
|
513
520
|
* @param {{[key: string]: unknown}} [obj] - query object
|
|
@@ -595,7 +602,7 @@ export function sortByKey(collection: {
|
|
|
595
602
|
}[], key: string): object[];
|
|
596
603
|
/**
|
|
597
604
|
* Creates a throttled function that only invokes `func` at most once per every `wait` milliseconds
|
|
598
|
-
* @param {
|
|
605
|
+
* @param {(...args: any[]) => any} func
|
|
599
606
|
* @param {number} [wait=0] - the number of milliseconds to throttle invocations to
|
|
600
607
|
* @param {{
|
|
601
608
|
* leading?: boolean, // invoke on the leading edge of the timeout
|
|
@@ -605,7 +612,7 @@ export function sortByKey(collection: {
|
|
|
605
612
|
* @example const throttled = util.throttle(updatePosition, 100)
|
|
606
613
|
* @see lodash
|
|
607
614
|
*/
|
|
608
|
-
export function throttle(func:
|
|
615
|
+
export function throttle(func: (...args: any[]) => any, wait?: number, options?: {
|
|
609
616
|
leading?: boolean;
|
|
610
617
|
trailing?: boolean;
|
|
611
618
|
}): Function;
|
package/types/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"AAkBA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BC;AAED;;;GAGG;AACH,yBAFa,OAAO,OAAO,EAAE,WAAW,CAevC;AAED;;;;;GAKG;AACH,8BAJW,MAAM,cACN;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAC,GACrB,MAAM,CAKlB;AAED;;;;;;GAMG;AACH,+BALW,MAAM,oBACN,OAAO,gBACP,OAAO,GACL,MAAM,CAelB;AAED;;;;;GAKG;AACH,sCAJW,MAAM,wBACN,OAAO,GACL,MAAM,CAMlB;AAED;;;;GAIG;AACH,sCAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;GAIG;AACH,iCAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;;;GAMG;AACH,gCALW,MAAM,aACN,MAAM,oBACN,MAAM,GACJ,MAAM,CAUlB;AAED;;;;GAIG;AACH,0CAHW,MAAM,GACJ,MAAM,CAMlB;AAED;;;;;;;;;;;GAWG;AACH,2BAVW,MAAM,GAAC,IAAI,WACX,MAAM,aACN,MAAM,GACJ,MAAM,CAsBlB;AAED
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../util.js"],"names":[],"mappings":"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"}
|
package/util.js
CHANGED
|
@@ -192,20 +192,21 @@ export function date (date, format, timezone) {
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
/**
|
|
195
|
+
* @template {(...args: any[]) => any} T
|
|
195
196
|
* Creates a debounced function that delays invoking `func` until after `wait`
|
|
196
197
|
* milliseconds have elapsed since the last time the debounced function was invoked.
|
|
197
198
|
*
|
|
198
|
-
* @param {
|
|
199
|
+
* @param {T} func - The function to debounce.
|
|
199
200
|
* @param {number} [wait=0] - Number of milliseconds to delay.
|
|
200
201
|
* @param {{
|
|
201
202
|
* leading?: boolean, // invoke on the leading edge of the timeout (default: false)
|
|
202
203
|
* maxWait?: number, // maximum time `func` is allowed to be delayed before it's invoked
|
|
203
204
|
* trailing?: boolean, // invoke on the trailing edge of the timeout (default: true)
|
|
204
205
|
* }} [options] - Options to control behavior.
|
|
205
|
-
* @returns {(...args:
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
* }}
|
|
206
|
+
* @returns {((...args: Parameters<T>) => ReturnType<T>) & {
|
|
207
|
+
* cancel: () => void;
|
|
208
|
+
* flush: () => ReturnType<T>
|
|
209
|
+
* }}
|
|
209
210
|
*
|
|
210
211
|
* @see https://lodash.com/docs/4.17.15#debounce
|
|
211
212
|
*/
|
|
@@ -1245,6 +1246,18 @@ export function queryObject (searchString, trueDefaults) {
|
|
|
1245
1246
|
return obj
|
|
1246
1247
|
}
|
|
1247
1248
|
|
|
1249
|
+
/**
|
|
1250
|
+
* Parses a query string into an array of objects
|
|
1251
|
+
* @param {string} searchString - location.search or location.href, e.g. '?page=1', 'https://...co.nz?page=1'
|
|
1252
|
+
* @returns {object[]} - e.g. [{ page: '1' }]
|
|
1253
|
+
*/
|
|
1254
|
+
export function queryArray (searchString) {
|
|
1255
|
+
const query = queryObject(searchString)
|
|
1256
|
+
return Object.keys(query).map((key) => {
|
|
1257
|
+
return { [key]: query[key] }
|
|
1258
|
+
})
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1248
1261
|
/**
|
|
1249
1262
|
* Parses an object and returns a query string
|
|
1250
1263
|
* @param {{[key: string]: unknown}} [obj] - query object
|
|
@@ -1489,7 +1502,7 @@ export function sortByKey (collection, key) {
|
|
|
1489
1502
|
|
|
1490
1503
|
/**
|
|
1491
1504
|
* Creates a throttled function that only invokes `func` at most once per every `wait` milliseconds
|
|
1492
|
-
* @param {
|
|
1505
|
+
* @param {(...args: any[]) => any} func
|
|
1493
1506
|
* @param {number} [wait=0] - the number of milliseconds to throttle invocations to
|
|
1494
1507
|
* @param {{
|
|
1495
1508
|
* leading?: boolean, // invoke on the leading edge of the timeout
|