@codecademy/gamut 68.0.1-alpha.7afb48.0 → 68.0.1-alpha.800a90.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 (57) hide show
  1. package/dist/Anchor/index.d.ts +93 -18
  2. package/dist/Badge/index.d.ts +93 -18
  3. package/dist/Box/props.d.ts +93 -18
  4. package/dist/Button/shared/styles.d.ts +93 -18
  5. package/dist/Card/elements.d.ts +279 -54
  6. package/dist/ConnectedForm/utils.d.ts +1 -1
  7. package/dist/Form/SelectDropdown/styles.d.ts +1 -1
  8. package/dist/Form/elements/Form.d.ts +94 -19
  9. package/dist/Form/elements/FormGroupLabel.js +2 -2
  10. package/dist/Form/inputs/Select.js +5 -6
  11. package/dist/GridForm/GridFormSections/GridFormSectionBreak.d.ts +33 -6
  12. package/dist/InternalFloatingCard/InternalFloatingCard.d.ts +63 -12
  13. package/dist/Layout/Column.d.ts +93 -18
  14. package/dist/Layout/LayoutGrid.d.ts +60 -12
  15. package/dist/List/elements.d.ts +187 -37
  16. package/dist/Menu/elements.d.ts +93 -18
  17. package/dist/Pagination/AnimatedPaginationButtons.d.ts +95 -20
  18. package/dist/Pagination/EllipsisButton.d.ts +5 -5
  19. package/dist/Pagination/EllipsisButton.js +2 -2
  20. package/dist/Pagination/index.js +4 -4
  21. package/dist/Pagination/utils.d.ts +93 -18
  22. package/dist/Pagination/utils.js +1 -1
  23. package/dist/Tabs/props.d.ts +93 -18
  24. package/dist/Tag/types.d.ts +93 -18
  25. package/dist/Toggle/elements.d.ts +120 -24
  26. package/dist/Typography/Text.d.ts +93 -18
  27. package/dist/index.d.ts +0 -1
  28. package/dist/index.js +0 -1
  29. package/package.json +7 -7
  30. package/dist/BarChart/BarChartProvider.d.ts +0 -19
  31. package/dist/BarChart/BarChartProvider.js +0 -31
  32. package/dist/BarChart/BarRow/elements.d.ts +0 -713
  33. package/dist/BarChart/BarRow/elements.js +0 -89
  34. package/dist/BarChart/BarRow/index.d.ts +0 -26
  35. package/dist/BarChart/BarRow/index.js +0 -254
  36. package/dist/BarChart/GENERIC_EXAMPLE.d.ts +0 -14
  37. package/dist/BarChart/GENERIC_EXAMPLE.js +0 -333
  38. package/dist/BarChart/index.d.ts +0 -4
  39. package/dist/BarChart/index.js +0 -158
  40. package/dist/BarChart/layout/GridLines.d.ts +0 -7
  41. package/dist/BarChart/layout/GridLines.js +0 -78
  42. package/dist/BarChart/layout/ScaleChartHeader.d.ts +0 -10
  43. package/dist/BarChart/layout/ScaleChartHeader.js +0 -89
  44. package/dist/BarChart/layout/VerticalSpacer.d.ts +0 -6
  45. package/dist/BarChart/layout/VerticalSpacer.js +0 -56
  46. package/dist/BarChart/shared/elements.d.ts +0 -7
  47. package/dist/BarChart/shared/elements.js +0 -12
  48. package/dist/BarChart/shared/styles.d.ts +0 -4
  49. package/dist/BarChart/shared/styles.js +0 -4
  50. package/dist/BarChart/shared/translations.d.ts +0 -17
  51. package/dist/BarChart/shared/translations.js +0 -16
  52. package/dist/BarChart/shared/types.d.ts +0 -88
  53. package/dist/BarChart/shared/types.js +0 -1
  54. package/dist/BarChart/utils/hooks.d.ts +0 -93
  55. package/dist/BarChart/utils/hooks.js +0 -301
  56. package/dist/BarChart/utils/index.d.ts +0 -86
  57. package/dist/BarChart/utils/index.js +0 -165
@@ -1,301 +0,0 @@
1
- import { useColorModes } from '@codecademy/gamut-styles';
2
- import { getContrast } from 'polished';
3
- import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
4
- import { BarChartContext } from '../BarChartProvider';
5
- import { calculatePositionPercent, getLabel, sortBars } from './index';
6
- const defaultStyleConfig = {
7
- textColor: 'text',
8
- foregroundBarColor: 'feedback-warning',
9
- backgroundBarColor: 'background-primary',
10
- seriesOneLabel: 'text-secondary',
11
- seriesTwoLabel: 'primary'
12
- };
13
-
14
- /**
15
- * Hook that calculates label positions for a given range and count
16
- * Returns an array of { value, positionPercent } objects
17
- */
18
- export const useLabelPositions = ({
19
- min,
20
- max,
21
- count
22
- }) => {
23
- return useMemo(() => Array.from({
24
- length: count
25
- }, (_, i) => {
26
- const value = getLabel({
27
- labelCount: count,
28
- labelIndex: i,
29
- min,
30
- max
31
- });
32
- const positionPercent = calculatePositionPercent({
33
- value,
34
- min,
35
- max
36
- });
37
- return {
38
- value,
39
- positionPercent
40
- };
41
- }), [min, max, count]);
42
- };
43
- export const useBarChartContext = () => {
44
- return useContext(BarChartContext);
45
- };
46
- export const useBarChart = ({
47
- minRange,
48
- maxRange,
49
- xScale,
50
- unit = '',
51
- styleConfig,
52
- animate = false,
53
- barCount = 0,
54
- translations
55
- }) => {
56
- const [widestLeftLabelWidth, setWidestLeftLabelWidthState] = useState(null);
57
- const [widestRightLabelWidth, setWidestRightLabelWidthState] = useState(null);
58
- const [isMeasuring, setIsMeasuring] = useState(true);
59
- const measuredCountRef = useRef(0);
60
- const maxLeftWidthRef = useRef(0);
61
- const maxRightWidthRef = useRef(0);
62
- const setWidestLeftLabelWidth = useCallback(width => {
63
- if (width > maxLeftWidthRef.current) {
64
- maxLeftWidthRef.current = width;
65
- setWidestLeftLabelWidthState(width);
66
- }
67
- measuredCountRef.current += 1;
68
- // Only stop measuring when we've received measurements from all bars
69
- if (measuredCountRef.current >= barCount * 2 && barCount > 0) {
70
- setIsMeasuring(false);
71
- }
72
- }, [barCount]);
73
- const setWidestRightLabelWidth = useCallback(width => {
74
- if (width > maxRightWidthRef.current) {
75
- maxRightWidthRef.current = width;
76
- setWidestRightLabelWidthState(width);
77
- }
78
- measuredCountRef.current += 1;
79
- // Only stop measuring when we've received measurements from all bars (left + right)
80
- if (measuredCountRef.current >= barCount * 2 && barCount > 0) {
81
- setIsMeasuring(false);
82
- }
83
- }, [barCount]);
84
- useEffect(() => {
85
- if (barCount > 0) {
86
- measuredCountRef.current = 0;
87
- maxLeftWidthRef.current = 0;
88
- maxRightWidthRef.current = 0;
89
- setIsMeasuring(true);
90
- }
91
- }, [barCount]);
92
- return useMemo(() => ({
93
- minRange,
94
- maxRange,
95
- xScale: xScale ?? Math.ceil((maxRange - minRange) / 5),
96
- unit,
97
- styleConfig: {
98
- ...defaultStyleConfig,
99
- ...styleConfig
100
- },
101
- animate,
102
- widestLeftLabelWidth,
103
- setWidestLeftLabelWidth,
104
- widestRightLabelWidth,
105
- setWidestRightLabelWidth,
106
- isMeasuring,
107
- translations
108
- }), [minRange, maxRange, xScale, unit, styleConfig, animate, widestLeftLabelWidth, setWidestLeftLabelWidth, widestRightLabelWidth, setWidestRightLabelWidth, isMeasuring, translations]);
109
- };
110
-
111
- /**
112
- * Checks if a color is a color alias (exists in the color mode shape)
113
- */
114
- const isColorAlias = (mode, color) => Object.keys(mode).includes(color);
115
-
116
- /**
117
- * Hook that returns a function to get the highest contrast border color
118
- * (white or navy-900) for a given background color.
119
- *
120
- * Similar to the Background component, this resolves color aliases and
121
- * compares contrast ratios to determine the best border color.
122
- *
123
- * @returns A function that takes a background color and returns either 'white' or 'navy-900'
124
- */
125
- export const useBarBorderColor = () => {
126
- const [, activeColors,, getColorValue] = useColorModes();
127
- const getBorderColor = useCallback(bg => {
128
- /** If a color alias was used then look up the true color key from the active mode */
129
- const trueColor = isColorAlias(activeColors, bg) ? activeColors[bg] : bg;
130
- const backgroundColor = getColorValue(trueColor);
131
- const whiteContrast = getContrast(getColorValue('white'), backgroundColor);
132
- const navyContrast = getContrast(getColorValue('navy-900'), backgroundColor);
133
- return whiteContrast > navyContrast ? 'white' : 'navy-900';
134
- }, [activeColors, getColorValue]);
135
- return getBorderColor;
136
- };
137
-
138
- /**
139
- * Generic hook for measuring element width and reporting to a callback.
140
- * Used internally by useMeasureLeftLabelWidth and useMeasureRightLabelWidth.
141
- */
142
- const useMeasureWidth = ({
143
- ref,
144
- onMeasure,
145
- isMeasuring
146
- }) => {
147
- const hasMeasuredRef = useRef(false);
148
-
149
- // Reset measurement flag when a new measurement cycle starts
150
- const prevIsMeasuringRef = useRef(isMeasuring);
151
- useEffect(() => {
152
- if (isMeasuring && !prevIsMeasuringRef.current) {
153
- // New measurement cycle started
154
- hasMeasuredRef.current = false;
155
- }
156
- prevIsMeasuringRef.current = isMeasuring;
157
- }, [isMeasuring]);
158
- useLayoutEffect(() => {
159
- if (!ref.current || hasMeasuredRef.current || !isMeasuring) {
160
- return;
161
- }
162
- const element = ref.current;
163
- const width = element?.getBoundingClientRect()?.width;
164
- if (width > 0) {
165
- onMeasure(width);
166
- hasMeasuredRef.current = true;
167
- }
168
- }, [ref, onMeasure, isMeasuring]);
169
- };
170
- export const useMeasureLeftLabelWidth = ({
171
- ref
172
- }) => {
173
- const {
174
- setWidestLeftLabelWidth,
175
- isMeasuring
176
- } = useBarChartContext();
177
- useMeasureWidth({
178
- ref,
179
- onMeasure: setWidestLeftLabelWidth,
180
- isMeasuring
181
- });
182
- };
183
- export const useMeasureRightLabelWidth = ({
184
- ref
185
- }) => {
186
- const {
187
- setWidestRightLabelWidth,
188
- isMeasuring
189
- } = useBarChartContext();
190
- useMeasureWidth({
191
- ref,
192
- onMeasure: setWidestRightLabelWidth,
193
- isMeasuring
194
- });
195
- };
196
- /**
197
- * Hook that manages bar sorting state and provides memoized sorted bars.
198
- * Supports predefined sort options (via string literals) and custom sort functions.
199
- * Only returns selectProps if sortFns is provided.
200
- */
201
- export const useBarChartSort = ({
202
- bars,
203
- sortFns,
204
- translations
205
- }) => {
206
- // Build options map and custom sort map from sortFns array
207
- const {
208
- allSortOptions,
209
- customSortMap,
210
- defaultSortValue
211
- } = useMemo(() => {
212
- if (!sortFns || sortFns.length === 0) {
213
- return {
214
- allSortOptions: null,
215
- customSortMap: new Map(),
216
- defaultSortValue: 'none'
217
- };
218
- }
219
- const options = {};
220
- const customMap = new Map();
221
- const availableValues = [];
222
- sortFns.forEach(item => {
223
- if (typeof item === 'string') {
224
- if (item === 'alphabetically') {
225
- options['label-asc'] = translations.sortOptions.labelAsc;
226
- options['label-desc'] = translations.sortOptions.labelDesc;
227
- availableValues.push('label-asc', 'label-desc');
228
- } else if (item === 'numerically') {
229
- options['value-asc'] = translations.sortOptions.valueAsc;
230
- options['value-desc'] = translations.sortOptions.valueDesc;
231
- availableValues.push('value-asc', 'value-desc');
232
- } else if (item === 'none') {
233
- options.none = translations.sortOptions.none;
234
- availableValues.push('none');
235
- }
236
- } else {
237
- // CustomSortOption
238
- options[item.value] = item.label;
239
- customMap.set(item.value, item.sortFn);
240
- availableValues.push(item.value);
241
- }
242
- });
243
-
244
- // Default to "none" if available, otherwise first option
245
- const defaultVal = availableValues.includes('none') ? 'none' : availableValues[0] || 'none';
246
- return {
247
- allSortOptions: options,
248
- customSortMap: customMap,
249
- defaultSortValue: defaultVal
250
- };
251
- }, [sortFns, translations]);
252
- const [sortValue, setSortValue] = useState(defaultSortValue);
253
-
254
- // Update sortValue when defaultSortValue changes (e.g., when sortFns changes)
255
- useEffect(() => {
256
- setSortValue(defaultSortValue);
257
- }, [defaultSortValue]);
258
- const sortedBars = useMemo(() => {
259
- // Check if current selection is a custom sort function
260
- const customSortFn = customSortMap.get(sortValue);
261
- if (customSortFn) {
262
- return customSortFn([...bars]);
263
- }
264
-
265
- // Otherwise use predefined sort options
266
- if (sortValue === 'none') {
267
- return bars;
268
- }
269
- const [sortBy, order] = sortValue.split('-');
270
- const sortByValue = sortBy;
271
- const orderValue = order === 'desc' ? 'descending' : 'ascending';
272
- return sortBars({
273
- bars: bars,
274
- sortBy: sortByValue,
275
- order: orderValue
276
- });
277
- }, [bars, sortValue, customSortMap]);
278
- const onSortChange = useCallback(value => {
279
- setSortValue(value);
280
- }, []);
281
- const selectId = useRef(`bar-chart-sort-${Math.random().toString(36).slice(2, 11)}`);
282
- const selectProps = useMemo(() => {
283
- if (!allSortOptions) {
284
- return null;
285
- }
286
- return {
287
- options: allSortOptions,
288
- value: sortValue,
289
- onChange: e => {
290
- onSortChange(e.target.value);
291
- },
292
- id: selectId.current
293
- };
294
- }, [sortValue, onSortChange, allSortOptions]);
295
- return {
296
- sortedBars,
297
- sortValue,
298
- onSortChange,
299
- selectProps
300
- };
301
- };
@@ -1,86 +0,0 @@
1
- import { BarChartTranslations } from '../shared/translations';
2
- import { BarChartRange, BarChartUnit, BarProps } from '../shared/types';
3
- export declare const numDigits: ({ num }: {
4
- num: number;
5
- }) => number;
6
- export declare const columnBaseSize: ({ experience }: {
7
- experience?: number | undefined;
8
- }) => {
9
- sm: number;
10
- md: number;
11
- lg: number;
12
- xl: number;
13
- };
14
- export declare const calculatePercent: ({ value, total, }: {
15
- value: number;
16
- total: number;
17
- }) => number;
18
- export declare const calculateBarWidth: ({ value, minRange, maxRange, }: {
19
- value: number;
20
- } & BarChartRange) => number;
21
- /**
22
- * Calculate tick spacing and nice minimum and maximum data points on the axis.
23
- */
24
- export declare const calculateTicksAndRange: ({ maxTicks, min, max, }: {
25
- maxTicks: number;
26
- } & {
27
- min: BarChartRange['minRange'];
28
- max: BarChartRange['maxRange'];
29
- }) => [number, number, number];
30
- /**
31
- * Returns a "nice" number approximately equal to range
32
- * Rounds the number if round = true
33
- * Takes the ceiling if round = false.
34
- * A nice number is a simple decimal number, for example if a number is 1234, a nice number would be 1000 or 2000.
35
- */
36
- export declare const niceNum: ({ range, roundDown, }: {
37
- range: number;
38
- roundDown: boolean;
39
- }) => number;
40
- export declare const getPercentDiff: ({ v1, v2 }: {
41
- v1: number;
42
- v2: number;
43
- }) => number;
44
- export declare const formatNumberUS: ({ num, locale, }: {
45
- num: number;
46
- locale?: string | undefined;
47
- }) => string;
48
- export declare const formatNumberUSCompact: ({ num, locale, }: {
49
- num: number;
50
- locale?: string | undefined;
51
- }) => string;
52
- /**
53
- * Sort bars based on sortBy and order configuration
54
- */
55
- export declare const sortBars: <T extends BarProps>({ bars, sortBy, order, }: {
56
- bars: T[];
57
- sortBy: 'label' | 'value' | 'none';
58
- order: 'ascending' | 'descending';
59
- }) => T[];
60
- /**
61
- * Generates an accessible summary of the bar values
62
- */
63
- export declare const getValuesSummary: ({ isInteractive, seriesOneValue, seriesTwoValue, unit, yLabel, translations, }: Pick<BarProps, "yLabel" | "seriesOneValue" | "seriesTwoValue"> & Required<Pick<BarChartUnit, "unit">> & {
64
- isInteractive: boolean;
65
- translations: BarChartTranslations;
66
- }) => string;
67
- /**
68
- * Calculates the value for a given label position
69
- */
70
- export declare const getLabel: ({ labelCount, labelIndex, min, max, }: {
71
- labelCount: number;
72
- labelIndex: number;
73
- } & {
74
- min: BarChartRange['minRange'];
75
- max: BarChartRange['maxRange'];
76
- }) => number;
77
- /**
78
- * Calculates the percentage position for a given value within a range
79
- * Returns a value between 0 and 100 representing the position percentage
80
- */
81
- export declare const calculatePositionPercent: ({ value, min, max, }: {
82
- value: number;
83
- } & {
84
- min: BarChartRange['minRange'];
85
- max: BarChartRange['maxRange'];
86
- }) => number;
@@ -1,165 +0,0 @@
1
- export const numDigits = ({
2
- num
3
- }) => {
4
- return Math.max(Math.floor(Math.log10(Math.abs(num))), 0) + 1;
5
- };
6
- export const columnBaseSize = ({
7
- experience = 3
8
- }) => {
9
- const digits = numDigits({
10
- num: experience
11
- });
12
- return {
13
- sm: digits > 4 ? 5 : 4,
14
- md: digits > 4 ? 5 : 4,
15
- lg: digits > 4 ? 4 : 5,
16
- xl: digits > 4 ? 5 : 4
17
- };
18
- };
19
- export const calculatePercent = ({
20
- value,
21
- total
22
- }) => {
23
- return value / total * 100;
24
- };
25
- export const calculateBarWidth = ({
26
- value,
27
- minRange,
28
- maxRange
29
- }) => {
30
- const range = maxRange - minRange;
31
- const adjustedValue = Math.max(0, Math.min(range, value - minRange));
32
- return Math.floor(calculatePercent({
33
- value: adjustedValue,
34
- total: range
35
- }));
36
- };
37
-
38
- /**
39
- * Calculate tick spacing and nice minimum and maximum data points on the axis.
40
- */
41
- export const calculateTicksAndRange = ({
42
- maxTicks,
43
- min,
44
- max
45
- }) => {
46
- const range = niceNum({
47
- range: max - min,
48
- roundDown: false
49
- });
50
- const tickSpacing = niceNum({
51
- range: range / (maxTicks - 1),
52
- roundDown: true
53
- });
54
- const niceMin = Math.floor(min / tickSpacing) * tickSpacing;
55
- const niceMax = Math.ceil(max / tickSpacing) * tickSpacing;
56
- const tickCount = range / tickSpacing;
57
- return [tickCount, niceMin, niceMax];
58
- };
59
-
60
- /**
61
- * Returns a "nice" number approximately equal to range
62
- * Rounds the number if round = true
63
- * Takes the ceiling if round = false.
64
- * A nice number is a simple decimal number, for example if a number is 1234, a nice number would be 1000 or 2000.
65
- */
66
- export const niceNum = ({
67
- range,
68
- roundDown
69
- }) => {
70
- const exponent = Math.floor(Math.log10(range));
71
- const fraction = range / 10 ** exponent;
72
- let niceFraction;
73
- if (roundDown) {
74
- if (fraction >= 10) niceFraction = 10;else if (fraction >= 5) niceFraction = 5;else if (fraction >= 2) niceFraction = 2;else if (fraction >= 1) niceFraction = 1;else niceFraction = 1;
75
- } else if (fraction <= 1) niceFraction = 1;else if (fraction <= 2) niceFraction = 2;else if (fraction <= 5) niceFraction = 5;else niceFraction = 10;
76
- return niceFraction * 10 ** exponent;
77
- };
78
- export const getPercentDiff = ({
79
- v1,
80
- v2
81
- }) => {
82
- return Math.abs(v1 - v2) / ((v1 + v2) / 2) * 100;
83
- };
84
- export const formatNumberUS = ({
85
- num,
86
- locale = 'en'
87
- }) => Intl.NumberFormat(locale).format(num);
88
- export const formatNumberUSCompact = ({
89
- num,
90
- locale = 'en'
91
- }) => Intl.NumberFormat(locale, {
92
- notation: 'compact',
93
- compactDisplay: 'short'
94
- }).format(num);
95
-
96
- /**
97
- * Sort bars based on sortBy and order configuration
98
- */
99
- export const sortBars = ({
100
- bars,
101
- sortBy,
102
- order
103
- }) => {
104
- if (sortBy === 'none' || !sortBy) {
105
- return bars;
106
- }
107
- const sorted = [...bars].sort((a, b) => {
108
- if (sortBy === 'label') {
109
- return a.yLabel.localeCompare(b.yLabel);
110
- }
111
- // sortBy === 'value' - use seriesTwoValue if available, otherwise seriesOneValue
112
- const aValue = a.seriesTwoValue ?? a.seriesOneValue;
113
- const bValue = b.seriesTwoValue ?? b.seriesOneValue;
114
- return aValue - bValue;
115
- });
116
- return order === 'descending' ? sorted.reverse() : sorted;
117
- };
118
-
119
- /**
120
- * Generates an accessible summary of the bar values
121
- */
122
- export const getValuesSummary = ({
123
- isInteractive,
124
- seriesOneValue,
125
- seriesTwoValue,
126
- unit,
127
- yLabel,
128
- translations
129
- }) => {
130
- const unitText = unit ? ` ${unit}` : '';
131
- if (seriesTwoValue !== undefined) {
132
- const gained = seriesOneValue;
133
- return `${gained}${unitText} ${translations.accessibility.gainedNowAt} ${seriesTwoValue}${unitText} ${translations.accessibility.inLabel} ${yLabel}`;
134
- }
135
- return isInteractive ? `${seriesOneValue}${unitText} ${translations.accessibility.inLabel} ${yLabel}` : `${seriesOneValue}${unitText} ${translations.accessibility.inOnly}`;
136
- };
137
-
138
- /**
139
- * Calculates the value for a given label position
140
- */
141
- export const getLabel = ({
142
- labelCount,
143
- labelIndex,
144
- min,
145
- max
146
- }) => {
147
- if (labelCount <= 1) return max;
148
- const incrementalDecimal = labelIndex / (labelCount - 1);
149
- return Math.floor(min + incrementalDecimal * (max - min));
150
- };
151
-
152
- /**
153
- * Calculates the percentage position for a given value within a range
154
- * Returns a value between 0 and 100 representing the position percentage
155
- */
156
- export const calculatePositionPercent = ({
157
- value,
158
- min,
159
- max
160
- }) => {
161
- if (max === min) return 0;
162
- const range = max - min;
163
- const adjustedValue = value - min;
164
- return adjustedValue / range * 100;
165
- };