@stack-spot/citric-react 0.36.0 → 0.37.0
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/dist/citric.css +2844 -2832
- package/dist/components/Accordion.d.ts +1 -1
- package/dist/components/Accordion.js +1 -1
- package/dist/components/Alert.d.ts +1 -1
- package/dist/components/Alert.js +1 -1
- package/dist/components/AsyncContent.d.ts +1 -1
- package/dist/components/AsyncContent.js +1 -1
- package/dist/components/Avatar.d.ts +1 -1
- package/dist/components/Avatar.js +1 -1
- package/dist/components/AvatarGroup.d.ts +1 -1
- package/dist/components/AvatarGroup.js +1 -1
- package/dist/components/Badge.d.ts +1 -1
- package/dist/components/Badge.js +1 -1
- package/dist/components/Blockquote.d.ts +1 -1
- package/dist/components/Blockquote.js +1 -1
- package/dist/components/Breadcrumb.d.ts +1 -1
- package/dist/components/Breadcrumb.js +1 -1
- package/dist/components/Button.d.ts +1 -1
- package/dist/components/Button.js +1 -1
- package/dist/components/ButtonLink.d.ts +1 -1
- package/dist/components/ButtonLink.js +1 -1
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.js +1 -1
- package/dist/components/Checkbox.d.ts +1 -1
- package/dist/components/Checkbox.js +1 -1
- package/dist/components/CheckboxGroup.d.ts +1 -1
- package/dist/components/CheckboxGroup.js +1 -1
- package/dist/components/Circle.d.ts +1 -1
- package/dist/components/Circle.js +1 -1
- package/dist/components/Divider.d.ts +1 -1
- package/dist/components/Divider.js +1 -1
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/ErrorMessage.d.ts +1 -1
- package/dist/components/ErrorMessage.js +1 -1
- package/dist/components/FallbackBoundary.d.ts +1 -1
- package/dist/components/FallbackBoundary.js +1 -1
- package/dist/components/Favorite.d.ts +1 -1
- package/dist/components/Favorite.js +1 -1
- package/dist/components/FieldGroup.d.ts +1 -1
- package/dist/components/FieldGroup.js +1 -1
- package/dist/components/Form.d.ts +2 -2
- package/dist/components/Form.js +1 -1
- package/dist/components/FormGroup.d.ts +1 -1
- package/dist/components/FormGroup.js +1 -1
- package/dist/components/Icon.d.ts +1 -1
- package/dist/components/Icon.js +1 -1
- package/dist/components/IconBox.d.ts +3 -3
- package/dist/components/IconBox.js +1 -1
- package/dist/components/ImageBox.d.ts +3 -3
- package/dist/components/ImageBox.js +1 -1
- package/dist/components/ImageWithFallback.d.ts +1 -1
- package/dist/components/ImageWithFallback.js +1 -1
- package/dist/components/Input.d.ts +1 -1
- package/dist/components/Input.js +1 -1
- package/dist/components/Link.d.ts +1 -1
- package/dist/components/Link.js +1 -1
- package/dist/components/LoadingPanel.d.ts +1 -1
- package/dist/components/LoadingPanel.js +1 -1
- package/dist/components/MenuOverlay/Menu.d.ts +1 -1
- package/dist/components/MenuOverlay/Menu.js +1 -1
- package/dist/components/MenuOverlay/index.d.ts +1 -1
- package/dist/components/MenuOverlay/index.js +1 -1
- package/dist/components/Overlay/index.d.ts +1 -1
- package/dist/components/Overlay/index.js +1 -1
- package/dist/components/Pagination.d.ts +1 -1
- package/dist/components/Pagination.js +1 -1
- package/dist/components/ProgressBar.d.ts +1 -1
- package/dist/components/ProgressBar.js +1 -1
- package/dist/components/ProgressCircular.d.ts +1 -1
- package/dist/components/ProgressCircular.js +1 -1
- package/dist/components/RadioGroup.d.ts +1 -1
- package/dist/components/RadioGroup.js +1 -1
- package/dist/components/Rating.d.ts +17 -3
- package/dist/components/Rating.d.ts.map +1 -1
- package/dist/components/Rating.js +11 -3
- package/dist/components/Rating.js.map +1 -1
- package/dist/components/Select/MultiSelect.d.ts +1 -1
- package/dist/components/Select/MultiSelect.js +1 -1
- package/dist/components/Select/RichSelect.d.ts +1 -1
- package/dist/components/Select/RichSelect.js +1 -1
- package/dist/components/Select/SimpleSelect.d.ts +1 -1
- package/dist/components/Select/SimpleSelect.js +1 -1
- package/dist/components/Select/index.d.ts +1 -1
- package/dist/components/Select/index.js +1 -1
- package/dist/components/SelectBox.d.ts +1 -1
- package/dist/components/SelectBox.js +1 -1
- package/dist/components/Skeleton.d.ts +1 -1
- package/dist/components/Skeleton.js +1 -1
- package/dist/components/Slider.d.ts +1 -1
- package/dist/components/Slider.js +1 -1
- package/dist/components/SmartTable.d.ts +1 -1
- package/dist/components/SmartTable.js +1 -1
- package/dist/components/Stepper.d.ts +1 -1
- package/dist/components/Stepper.js +1 -1
- package/dist/components/Table.d.ts +3 -3
- package/dist/components/Table.js +1 -1
- package/dist/components/Tabs/index.d.ts +1 -1
- package/dist/components/Tabs/index.js +1 -1
- package/dist/components/Textarea.d.ts +1 -1
- package/dist/components/Textarea.js +1 -1
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/Tooltip.js +1 -1
- package/dist/context/CitricProvider.d.ts +1 -1
- package/dist/context/CitricProvider.js +1 -1
- package/dist/overlay.js +1 -1
- package/dist/theme.css +415 -415
- package/package.json +3 -2
- package/scripts/build-css.ts +49 -49
- package/src/components/Accordion.tsx +130 -130
- package/src/components/Alert.tsx +24 -24
- package/src/components/AsyncContent.tsx +70 -70
- package/src/components/Avatar.tsx +45 -45
- package/src/components/AvatarGroup.tsx +49 -49
- package/src/components/Badge.tsx +47 -47
- package/src/components/Blockquote.tsx +18 -18
- package/src/components/Breadcrumb.tsx +33 -33
- package/src/components/Button.tsx +105 -105
- package/src/components/ButtonLink.tsx +45 -45
- package/src/components/Card.tsx +68 -68
- package/src/components/Checkbox.tsx +51 -51
- package/src/components/CheckboxGroup.tsx +152 -152
- package/src/components/Circle.tsx +43 -43
- package/src/components/CitricComponent.ts +47 -47
- package/src/components/Divider.tsx +24 -24
- package/src/components/ErrorBoundary.tsx +75 -75
- package/src/components/ErrorMessage.tsx +11 -11
- package/src/components/FallbackBoundary.tsx +40 -40
- package/src/components/Favorite.tsx +57 -57
- package/src/components/FieldGroup.tsx +46 -46
- package/src/components/Form.tsx +36 -36
- package/src/components/FormGroup.tsx +57 -57
- package/src/components/Icon.tsx +35 -35
- package/src/components/IconBox.tsx +134 -134
- package/src/components/ImageBox.tsx +125 -125
- package/src/components/ImageWithFallback.tsx +65 -65
- package/src/components/Input.tsx +49 -49
- package/src/components/Link.tsx +55 -55
- package/src/components/LoadingPanel.tsx +8 -8
- package/src/components/MenuOverlay/Menu.tsx +158 -158
- package/src/components/MenuOverlay/context.ts +20 -20
- package/src/components/MenuOverlay/index.tsx +55 -55
- package/src/components/MenuOverlay/keyboard.ts +60 -60
- package/src/components/MenuOverlay/types.ts +171 -171
- package/src/components/Overlay/context.ts +10 -10
- package/src/components/Overlay/index.tsx +164 -164
- package/src/components/Overlay/types.ts +70 -70
- package/src/components/Pagination.tsx +113 -113
- package/src/components/ProgressBar.tsx +45 -45
- package/src/components/ProgressCircular.tsx +45 -45
- package/src/components/RadioGroup.tsx +146 -146
- package/src/components/Rating.tsx +98 -35
- package/src/components/Select/MultiSelect.tsx +217 -217
- package/src/components/Select/RichSelect.tsx +128 -128
- package/src/components/Select/SimpleSelect.tsx +73 -73
- package/src/components/Select/hooks.ts +133 -133
- package/src/components/Select/index.tsx +35 -35
- package/src/components/Select/types.ts +134 -134
- package/src/components/SelectBox.tsx +167 -167
- package/src/components/Skeleton.tsx +53 -53
- package/src/components/Slider.tsx +89 -89
- package/src/components/SmartTable.tsx +227 -227
- package/src/components/Stepper.tsx +163 -163
- package/src/components/Table.tsx +234 -234
- package/src/components/Tabs/TabController.ts +54 -54
- package/src/components/Tabs/index.tsx +87 -87
- package/src/components/Tabs/types.ts +54 -54
- package/src/components/Tabs/utils.ts +6 -6
- package/src/components/Text.ts +111 -111
- package/src/components/Textarea.tsx +27 -27
- package/src/components/Tooltip.tsx +72 -72
- package/src/components/layout.tsx +101 -101
- package/src/context/CitricContext.tsx +4 -4
- package/src/context/CitricProvider.tsx +14 -14
- package/src/context/hooks.ts +6 -6
- package/src/index.ts +58 -58
- package/src/overlay.ts +341 -341
- package/src/types.ts +216 -216
- package/src/utils/ValueController.ts +28 -28
- package/src/utils/acessibility.ts +92 -92
- package/src/utils/checkbox.ts +121 -121
- package/src/utils/css.ts +119 -119
- package/src/utils/options.ts +9 -9
- package/src/utils/radio.ts +93 -93
- package/src/utils/react.ts +6 -6
- package/tsconfig.json +10 -10
package/src/components/Table.tsx
CHANGED
|
@@ -1,234 +1,234 @@
|
|
|
1
|
-
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
-
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
-
import { useEffect, useRef } from 'react'
|
|
4
|
-
import { applyCSSVariable } from '../utils/css'
|
|
5
|
-
import { withRef } from '../utils/react'
|
|
6
|
-
import { CitricComponent } from './CitricComponent'
|
|
7
|
-
|
|
8
|
-
export interface BaseTableProps {
|
|
9
|
-
/**
|
|
10
|
-
* The overall table's appearance. Includes a default configuration for every other styling related prop.
|
|
11
|
-
*
|
|
12
|
-
* @default 'spaced'
|
|
13
|
-
*/
|
|
14
|
-
appearance?: 'stripped' | 'spaced',
|
|
15
|
-
/**
|
|
16
|
-
* Whether or not to color each row a different color. Only valid when appearance is "spaced".
|
|
17
|
-
*
|
|
18
|
-
* @default false
|
|
19
|
-
*/
|
|
20
|
-
stripped?: boolean,
|
|
21
|
-
/**
|
|
22
|
-
* If true, there's no spacing between rows. Only valid when appearance is "spaced".
|
|
23
|
-
*
|
|
24
|
-
* @default false
|
|
25
|
-
*/
|
|
26
|
-
compressed?: boolean,
|
|
27
|
-
/**
|
|
28
|
-
* Shows an outer border for the whole table.
|
|
29
|
-
*
|
|
30
|
-
* @default "true if appearance is 'stripped', false otherwise"
|
|
31
|
-
*/
|
|
32
|
-
showBorders?: boolean,
|
|
33
|
-
/**
|
|
34
|
-
* Shows borders in each of the rows.
|
|
35
|
-
*
|
|
36
|
-
* @default false
|
|
37
|
-
*/
|
|
38
|
-
showRowBorders?: boolean,
|
|
39
|
-
/**
|
|
40
|
-
* Shows borders in the header.
|
|
41
|
-
*
|
|
42
|
-
* @default "false if appearance is 'stripped', true otherwise"
|
|
43
|
-
*/
|
|
44
|
-
showHeaderBorders?: boolean,
|
|
45
|
-
/**
|
|
46
|
-
* Whether or not the table borders are rounded.
|
|
47
|
-
*
|
|
48
|
-
* @default true
|
|
49
|
-
*/
|
|
50
|
-
rounded?: boolean,
|
|
51
|
-
/**
|
|
52
|
-
* Whether or not each row in the table have rounded borders.
|
|
53
|
-
*
|
|
54
|
-
* @default "false if appearance is 'stripped', true otherwise"
|
|
55
|
-
*/
|
|
56
|
-
roundedRows?: boolean,
|
|
57
|
-
/**
|
|
58
|
-
* If true, all headers are uppercase.
|
|
59
|
-
*
|
|
60
|
-
* @default false
|
|
61
|
-
*/
|
|
62
|
-
uppercaseHeader?: boolean,
|
|
63
|
-
/**
|
|
64
|
-
* Allows rows to act as accordions. To create an accordion row, use `<tbody>` to group two `<tr>` elements, where the first is the actual
|
|
65
|
-
* row and the second is the collapsible content of the row. The row with the collapsible content must be defined as `<Tr accordion>`.
|
|
66
|
-
*
|
|
67
|
-
* @default false
|
|
68
|
-
*/
|
|
69
|
-
accordionRows?: boolean,
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export type SortingDirection = 'asc' | 'desc'
|
|
73
|
-
|
|
74
|
-
interface BaseThProps {
|
|
75
|
-
/**
|
|
76
|
-
* Which is the current direction of the sorting? Only relevant if 'onSort' is set.
|
|
77
|
-
*
|
|
78
|
-
* @default 'desc'
|
|
79
|
-
*/
|
|
80
|
-
direction?: SortingDirection,
|
|
81
|
-
/**
|
|
82
|
-
* What to do when the header is clicked (sorting).
|
|
83
|
-
*/
|
|
84
|
-
onSort?: (value: SortingDirection | undefined) => void,
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface BaseTrProps {
|
|
88
|
-
/**
|
|
89
|
-
* True if this table is an accordion, false otherwise.
|
|
90
|
-
*
|
|
91
|
-
* @default false
|
|
92
|
-
*/
|
|
93
|
-
accordion?: boolean,
|
|
94
|
-
/**
|
|
95
|
-
* If the next row is an accordion, what should make it expand or contract? A click on a button in the last column or a click anywhere in
|
|
96
|
-
* the row?
|
|
97
|
-
*
|
|
98
|
-
* @default 'button'
|
|
99
|
-
*/
|
|
100
|
-
accordionTrigger?: 'button' | 'row',
|
|
101
|
-
/**
|
|
102
|
-
* Sets the maximum height of this accordion. Only valid if `accordion` is true.
|
|
103
|
-
*
|
|
104
|
-
* @default '200px'
|
|
105
|
-
*/
|
|
106
|
-
accordionMaxHeight?: string,
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export type TableProps = React.JSX.IntrinsicElements['table'] & BaseTableProps
|
|
110
|
-
export type ThProps = React.JSX.IntrinsicElements['th'] & BaseThProps
|
|
111
|
-
export type TrProps = React.JSX.IntrinsicElements['tr'] & BaseTrProps
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Renders an HTML table. Use its props for customizing the appearance.
|
|
115
|
-
*
|
|
116
|
-
* - If you need to use Accordion rows, use `<Tr>`instead of `<tr>`.
|
|
117
|
-
* - If you need to sort columns, use `<Th>`instead of `<th>`.
|
|
118
|
-
*
|
|
119
|
-
* This works exactly like the HTML tag "table".
|
|
120
|
-
*
|
|
121
|
-
* Attention: prefer using the component "SmartTable". Use this only if you need full control over the table.
|
|
122
|
-
*/
|
|
123
|
-
export const Table = withRef(({
|
|
124
|
-
appearance, stripped, compressed, showBorders, showHeaderBorders, showRowBorders, rounded, roundedRows, uppercaseHeader, className,
|
|
125
|
-
children, accordionRows, ...props
|
|
126
|
-
}: TableProps) => {
|
|
127
|
-
const classes = listToClass([
|
|
128
|
-
className,
|
|
129
|
-
stripped && 'stripped',
|
|
130
|
-
compressed && 'compressed',
|
|
131
|
-
showBorders && 'bordered',
|
|
132
|
-
showHeaderBorders === false && 'borderless-header',
|
|
133
|
-
showRowBorders && 'bordered-rows',
|
|
134
|
-
rounded === false && 'square',
|
|
135
|
-
roundedRows === false && 'square-rows',
|
|
136
|
-
uppercaseHeader && 'uppercase-header',
|
|
137
|
-
accordionRows && 'accordion-rows',
|
|
138
|
-
])
|
|
139
|
-
return (
|
|
140
|
-
<CitricComponent tag="table" component="table" data-appearance={appearance} className={classes} {...props}>
|
|
141
|
-
{children}
|
|
142
|
-
</CitricComponent>
|
|
143
|
-
)
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
export const Th = withRef(({ direction, onSort, children, className, ...props }: ThProps) => {
|
|
147
|
-
const t = useTranslate(dictionary)
|
|
148
|
-
let tip: string | undefined
|
|
149
|
-
const label = typeof children === 'string' ? children : undefined
|
|
150
|
-
if (onSort && !direction) tip = label ? `${label}. ${t.sortAscending}` : t.sortAscending
|
|
151
|
-
if (onSort && direction === 'asc') tip = label ? `${label}. ${t.sortDescending}` : t.sortDescending
|
|
152
|
-
if (onSort && direction === 'desc') tip = label ? `${label}. ${t.sortNone}` : t.sortNone
|
|
153
|
-
|
|
154
|
-
function handleSort() {
|
|
155
|
-
if (!direction) onSort?.('asc')
|
|
156
|
-
else if (direction === 'asc') onSort?.('desc')
|
|
157
|
-
else onSort?.(undefined)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return (
|
|
161
|
-
<th
|
|
162
|
-
className={listToClass([onSort && 'sortable', direction, className])}
|
|
163
|
-
onClick={onSort? handleSort : undefined}
|
|
164
|
-
onKeyDown={onSort ? (e => e.key === 'Enter' && handleSort()) : undefined}
|
|
165
|
-
aria-label={tip}
|
|
166
|
-
tabIndex={onSort ? 0 : undefined}
|
|
167
|
-
{...props}
|
|
168
|
-
>
|
|
169
|
-
{children}
|
|
170
|
-
</th>
|
|
171
|
-
)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
export const Tr = withRef(
|
|
175
|
-
({
|
|
176
|
-
ref: outerRef, accordion, accordionTrigger, accordionMaxHeight, children, className, style, onClick, ...props
|
|
177
|
-
}: TrProps) => {
|
|
178
|
-
const innerRef = useRef<HTMLTableRowElement | null>(null)
|
|
179
|
-
const ref = outerRef as React.MutableRefObject<HTMLTableRowElement | null> ?? innerRef
|
|
180
|
-
|
|
181
|
-
useEffect(() => {
|
|
182
|
-
if (!accordion) return
|
|
183
|
-
const checkbox = ref.current?.closest('tbody')?.querySelector('td:last-child input[aria-controls]')
|
|
184
|
-
if (checkbox instanceof HTMLElement) {
|
|
185
|
-
const onChange = (e: Event) => {
|
|
186
|
-
if (!ref.current || !(e.target instanceof HTMLInputElement)) return
|
|
187
|
-
if (e.target.checked) {
|
|
188
|
-
ref.current.setAttribute('aria-hidden', 'false')
|
|
189
|
-
ref.current.removeAttribute('inert')
|
|
190
|
-
} else {
|
|
191
|
-
ref.current.setAttribute('aria-hidden', 'true')
|
|
192
|
-
ref.current.setAttribute('inert', 'true')
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
checkbox.addEventListener('change', onChange)
|
|
196
|
-
return () => checkbox.removeEventListener('change', onChange)
|
|
197
|
-
}
|
|
198
|
-
}, [ref.current])
|
|
199
|
-
|
|
200
|
-
return (
|
|
201
|
-
<tr
|
|
202
|
-
ref={ref}
|
|
203
|
-
{...props}
|
|
204
|
-
className={listToClass([className, accordion && 'accordion', (accordionTrigger === 'row' || onClick) && 'clickable'])}
|
|
205
|
-
onClick={accordionTrigger === 'row'
|
|
206
|
-
? (e) => {
|
|
207
|
-
const checkbox = ref.current?.querySelector('td:last-child input[aria-controls]')
|
|
208
|
-
if (checkbox instanceof HTMLInputElement && e.target !== checkbox) checkbox.click()
|
|
209
|
-
onClick?.(e)
|
|
210
|
-
}
|
|
211
|
-
: onClick
|
|
212
|
-
}
|
|
213
|
-
style={applyCSSVariable(style, 'max-height', accordionMaxHeight)}
|
|
214
|
-
aria-hidden={accordion}
|
|
215
|
-
{...(accordion ? { inert: 'true' } : {})}
|
|
216
|
-
>
|
|
217
|
-
{children}
|
|
218
|
-
</tr>
|
|
219
|
-
)
|
|
220
|
-
},
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
const dictionary = {
|
|
224
|
-
en: {
|
|
225
|
-
sortAscending: 'Click to sort in ascending order.',
|
|
226
|
-
sortDescending: 'Click to sort in descending order.',
|
|
227
|
-
sortNone: 'Click to remove sorting.',
|
|
228
|
-
},
|
|
229
|
-
pt: {
|
|
230
|
-
sortAscending: 'Clique para ordenar em ordem crescente.',
|
|
231
|
-
sortDescending: 'Clique para ordenar em ordem decrescente.',
|
|
232
|
-
sortNone: 'Clique para remover a ordenação.',
|
|
233
|
-
},
|
|
234
|
-
} satisfies Dictionary
|
|
1
|
+
import { listToClass } from '@stack-spot/portal-theme'
|
|
2
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
3
|
+
import { useEffect, useRef } from 'react'
|
|
4
|
+
import { applyCSSVariable } from '../utils/css'
|
|
5
|
+
import { withRef } from '../utils/react'
|
|
6
|
+
import { CitricComponent } from './CitricComponent'
|
|
7
|
+
|
|
8
|
+
export interface BaseTableProps {
|
|
9
|
+
/**
|
|
10
|
+
* The overall table's appearance. Includes a default configuration for every other styling related prop.
|
|
11
|
+
*
|
|
12
|
+
* @default 'spaced'
|
|
13
|
+
*/
|
|
14
|
+
appearance?: 'stripped' | 'spaced',
|
|
15
|
+
/**
|
|
16
|
+
* Whether or not to color each row a different color. Only valid when appearance is "spaced".
|
|
17
|
+
*
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
stripped?: boolean,
|
|
21
|
+
/**
|
|
22
|
+
* If true, there's no spacing between rows. Only valid when appearance is "spaced".
|
|
23
|
+
*
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
compressed?: boolean,
|
|
27
|
+
/**
|
|
28
|
+
* Shows an outer border for the whole table.
|
|
29
|
+
*
|
|
30
|
+
* @default "true if appearance is 'stripped', false otherwise"
|
|
31
|
+
*/
|
|
32
|
+
showBorders?: boolean,
|
|
33
|
+
/**
|
|
34
|
+
* Shows borders in each of the rows.
|
|
35
|
+
*
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
showRowBorders?: boolean,
|
|
39
|
+
/**
|
|
40
|
+
* Shows borders in the header.
|
|
41
|
+
*
|
|
42
|
+
* @default "false if appearance is 'stripped', true otherwise"
|
|
43
|
+
*/
|
|
44
|
+
showHeaderBorders?: boolean,
|
|
45
|
+
/**
|
|
46
|
+
* Whether or not the table borders are rounded.
|
|
47
|
+
*
|
|
48
|
+
* @default true
|
|
49
|
+
*/
|
|
50
|
+
rounded?: boolean,
|
|
51
|
+
/**
|
|
52
|
+
* Whether or not each row in the table have rounded borders.
|
|
53
|
+
*
|
|
54
|
+
* @default "false if appearance is 'stripped', true otherwise"
|
|
55
|
+
*/
|
|
56
|
+
roundedRows?: boolean,
|
|
57
|
+
/**
|
|
58
|
+
* If true, all headers are uppercase.
|
|
59
|
+
*
|
|
60
|
+
* @default false
|
|
61
|
+
*/
|
|
62
|
+
uppercaseHeader?: boolean,
|
|
63
|
+
/**
|
|
64
|
+
* Allows rows to act as accordions. To create an accordion row, use `<tbody>` to group two `<tr>` elements, where the first is the actual
|
|
65
|
+
* row and the second is the collapsible content of the row. The row with the collapsible content must be defined as `<Tr accordion>`.
|
|
66
|
+
*
|
|
67
|
+
* @default false
|
|
68
|
+
*/
|
|
69
|
+
accordionRows?: boolean,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export type SortingDirection = 'asc' | 'desc'
|
|
73
|
+
|
|
74
|
+
interface BaseThProps {
|
|
75
|
+
/**
|
|
76
|
+
* Which is the current direction of the sorting? Only relevant if 'onSort' is set.
|
|
77
|
+
*
|
|
78
|
+
* @default 'desc'
|
|
79
|
+
*/
|
|
80
|
+
direction?: SortingDirection,
|
|
81
|
+
/**
|
|
82
|
+
* What to do when the header is clicked (sorting).
|
|
83
|
+
*/
|
|
84
|
+
onSort?: (value: SortingDirection | undefined) => void,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface BaseTrProps {
|
|
88
|
+
/**
|
|
89
|
+
* True if this table is an accordion, false otherwise.
|
|
90
|
+
*
|
|
91
|
+
* @default false
|
|
92
|
+
*/
|
|
93
|
+
accordion?: boolean,
|
|
94
|
+
/**
|
|
95
|
+
* If the next row is an accordion, what should make it expand or contract? A click on a button in the last column or a click anywhere in
|
|
96
|
+
* the row?
|
|
97
|
+
*
|
|
98
|
+
* @default 'button'
|
|
99
|
+
*/
|
|
100
|
+
accordionTrigger?: 'button' | 'row',
|
|
101
|
+
/**
|
|
102
|
+
* Sets the maximum height of this accordion. Only valid if `accordion` is true.
|
|
103
|
+
*
|
|
104
|
+
* @default '200px'
|
|
105
|
+
*/
|
|
106
|
+
accordionMaxHeight?: string,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type TableProps = React.JSX.IntrinsicElements['table'] & BaseTableProps
|
|
110
|
+
export type ThProps = React.JSX.IntrinsicElements['th'] & BaseThProps
|
|
111
|
+
export type TrProps = React.JSX.IntrinsicElements['tr'] & BaseTrProps
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Renders an HTML table. Use its props for customizing the appearance.
|
|
115
|
+
*
|
|
116
|
+
* - If you need to use Accordion rows, use `<Tr>`instead of `<tr>`.
|
|
117
|
+
* - If you need to sort columns, use `<Th>`instead of `<th>`.
|
|
118
|
+
*
|
|
119
|
+
* This works exactly like the HTML tag "table".
|
|
120
|
+
*
|
|
121
|
+
* Attention: prefer using the component "SmartTable". Use this only if you need full control over the table.
|
|
122
|
+
*/
|
|
123
|
+
export const Table = withRef(({
|
|
124
|
+
appearance, stripped, compressed, showBorders, showHeaderBorders, showRowBorders, rounded, roundedRows, uppercaseHeader, className,
|
|
125
|
+
children, accordionRows, ...props
|
|
126
|
+
}: TableProps) => {
|
|
127
|
+
const classes = listToClass([
|
|
128
|
+
className,
|
|
129
|
+
stripped && 'stripped',
|
|
130
|
+
compressed && 'compressed',
|
|
131
|
+
showBorders && 'bordered',
|
|
132
|
+
showHeaderBorders === false && 'borderless-header',
|
|
133
|
+
showRowBorders && 'bordered-rows',
|
|
134
|
+
rounded === false && 'square',
|
|
135
|
+
roundedRows === false && 'square-rows',
|
|
136
|
+
uppercaseHeader && 'uppercase-header',
|
|
137
|
+
accordionRows && 'accordion-rows',
|
|
138
|
+
])
|
|
139
|
+
return (
|
|
140
|
+
<CitricComponent tag="table" component="table" data-appearance={appearance} className={classes} {...props}>
|
|
141
|
+
{children}
|
|
142
|
+
</CitricComponent>
|
|
143
|
+
)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
export const Th = withRef(({ direction, onSort, children, className, ...props }: ThProps) => {
|
|
147
|
+
const t = useTranslate(dictionary)
|
|
148
|
+
let tip: string | undefined
|
|
149
|
+
const label = typeof children === 'string' ? children : undefined
|
|
150
|
+
if (onSort && !direction) tip = label ? `${label}. ${t.sortAscending}` : t.sortAscending
|
|
151
|
+
if (onSort && direction === 'asc') tip = label ? `${label}. ${t.sortDescending}` : t.sortDescending
|
|
152
|
+
if (onSort && direction === 'desc') tip = label ? `${label}. ${t.sortNone}` : t.sortNone
|
|
153
|
+
|
|
154
|
+
function handleSort() {
|
|
155
|
+
if (!direction) onSort?.('asc')
|
|
156
|
+
else if (direction === 'asc') onSort?.('desc')
|
|
157
|
+
else onSort?.(undefined)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<th
|
|
162
|
+
className={listToClass([onSort && 'sortable', direction, className])}
|
|
163
|
+
onClick={onSort? handleSort : undefined}
|
|
164
|
+
onKeyDown={onSort ? (e => e.key === 'Enter' && handleSort()) : undefined}
|
|
165
|
+
aria-label={tip}
|
|
166
|
+
tabIndex={onSort ? 0 : undefined}
|
|
167
|
+
{...props}
|
|
168
|
+
>
|
|
169
|
+
{children}
|
|
170
|
+
</th>
|
|
171
|
+
)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
export const Tr = withRef(
|
|
175
|
+
({
|
|
176
|
+
ref: outerRef, accordion, accordionTrigger, accordionMaxHeight, children, className, style, onClick, ...props
|
|
177
|
+
}: TrProps) => {
|
|
178
|
+
const innerRef = useRef<HTMLTableRowElement | null>(null)
|
|
179
|
+
const ref = outerRef as React.MutableRefObject<HTMLTableRowElement | null> ?? innerRef
|
|
180
|
+
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (!accordion) return
|
|
183
|
+
const checkbox = ref.current?.closest('tbody')?.querySelector('td:last-child input[aria-controls]')
|
|
184
|
+
if (checkbox instanceof HTMLElement) {
|
|
185
|
+
const onChange = (e: Event) => {
|
|
186
|
+
if (!ref.current || !(e.target instanceof HTMLInputElement)) return
|
|
187
|
+
if (e.target.checked) {
|
|
188
|
+
ref.current.setAttribute('aria-hidden', 'false')
|
|
189
|
+
ref.current.removeAttribute('inert')
|
|
190
|
+
} else {
|
|
191
|
+
ref.current.setAttribute('aria-hidden', 'true')
|
|
192
|
+
ref.current.setAttribute('inert', 'true')
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
checkbox.addEventListener('change', onChange)
|
|
196
|
+
return () => checkbox.removeEventListener('change', onChange)
|
|
197
|
+
}
|
|
198
|
+
}, [ref.current])
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<tr
|
|
202
|
+
ref={ref}
|
|
203
|
+
{...props}
|
|
204
|
+
className={listToClass([className, accordion && 'accordion', (accordionTrigger === 'row' || onClick) && 'clickable'])}
|
|
205
|
+
onClick={accordionTrigger === 'row'
|
|
206
|
+
? (e) => {
|
|
207
|
+
const checkbox = ref.current?.querySelector('td:last-child input[aria-controls]')
|
|
208
|
+
if (checkbox instanceof HTMLInputElement && e.target !== checkbox) checkbox.click()
|
|
209
|
+
onClick?.(e)
|
|
210
|
+
}
|
|
211
|
+
: onClick
|
|
212
|
+
}
|
|
213
|
+
style={applyCSSVariable(style, 'max-height', accordionMaxHeight)}
|
|
214
|
+
aria-hidden={accordion}
|
|
215
|
+
{...(accordion ? { inert: 'true' } : {})}
|
|
216
|
+
>
|
|
217
|
+
{children}
|
|
218
|
+
</tr>
|
|
219
|
+
)
|
|
220
|
+
},
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
const dictionary = {
|
|
224
|
+
en: {
|
|
225
|
+
sortAscending: 'Click to sort in ascending order.',
|
|
226
|
+
sortDescending: 'Click to sort in descending order.',
|
|
227
|
+
sortNone: 'Click to remove sorting.',
|
|
228
|
+
},
|
|
229
|
+
pt: {
|
|
230
|
+
sortAscending: 'Clique para ordenar em ordem crescente.',
|
|
231
|
+
sortDescending: 'Clique para ordenar em ordem decrescente.',
|
|
232
|
+
sortNone: 'Clique para remover a ordenação.',
|
|
233
|
+
},
|
|
234
|
+
} satisfies Dictionary
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import { ValueController } from '../../utils/ValueController'
|
|
2
|
-
|
|
3
|
-
export class TabController<Key extends string> extends ValueController<Key> {
|
|
4
|
-
private tabOrder: Key[]
|
|
5
|
-
|
|
6
|
-
constructor(tabOrder: Key[], value: Key) {
|
|
7
|
-
super(value)
|
|
8
|
-
this.tabOrder = tabOrder
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
private getCurrentIndex() {
|
|
12
|
-
return this.tabOrder.findIndex(t => t === this.value)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @returns true if there's another tab after the one currently selected. False otherwise.
|
|
17
|
-
*/
|
|
18
|
-
hasNext(): boolean {
|
|
19
|
-
const current = this.getCurrentIndex()
|
|
20
|
-
return current > -1 && current + 1 < this.tabOrder.length
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @returns true if there's a tab before the one currently selected. False otherwise.
|
|
25
|
-
*/
|
|
26
|
-
hasPrevious(): boolean {
|
|
27
|
-
const current = this.getCurrentIndex()
|
|
28
|
-
return current > -1 && current - 1 >= 0
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Selects the tab on the right of the one currently selected. If there's no next tab, nothing happens.
|
|
33
|
-
* @returns true if the tab is changed, false otherwise.
|
|
34
|
-
*/
|
|
35
|
-
next(): boolean {
|
|
36
|
-
if (this.hasNext()) {
|
|
37
|
-
this.setValue(this.tabOrder[this.getCurrentIndex() + 1])
|
|
38
|
-
return true
|
|
39
|
-
}
|
|
40
|
-
return false
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Selects the tab on the left of the one currently selected. If there's no previous tab, nothing happens.
|
|
45
|
-
* @returns true if the tab is changed, false otherwise.
|
|
46
|
-
*/
|
|
47
|
-
previous(): boolean {
|
|
48
|
-
if (this.hasPrevious()) {
|
|
49
|
-
this.setValue(this.tabOrder[this.getCurrentIndex() - 1])
|
|
50
|
-
return true
|
|
51
|
-
}
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
54
|
-
}
|
|
1
|
+
import { ValueController } from '../../utils/ValueController'
|
|
2
|
+
|
|
3
|
+
export class TabController<Key extends string> extends ValueController<Key> {
|
|
4
|
+
private tabOrder: Key[]
|
|
5
|
+
|
|
6
|
+
constructor(tabOrder: Key[], value: Key) {
|
|
7
|
+
super(value)
|
|
8
|
+
this.tabOrder = tabOrder
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private getCurrentIndex() {
|
|
12
|
+
return this.tabOrder.findIndex(t => t === this.value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @returns true if there's another tab after the one currently selected. False otherwise.
|
|
17
|
+
*/
|
|
18
|
+
hasNext(): boolean {
|
|
19
|
+
const current = this.getCurrentIndex()
|
|
20
|
+
return current > -1 && current + 1 < this.tabOrder.length
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @returns true if there's a tab before the one currently selected. False otherwise.
|
|
25
|
+
*/
|
|
26
|
+
hasPrevious(): boolean {
|
|
27
|
+
const current = this.getCurrentIndex()
|
|
28
|
+
return current > -1 && current - 1 >= 0
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Selects the tab on the right of the one currently selected. If there's no next tab, nothing happens.
|
|
33
|
+
* @returns true if the tab is changed, false otherwise.
|
|
34
|
+
*/
|
|
35
|
+
next(): boolean {
|
|
36
|
+
if (this.hasNext()) {
|
|
37
|
+
this.setValue(this.tabOrder[this.getCurrentIndex() + 1])
|
|
38
|
+
return true
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Selects the tab on the left of the one currently selected. If there's no previous tab, nothing happens.
|
|
45
|
+
* @returns true if the tab is changed, false otherwise.
|
|
46
|
+
*/
|
|
47
|
+
previous(): boolean {
|
|
48
|
+
if (this.hasPrevious()) {
|
|
49
|
+
this.setValue(this.tabOrder[this.getCurrentIndex() - 1])
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|