@codecademy/gamut 68.2.0-alpha.a8fc55.0 → 68.2.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.
Files changed (43) hide show
  1. package/dist/BarChart/BarChartProvider.d.ts +19 -0
  2. package/dist/BarChart/BarChartProvider.js +27 -0
  3. package/dist/BarChart/BarRow/ValueLabelsContent.d.ts +7 -0
  4. package/dist/BarChart/BarRow/ValueLabelsContent.js +34 -0
  5. package/dist/BarChart/BarRow/elements.d.ts +959 -0
  6. package/dist/BarChart/BarRow/elements.js +110 -0
  7. package/dist/BarChart/BarRow/index.d.ts +6 -0
  8. package/dist/BarChart/BarRow/index.js +231 -0
  9. package/dist/BarChart/SortSelect/index.d.ts +15 -0
  10. package/dist/BarChart/SortSelect/index.js +18 -0
  11. package/dist/BarChart/index.d.ts +4 -0
  12. package/dist/BarChart/index.js +136 -0
  13. package/dist/BarChart/layout/GridLines.d.ts +3 -0
  14. package/dist/BarChart/layout/GridLines.js +69 -0
  15. package/dist/BarChart/layout/LabelSpacer.d.ts +6 -0
  16. package/dist/BarChart/layout/LabelSpacer.js +56 -0
  17. package/dist/BarChart/layout/ScaleChartHeader.d.ts +3 -0
  18. package/dist/BarChart/layout/ScaleChartHeader.js +87 -0
  19. package/dist/BarChart/shared/elements.d.ts +7 -0
  20. package/dist/BarChart/shared/elements.js +12 -0
  21. package/dist/BarChart/shared/styles.d.ts +4 -0
  22. package/dist/BarChart/shared/styles.js +4 -0
  23. package/dist/BarChart/shared/translations.d.ts +69 -0
  24. package/dist/BarChart/shared/translations.js +57 -0
  25. package/dist/BarChart/shared/types.d.ts +100 -0
  26. package/dist/BarChart/shared/types.js +1 -0
  27. package/dist/BarChart/utils/hooks.d.ts +89 -0
  28. package/dist/BarChart/utils/hooks.js +281 -0
  29. package/dist/BarChart/utils/index.d.ts +56 -0
  30. package/dist/BarChart/utils/index.js +122 -0
  31. package/dist/ConnectedForm/utils.d.ts +1 -1
  32. package/dist/DataList/Tables/Rows/TableHeaderRow.js +49 -53
  33. package/dist/DataList/Tables/Rows/TableRow.js +0 -7
  34. package/dist/Form/SelectDropdown/styles.d.ts +1 -1
  35. package/dist/Form/elements/Form.d.ts +1 -1
  36. package/dist/Form/elements/FormGroupLabel.js +2 -2
  37. package/dist/Form/inputs/Select.js +6 -5
  38. package/dist/List/ListCol.js +0 -1
  39. package/dist/List/elements.d.ts +10 -2
  40. package/dist/List/elements.js +16 -27
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.js +1 -0
  43. package/package.json +6 -6
@@ -0,0 +1,281 @@
1
+ import { isColorAlias, useColorModes } from '@codecademy/gamut-styles';
2
+ import { getContrast } from 'polished';
3
+ import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
4
+ import { BarChartContext, defaultStyleConfig } from '../BarChartProvider';
5
+ import { calculatePositionPercent, getLabel, sortBars } from './index';
6
+ /**
7
+ * Hook that calculates label positions for a given maxScaleValue and tickCount (scale min is always 0).
8
+ * Returns an array of { value, positionPercent } objects.
9
+ */
10
+ export const useLabelPositions = ({
11
+ maxScaleValue,
12
+ tickCount
13
+ }) => {
14
+ return useMemo(() => Array.from({
15
+ length: tickCount
16
+ }, (_, i) => {
17
+ const value = getLabel({
18
+ labelIndex: i,
19
+ maxScaleValue,
20
+ tickCount
21
+ });
22
+ const positionPercent = calculatePositionPercent({
23
+ value,
24
+ maxScaleValue
25
+ });
26
+ return {
27
+ value,
28
+ positionPercent
29
+ };
30
+ }), [maxScaleValue, tickCount]);
31
+ };
32
+ export const useBarChartContext = () => {
33
+ return useContext(BarChartContext);
34
+ };
35
+ export const useBarChart = ({
36
+ maxScaleValue,
37
+ scaleInterval,
38
+ unit = '',
39
+ styleConfig,
40
+ animate = false,
41
+ barCount = 0,
42
+ translations
43
+ }) => {
44
+ const [widestCategoryLabelWidth, setWidestCategoryLabelWidthState] = useState(null);
45
+ const [widestTotalValueLabelWidth, setWidestTotalValueLabelWidthState] = useState(null);
46
+ const [isMeasuring, setIsMeasuring] = useState(true);
47
+ const measuredCountRef = useRef(0);
48
+ const maxCategoryLabelWidthRef = useRef(0);
49
+ const maxTotalValueLabelWidthRef = useRef(0);
50
+ const setWidestCategoryLabelWidth = useCallback(width => {
51
+ if (width > maxCategoryLabelWidthRef.current) {
52
+ maxCategoryLabelWidthRef.current = width;
53
+ setWidestCategoryLabelWidthState(width);
54
+ }
55
+ measuredCountRef.current += 1;
56
+ // Only stop measuring when we've received measurements from all bars
57
+ if (measuredCountRef.current >= barCount * 2 && barCount > 0) {
58
+ setIsMeasuring(false);
59
+ }
60
+ }, [barCount]);
61
+ const setWidestTotalValueLabelWidth = useCallback(width => {
62
+ if (width > maxTotalValueLabelWidthRef.current) {
63
+ maxTotalValueLabelWidthRef.current = width;
64
+ setWidestTotalValueLabelWidthState(width);
65
+ }
66
+ measuredCountRef.current += 1;
67
+ // Only stop measuring when we've received measurements from all bars (category + total value)
68
+ if (measuredCountRef.current >= barCount * 2 && barCount > 0) {
69
+ setIsMeasuring(false);
70
+ }
71
+ }, [barCount]);
72
+ useEffect(() => {
73
+ if (barCount > 0) {
74
+ measuredCountRef.current = 0;
75
+ maxCategoryLabelWidthRef.current = 0;
76
+ maxTotalValueLabelWidthRef.current = 0;
77
+ setIsMeasuring(true);
78
+ }
79
+ }, [barCount]);
80
+ return useMemo(() => ({
81
+ maxScaleValue,
82
+ scaleInterval: scaleInterval ?? Math.ceil(maxScaleValue / 5),
83
+ unit,
84
+ styleConfig: {
85
+ ...defaultStyleConfig,
86
+ ...styleConfig
87
+ },
88
+ animate,
89
+ widestCategoryLabelWidth,
90
+ setWidestCategoryLabelWidth,
91
+ widestTotalValueLabelWidth,
92
+ setWidestTotalValueLabelWidth,
93
+ isMeasuring,
94
+ translations
95
+ }), [maxScaleValue, scaleInterval, unit, styleConfig, animate, widestCategoryLabelWidth, setWidestCategoryLabelWidth, widestTotalValueLabelWidth, setWidestTotalValueLabelWidth, isMeasuring, translations]);
96
+ };
97
+
98
+ /**
99
+ * Hook that returns a function to get the highest contrast border color
100
+ * (white or navy-900) for a given background color.
101
+ *
102
+ * Similar to the Background component, this resolves color aliases and
103
+ * compares contrast ratios to determine the best border color.
104
+ *
105
+ * @returns A function that takes a background color and returns either 'white' or 'navy-900'
106
+ */
107
+ export const useBarBorderColor = () => {
108
+ const [, activeColors,, getColorValue] = useColorModes();
109
+ const getBorderColor = useCallback(bg => {
110
+ /** If a color alias was used then look up the true color key from the active mode */
111
+ const trueColor = isColorAlias(activeColors, bg) ? activeColors[bg] : bg;
112
+ const backgroundColor = getColorValue(trueColor);
113
+ const whiteContrast = getContrast(getColorValue('white'), backgroundColor);
114
+ const navyContrast = getContrast(getColorValue('navy-900'), backgroundColor);
115
+ return whiteContrast > navyContrast ? 'white' : 'navy-900';
116
+ }, [activeColors, getColorValue]);
117
+ return getBorderColor;
118
+ };
119
+
120
+ /**
121
+ * Generic hook for measuring element width and reporting to a callback.
122
+ * Used internally by useMeasureCategoryLabelWidth and useMeasureTotalValueLabelWidth.
123
+ */
124
+ const useMeasureWidth = ({
125
+ ref,
126
+ onMeasure,
127
+ isMeasuring
128
+ }) => {
129
+ const hasMeasuredRef = useRef(false);
130
+
131
+ // Reset measurement flag when a new measurement cycle starts
132
+ const prevIsMeasuringRef = useRef(isMeasuring);
133
+ useEffect(() => {
134
+ if (isMeasuring && !prevIsMeasuringRef.current) {
135
+ // New measurement cycle started
136
+ hasMeasuredRef.current = false;
137
+ }
138
+ prevIsMeasuringRef.current = isMeasuring;
139
+ }, [isMeasuring]);
140
+ useLayoutEffect(() => {
141
+ if (!ref.current || hasMeasuredRef.current || !isMeasuring) {
142
+ return;
143
+ }
144
+ const element = ref.current;
145
+ const width = element?.getBoundingClientRect()?.width;
146
+ if (width > 0) {
147
+ onMeasure(width);
148
+ hasMeasuredRef.current = true;
149
+ }
150
+ }, [ref, onMeasure, isMeasuring]);
151
+ };
152
+ export const useMeasureCategoryLabelWidth = ({
153
+ ref
154
+ }) => {
155
+ const {
156
+ setWidestCategoryLabelWidth,
157
+ isMeasuring
158
+ } = useBarChartContext();
159
+ useMeasureWidth({
160
+ ref,
161
+ onMeasure: setWidestCategoryLabelWidth,
162
+ isMeasuring
163
+ });
164
+ };
165
+ export const useMeasureTotalValueLabelWidth = ({
166
+ ref
167
+ }) => {
168
+ const {
169
+ setWidestTotalValueLabelWidth,
170
+ isMeasuring
171
+ } = useBarChartContext();
172
+ useMeasureWidth({
173
+ ref,
174
+ onMeasure: setWidestTotalValueLabelWidth,
175
+ isMeasuring
176
+ });
177
+ };
178
+ /**
179
+ * Hook that manages bar sorting state and provides memoized sorted bars.
180
+ * Supports predefined sort options (via string literals) and custom sort functions.
181
+ * Only returns selectProps if sortFns is provided.
182
+ */
183
+ export const useBarChartSort = ({
184
+ bars,
185
+ sortFns,
186
+ translations
187
+ }) => {
188
+ // Build options map and custom sort map from sortFns array
189
+ const {
190
+ allSortOptions,
191
+ customSortMap,
192
+ defaultSortValue
193
+ } = useMemo(() => {
194
+ if (!sortFns || sortFns.length === 0) {
195
+ return {
196
+ allSortOptions: null,
197
+ customSortMap: new Map(),
198
+ defaultSortValue: 'none'
199
+ };
200
+ }
201
+ const options = {};
202
+ const customMap = new Map();
203
+ const availableValues = [];
204
+ sortFns.forEach(item => {
205
+ if (typeof item === 'string') {
206
+ if (item === 'alphabetically') {
207
+ options['label-asc'] = translations.sortOptions.labelAsc;
208
+ options['label-desc'] = translations.sortOptions.labelDesc;
209
+ availableValues.push('label-asc', 'label-desc');
210
+ } else if (item === 'numerically') {
211
+ options['value-asc'] = translations.sortOptions.valueAsc;
212
+ options['value-desc'] = translations.sortOptions.valueDesc;
213
+ availableValues.push('value-asc', 'value-desc');
214
+ } else if (item === 'none') {
215
+ options.none = translations.sortOptions.none;
216
+ availableValues.push('none');
217
+ }
218
+ } else {
219
+ // CustomSortOption
220
+ options[item.value] = item.label;
221
+ customMap.set(item.value, item.sortFn);
222
+ availableValues.push(item.value);
223
+ }
224
+ });
225
+
226
+ // Default to "none" if available, otherwise first option
227
+ const defaultVal = availableValues.includes('none') ? 'none' : availableValues[0] || 'none';
228
+ return {
229
+ allSortOptions: options,
230
+ customSortMap: customMap,
231
+ defaultSortValue: defaultVal
232
+ };
233
+ }, [sortFns, translations]);
234
+ const [sortValue, setSortValue] = useState(defaultSortValue);
235
+
236
+ // Update sortValue when defaultSortValue changes (e.g., when sortFns changes)
237
+ useEffect(() => {
238
+ setSortValue(defaultSortValue);
239
+ }, [defaultSortValue]);
240
+ const sortedBars = useMemo(() => {
241
+ // Check if current selection is a custom sort function
242
+ const customSortFn = customSortMap.get(sortValue);
243
+ if (customSortFn) {
244
+ return customSortFn([...bars]);
245
+ }
246
+
247
+ // Otherwise use predefined sort options
248
+ if (sortValue === 'none') {
249
+ return bars;
250
+ }
251
+ const [sortBy, order] = sortValue.split('-');
252
+ const sortByValue = sortBy;
253
+ const orderValue = order === 'desc' ? 'descending' : 'ascending';
254
+ return sortBars({
255
+ bars: bars,
256
+ sortBy: sortByValue,
257
+ order: orderValue
258
+ });
259
+ }, [bars, sortValue, customSortMap]);
260
+ const onSortChange = useCallback(value => {
261
+ setSortValue(value);
262
+ }, []);
263
+ const selectProps = useMemo(() => {
264
+ if (!allSortOptions) {
265
+ return null;
266
+ }
267
+ return {
268
+ options: allSortOptions,
269
+ value: sortValue,
270
+ onChange: e => {
271
+ onSortChange(e.target.value);
272
+ }
273
+ };
274
+ }, [sortValue, onSortChange, allSortOptions]);
275
+ return {
276
+ sortedBars,
277
+ sortValue,
278
+ onSortChange,
279
+ selectProps
280
+ };
281
+ };
@@ -0,0 +1,56 @@
1
+ import { BarChartTranslations } from '../shared/translations';
2
+ import { BarChartUnit, BarProps, MaxScaleValue, ScaleTickCount } from '../shared/types';
3
+ export declare const calculatePercent: ({ value, total, }: {
4
+ value: number;
5
+ total: number;
6
+ }) => number;
7
+ export declare const calculateBarWidth: ({ value, maxScaleValue, }: {
8
+ value: number;
9
+ maxScaleValue: MaxScaleValue;
10
+ }) => number;
11
+ /**
12
+ * Formats a numeric value with optional unit for bar chart labels.
13
+ */
14
+ export declare const formatValueWithUnit: ({ value, unit, locale, }: {
15
+ value: number;
16
+ unit: string;
17
+ locale?: string | undefined;
18
+ }) => string;
19
+ export declare const formatNumberUnitCompact: ({ num, locale, }: {
20
+ num: number;
21
+ locale?: string | undefined;
22
+ }) => string;
23
+ /**
24
+ * Sort bars based on sortBy and order configuration
25
+ */
26
+ export declare const sortBars: <T extends BarProps>({ bars, sortBy, order, }: {
27
+ bars: T[];
28
+ sortBy: 'label' | 'value' | 'none';
29
+ order: 'ascending' | 'descending';
30
+ }) => T[];
31
+ /**
32
+ * Returns the accessibility summary for a bar row. The summary is used as aria-label on the row's link/button when interactive, or as screenreader-only text when not. Summaries come from stackedBarSummary (stacked rows) or singleValueBarSummary (single-value rows), with default implementations when not overridden.
33
+ */
34
+ export declare const getValuesSummary: ({ seriesOneValue, seriesTwoValue, unit, categoryLabel, translations, }: Pick<BarProps, "categoryLabel" | "seriesOneValue" | "seriesTwoValue"> & Required<Pick<BarChartUnit, "unit">> & {
35
+ translations: BarChartTranslations;
36
+ }) => string;
37
+ /**
38
+ * Calculates the value for a given label position (scale min is always 0).
39
+ */
40
+ export declare const getLabel: ({ tickCount, labelIndex, maxScaleValue, }: {
41
+ tickCount: ScaleTickCount;
42
+ labelIndex: number;
43
+ maxScaleValue: MaxScaleValue;
44
+ }) => number;
45
+ /**
46
+ * Calculates the percentage position for a given value within 0–maxScaleValue range.
47
+ * Returns a value between 0 and 100 representing the position percentage.
48
+ */
49
+ export declare const calculatePositionPercent: ({ value, maxScaleValue, }: {
50
+ value: number;
51
+ maxScaleValue: MaxScaleValue;
52
+ }) => number;
53
+ /**
54
+ * Generates a stable key for a bar row (for React list keys).
55
+ */
56
+ export declare const getBarRowKey: (bar: Pick<BarProps, 'categoryLabel' | 'seriesOneValue' | 'seriesTwoValue'>, index: number) => string;
@@ -0,0 +1,122 @@
1
+ import { getDefaultSingleValueBarSummary, getDefaultStackedBarSummary } from '../shared/translations';
2
+ export const calculatePercent = ({
3
+ value,
4
+ total
5
+ }) => {
6
+ return value / total * 100;
7
+ };
8
+ export const calculateBarWidth = ({
9
+ value,
10
+ maxScaleValue
11
+ }) => {
12
+ const adjustedValue = Math.max(0, Math.min(maxScaleValue, value));
13
+ return Math.floor(calculatePercent({
14
+ value: adjustedValue,
15
+ total: maxScaleValue
16
+ }));
17
+ };
18
+
19
+ /**
20
+ * Formats a numeric value with optional unit for bar chart labels.
21
+ */
22
+ export const formatValueWithUnit = ({
23
+ value,
24
+ unit,
25
+ locale = 'en'
26
+ }) => {
27
+ const formatted = Intl.NumberFormat(locale).format(value);
28
+ return unit ? `${formatted} ${unit}` : formatted;
29
+ };
30
+ export const formatNumberUnitCompact = ({
31
+ num,
32
+ locale = 'en'
33
+ }) => Intl.NumberFormat(locale, {
34
+ notation: 'compact',
35
+ compactDisplay: 'short'
36
+ }).format(num);
37
+
38
+ /**
39
+ * Sort bars based on sortBy and order configuration
40
+ */
41
+ export const sortBars = ({
42
+ bars,
43
+ sortBy,
44
+ order
45
+ }) => {
46
+ if (sortBy === 'none' || !sortBy) {
47
+ return bars;
48
+ }
49
+ const sorted = [...bars].sort((a, b) => {
50
+ if (sortBy === 'label') {
51
+ return a.categoryLabel.localeCompare(b.categoryLabel);
52
+ }
53
+ const aValue = a.seriesTwoValue ?? a.seriesOneValue;
54
+ const bValue = b.seriesTwoValue ?? b.seriesOneValue;
55
+ return aValue - bValue;
56
+ });
57
+ return order === 'descending' ? sorted.reverse() : sorted;
58
+ };
59
+
60
+ /**
61
+ * Returns the accessibility summary for a bar row. The summary is used as aria-label on the row's link/button when interactive, or as screenreader-only text when not. Summaries come from stackedBarSummary (stacked rows) or singleValueBarSummary (single-value rows), with default implementations when not overridden.
62
+ */
63
+ export const getValuesSummary = ({
64
+ seriesOneValue,
65
+ seriesTwoValue,
66
+ unit,
67
+ categoryLabel,
68
+ translations
69
+ }) => {
70
+ const {
71
+ locale
72
+ } = translations;
73
+ const singleValueCtx = {
74
+ value: seriesOneValue,
75
+ unit,
76
+ locale,
77
+ categoryLabel
78
+ };
79
+ if (seriesTwoValue !== undefined) {
80
+ const gained = seriesTwoValue - seriesOneValue;
81
+ const stackedCtx = {
82
+ categoryLabel,
83
+ seriesOneValue,
84
+ seriesTwoValue,
85
+ unit,
86
+ locale,
87
+ gained
88
+ };
89
+ return (translations.accessibility.stackedBarSummary ?? getDefaultStackedBarSummary)(stackedCtx);
90
+ }
91
+ return (translations.accessibility.singleValueBarSummary ?? getDefaultSingleValueBarSummary)(singleValueCtx);
92
+ };
93
+
94
+ /**
95
+ * Calculates the value for a given label position (scale min is always 0).
96
+ */
97
+ export const getLabel = ({
98
+ tickCount,
99
+ labelIndex,
100
+ maxScaleValue
101
+ }) => {
102
+ if (tickCount <= 1) return maxScaleValue;
103
+ const incrementalDecimal = labelIndex / (tickCount - 1);
104
+ return Math.floor(incrementalDecimal * maxScaleValue);
105
+ };
106
+
107
+ /**
108
+ * Calculates the percentage position for a given value within 0–maxScaleValue range.
109
+ * Returns a value between 0 and 100 representing the position percentage.
110
+ */
111
+ export const calculatePositionPercent = ({
112
+ value,
113
+ maxScaleValue
114
+ }) => {
115
+ if (maxScaleValue === 0) return 0;
116
+ return value / maxScaleValue * 100;
117
+ };
118
+
119
+ /**
120
+ * Generates a stable key for a bar row (for React list keys).
121
+ */
122
+ export const getBarRowKey = (bar, index) => bar.categoryLabel && bar.seriesOneValue ? `${bar.categoryLabel}-${bar.seriesOneValue}-${bar.seriesTwoValue ?? ''}-${index}` : String(index);
@@ -122,9 +122,9 @@ export declare function useDebouncedField<T extends InputTypes>({ name, watchUpd
122
122
  value: T extends "checkbox" ? boolean : string;
123
123
  error: string | FieldError | Merge<FieldError, FieldErrorsImpl<any>> | undefined;
124
124
  ref: import("react-hook-form").UseFormRegisterReturn<string>;
125
- control: import("react-hook-form").Control<import("react-hook-form").FieldValues, any, import("react-hook-form").FieldValues>;
126
125
  isDisabled: boolean;
127
126
  isLoading: boolean | undefined;
127
+ control: import("react-hook-form").Control<import("react-hook-form").FieldValues, any, import("react-hook-form").FieldValues>;
128
128
  isSoloField: boolean | undefined;
129
129
  validation: RegisterOptions | undefined;
130
130
  getValues: import("react-hook-form").UseFormGetValues<import("react-hook-form").FieldValues>;
@@ -6,7 +6,7 @@ import { ExpandControl, FilterControl, SelectControl, SortControl } from '../../
6
6
  import { useControlContext } from '../../hooks/useListControls';
7
7
  import { useListState } from '../../hooks/useListState';
8
8
  import { StyledHeaderRow } from './elements';
9
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  export const TableHeaderRow = ({
11
11
  columns,
12
12
  selected = false,
@@ -26,62 +26,58 @@ export const TableHeaderRow = ({
26
26
  variant,
27
27
  listType
28
28
  } = useListContext();
29
- const dataTablePadding = listType === 'table' && variant === 'table';
30
29
  const headerRowDirections = useListState().query?.sort;
31
- return /*#__PURE__*/_jsx(StyledHeaderRow, {
30
+ return /*#__PURE__*/_jsxs(StyledHeaderRow, {
32
31
  invisible: invisible,
33
32
  isDataList: listType === 'table' && variant !== 'table',
34
- children: /*#__PURE__*/_jsxs(_Fragment, {
35
- children: [selectable && /*#__PURE__*/_jsx(ListCol, {
36
- size: "content",
37
- children: !hideSelectAll && /*#__PURE__*/_jsx(SelectControl, {
38
- disabled: empty,
39
- label: "Select All",
40
- name: prefixId('all'),
41
- rowId: "header",
42
- selected: selected,
43
- onSelect: onSelect
33
+ children: [selectable && /*#__PURE__*/_jsx(ListCol, {
34
+ size: "content",
35
+ children: !hideSelectAll && /*#__PURE__*/_jsx(SelectControl, {
36
+ disabled: empty,
37
+ label: "Select All",
38
+ name: prefixId('all'),
39
+ rowId: "header",
40
+ selected: selected,
41
+ onSelect: onSelect
42
+ })
43
+ }), columns.map(({
44
+ key,
45
+ header,
46
+ sortable,
47
+ filters,
48
+ ...colProps
49
+ }) => {
50
+ const rowProperty = key;
51
+ const renderKey = prefixId(`header-col-${rowProperty}`);
52
+ const columnText = String(header || key);
53
+ const sortDirection = headerRowDirections?.[rowProperty] ?? 'none';
54
+ const ariaSortDirection = sortDirection === 'none' ? 'none' : sortDirection === 'asc' ? 'ascending' : 'descending';
55
+ return /*#__PURE__*/_jsx(ListCol, {
56
+ ...colProps,
57
+ "aria-sort": sortable ? ariaSortDirection : undefined,
58
+ columnHeader: true,
59
+ children: /*#__PURE__*/_jsxs(FlexBox, {
60
+ alignItems: selectable ? 'center' : 'flex-end',
61
+ gap: 8,
62
+ height: "100%",
63
+ width: "100%",
64
+ children: [filters && /*#__PURE__*/_jsx(FilterControl, {
65
+ columnKey: rowProperty,
66
+ justify: colProps.justify,
67
+ options: filters,
68
+ onFilter: onFilter
69
+ }), sortable ? /*#__PURE__*/_jsx(SortControl, {
70
+ columnKey: rowProperty,
71
+ onSort: onSort,
72
+ children: columnText
73
+ }) : columnText]
44
74
  })
45
- }), columns.map(({
46
- key,
47
- header,
48
- sortable,
49
- filters,
50
- ...colProps
51
- }) => {
52
- const rowProperty = key;
53
- const renderKey = prefixId(`header-col-${rowProperty}`);
54
- const columnText = String(header || key);
55
- const sortDirection = headerRowDirections?.[rowProperty] ?? 'none';
56
- const ariaSortDirection = sortDirection === 'none' ? 'none' : sortDirection === 'asc' ? 'ascending' : 'descending';
57
- return /*#__PURE__*/_jsx(ListCol, {
58
- ...colProps,
59
- "aria-sort": sortable ? ariaSortDirection : undefined,
60
- columnHeader: true,
61
- dataTablePadding: dataTablePadding,
62
- children: /*#__PURE__*/_jsxs(FlexBox, {
63
- alignItems: "flex-end",
64
- gap: 8,
65
- height: "100%",
66
- width: "100%",
67
- children: [filters && /*#__PURE__*/_jsx(FilterControl, {
68
- columnKey: rowProperty,
69
- justify: colProps.justify,
70
- options: filters,
71
- onFilter: onFilter
72
- }), sortable ? /*#__PURE__*/_jsx(SortControl, {
73
- columnKey: rowProperty,
74
- onSort: onSort,
75
- children: columnText
76
- }) : columnText]
77
- })
78
- }, renderKey);
79
- }), expandable && /*#__PURE__*/_jsx(ListCol, {
80
- ghost: true,
81
- size: "content",
82
- children: /*#__PURE__*/_jsx(ExpandControl, {})
83
- })]
84
- })
75
+ }, renderKey);
76
+ }), expandable && /*#__PURE__*/_jsx(ListCol, {
77
+ ghost: true,
78
+ size: "content",
79
+ children: /*#__PURE__*/_jsx(ExpandControl, {})
80
+ })]
85
81
  });
86
82
  };
87
83
  export const HeaderRow = /*#__PURE__*/memo(TableHeaderRow);
@@ -1,7 +1,6 @@
1
1
  import { isValidElement, memo, useCallback, useMemo, createElement as _createElement } from 'react';
2
2
  import { Text } from '../../..';
3
3
  import { ListCol, ListRow } from '../../../List';
4
- import { useListContext } from '../../../List/ListProvider';
5
4
  import { Shimmer } from '../../../Loading/Shimmer';
6
5
  import { ExpandControl, SelectControl } from '../../Controls';
7
6
  import { useControlContext } from '../../hooks/useListControls';
@@ -23,13 +22,7 @@ export const TableRow = ({
23
22
  onSelect,
24
23
  prefixId
25
24
  } = useControlContext();
26
- const {
27
- variant,
28
- listType
29
- } = useListContext();
30
- const dataTablePadding = listType === 'table' && variant === 'table';
31
25
  const listColProps = {
32
- dataTablePadding,
33
26
  showOverflow
34
27
  };
35
28
  const controlIndices = useMemo(() => {
@@ -1,6 +1,6 @@
1
1
  import { theme as GamutTheme } from '@codecademy/gamut-styles';
2
2
  import { StylesConfig } from 'react-select';
3
- export declare const conditionalBorderStates: (props: Partial<Record<"error" | "isFocused" | "activated" | "isDisabled", boolean>> & {
3
+ export declare const conditionalBorderStates: (props: Partial<Record<"error" | "activated" | "isDisabled" | "isFocused", boolean>> & {
4
4
  theme?: import("@emotion/react").Theme | undefined;
5
5
  }) => import("@codecademy/variance").CSSObject;
6
6
  export declare const getMemoizedStyles: (theme: typeof GamutTheme, zIndex?: number) => StylesConfig<any, false>;
@@ -526,7 +526,7 @@ declare const StyledForm: import("@emotion/styled").StyledComponent<{
526
526
  }>;
527
527
  } & {
528
528
  theme?: import("@emotion/react").Theme | undefined;
529
- }, Pick<React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>, "name" | "slot" | "style" | "title" | "dir" | "children" | "className" | "aria-hidden" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoCapitalize" | "autoFocus" | "contentEditable" | "contextMenu" | "draggable" | "enterKeyHint" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "content" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "exportparts" | "part" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "method" | "target" | "acceptCharset" | "action" | "autoComplete" | "encType" | "noValidate" | keyof React.ClassAttributes<HTMLFormElement>>, {}>;
529
+ }, Pick<React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>, "name" | "slot" | "style" | "title" | "dir" | "children" | "className" | "aria-hidden" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoCapitalize" | "autoFocus" | "contentEditable" | "contextMenu" | "draggable" | "enterKeyHint" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "content" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "exportparts" | "part" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "method" | "target" | "autoComplete" | "acceptCharset" | "action" | "encType" | "noValidate" | keyof React.ClassAttributes<HTMLFormElement>>, {}>;
530
530
  export type FormProps = ComponentProps<typeof StyledForm>;
531
531
  export declare const Form: React.FC<FormProps>;
532
532
  export {};