@up42/up-components 9.1.0 → 9.2.1

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/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Fade, Slide, Grow, createTheme, SvgIcon, Alert as Alert$1, AlertTitle, capitalize as capitalize$1, Box, Snackbar, CssBaseline, CircularProgress, Avatar as Avatar$1, Grid, Container, Checkbox as Checkbox$1, FormLabel as FormLabel$1, FormControl, FormGroup, FormControlLabel, FormHelperText, InputAdornment, IconButton, Button as Button$1, Radio as Radio$1, RadioGroup, Switch as Switch$1, Select as Select$1, MenuItem, TextField, Slider as Slider$1, Link as Link$1, Tab as Tab$1, Tabs as Tabs$1, Divider as Divider$1, Card, CardContent, Modal, Tooltip, Popover as Popover$1, Chip, Autocomplete, Typography as Typography$1, Stack, Badge as Badge$1, Menu, styled, Dialog, alpha } from '@mui/material';
1
+ import { Fade, Slide, Grow, createTheme, SvgIcon, Alert as Alert$1, AlertTitle, capitalize as capitalize$1, Box, Snackbar, CssBaseline, CircularProgress, Avatar as Avatar$1, Grid, Container, Checkbox as Checkbox$1, FormLabel as FormLabel$1, FormControl, FormGroup, FormControlLabel, FormHelperText, InputAdornment, IconButton, Button as Button$1, Radio as Radio$1, RadioGroup, Switch as Switch$1, Select as Select$1, MenuItem, TextField, Slider as Slider$1, Link as Link$1, Tab as Tab$1, Tabs as Tabs$1, Divider as Divider$1, Card, CardContent, Modal, Tooltip, Popover as Popover$1, Chip, Autocomplete, Typography as Typography$1, Stack, Badge as Badge$1, alpha, Menu, styled, Dialog } from '@mui/material';
2
2
  export * from '@mui/material';
3
3
  import { ThemeProvider } from '@mui/material/styles';
4
4
  import * as React from 'react';
@@ -27,6 +27,7 @@ import MuiTablePagination$1 from '@mui/material/TablePagination';
27
27
  import MuiTableSortLabel from '@mui/material/TableSortLabel';
28
28
  import MuiTableFooter$1 from '@mui/material/TableFooter';
29
29
  import { useSearchParams, useNavigate } from 'react-router';
30
+ import { isSchema } from 'yup';
30
31
 
31
32
  var color$2 = {
32
33
  gray0: "#FFFFFF",
@@ -6909,6 +6910,14 @@ const BasePopover = ({ open, onClose, anchorEl, header, children, footerInfo, pr
6909
6910
  e.stopPropagation();
6910
6911
  primaryButton?.onClick?.(e).then(handleClose);
6911
6912
  };
6913
+ const handleSecondaryButtonClick = (e) => {
6914
+ e.stopPropagation();
6915
+ if (secondaryButton?.onClick) {
6916
+ void Promise.resolve(secondaryButton.onClick(e));
6917
+ return;
6918
+ }
6919
+ handleClose();
6920
+ };
6912
6921
  const handleClose = () => {
6913
6922
  onClose?.();
6914
6923
  setPopoverAnchorEl(null);
@@ -6983,7 +6992,7 @@ const BasePopover = ({ open, onClose, anchorEl, header, children, footerInfo, pr
6983
6992
  } },
6984
6993
  footerInfo && footerInfo,
6985
6994
  React__default.createElement(Box, { sx: { display: 'flex', gap: tokens.size.spacing.scale8, marginLeft: 'auto' } },
6986
- secondaryButton && (React__default.createElement(Button, { variant: "outlined", size: "slim", ...secondaryButton, onClick: handleClose }, secondaryButton.label)),
6995
+ secondaryButton && (React__default.createElement(Button, { variant: "outlined", size: "slim", ...secondaryButton, onClick: handleSecondaryButtonClick }, secondaryButton.label)),
6987
6996
  primaryButton && (React__default.createElement(Button, { variant: "contained", size: "slim", ...primaryButton, onClick: handlePrimaryButtonClick, ...(primaryButton.startIcon && { startIcon: React__default.createElement(Icon, { name: primaryButton.startIcon }) }) }, primaryButton.label)))))),
6988
6997
  children))));
6989
6998
  };
@@ -7737,6 +7746,7 @@ const FeatureCardHeader = ({ header, subheader, checkboxProps, imageProps }) =>
7737
7746
  width: tokens.size.spacing.scale48,
7738
7747
  height: tokens.size.spacing.scale48,
7739
7748
  borderRadius: tokens.shape.border.radius4,
7749
+ border: `${tokens.shape.border.width1} solid ${alpha(tokens.color.gray900, 0.1)}`,
7740
7750
  backgroundColor: tokens.color.gray50,
7741
7751
  display: 'flex',
7742
7752
  alignItems: 'center',
@@ -12049,22 +12059,39 @@ const DEFAULT_MARK_VALUES = [0, 2.5, 10, 30];
12049
12059
  * Hook for GSD (Ground Sampling Distance) range slider with logarithmic scale.
12050
12060
  * Handles conversion between linear slider values and logarithmic GSD values.
12051
12061
  */
12052
- function useGsdSlider({ minValue, maxValue, onChange, markValues = DEFAULT_MARK_VALUES }) {
12053
- const minGsd = markValues.length > 0 ? Math.min(...markValues) : DEFAULT_MARK_VALUES[0];
12054
- const maxGsd = markValues.length > 0 ? Math.max(...markValues) : DEFAULT_MARK_VALUES[DEFAULT_MARK_VALUES.length - 1];
12055
- const sliderValue = useMemo(() => [getSliderValueFromGsd(minValue, minGsd, maxGsd), getSliderValueFromGsd(maxValue, minGsd, maxGsd)], [minValue, maxValue, minGsd, maxGsd]);
12056
- const marks = useMemo(() => markValues.map((mark) => ({
12057
- value: getSliderValueFromGsd(mark, minGsd, maxGsd),
12058
- label: `${mark} m`,
12059
- })), [markValues, minGsd, maxGsd]);
12062
+ function useGsdSlider({ minValue: minValueProp, maxValue: maxValueProp, markValues: markValuesProp = DEFAULT_MARK_VALUES, onChange, }) {
12063
+ const hasMarks = markValuesProp.length > 0;
12064
+ // If there are marks, use the prop mark values, otherwise use the default mark values
12065
+ const markValues = hasMarks ? markValuesProp : DEFAULT_MARK_VALUES;
12066
+ const minMarkValue = Math.min(...markValues);
12067
+ const maxMarkValue = Math.max(...markValues);
12068
+ // If there are marks, use the min and max mark values, otherwise use undefined
12069
+ const minGsd = hasMarks ? minMarkValue : undefined;
12070
+ const maxGsd = hasMarks ? maxMarkValue : undefined;
12071
+ // If the min or max value is not provided, use the min and max mark values
12072
+ const minSliderValue = minValueProp ?? minMarkValue;
12073
+ const maxSliderValue = maxValueProp ?? maxMarkValue;
12074
+ // Get the slider value from the min and max slider values
12075
+ const sliderValue = useMemo(() => [
12076
+ getSliderValueFromGsd(minSliderValue, minMarkValue, maxMarkValue),
12077
+ getSliderValueFromGsd(maxSliderValue, minMarkValue, maxMarkValue),
12078
+ ], [minSliderValue, maxSliderValue, minMarkValue, maxMarkValue]);
12079
+ const marks = useMemo(() => {
12080
+ if (!hasMarks)
12081
+ return [];
12082
+ return markValuesProp.map((mark) => ({
12083
+ value: getSliderValueFromGsd(mark, minMarkValue, maxMarkValue),
12084
+ label: `${mark} m`,
12085
+ }));
12086
+ }, [hasMarks, markValuesProp, minMarkValue, maxMarkValue]);
12060
12087
  const handleSliderChange = (_event, newValue) => {
12061
12088
  if (newValue === undefined)
12062
12089
  return;
12063
12090
  const values = Array.isArray(newValue) ? newValue : [newValue, newValue];
12064
- const [min, max] = values.map((v) => getGsdFromSliderValue(v, minGsd, maxGsd));
12091
+ const [min, max] = values.map((v) => getGsdFromSliderValue(v, minMarkValue, maxMarkValue));
12065
12092
  onChange(min, max);
12066
12093
  };
12067
- const scale = (n) => getGsdFromSliderValue(n, minGsd, maxGsd);
12094
+ const scale = (n) => getGsdFromSliderValue(n, minMarkValue, maxMarkValue);
12068
12095
  return {
12069
12096
  sliderValue,
12070
12097
  marks,
@@ -12195,4 +12222,234 @@ function useUrlParams(options = {}) {
12195
12222
  };
12196
12223
  }
12197
12224
 
12198
- export { ActionToolbar, Alert, Avatar, Badge, Banner, Button, Checkbox, CodeBlock, CodeInline, CodeSnippet, ContactBox, ControlButton, CopyButton, DataGrid, DateTime, Divider, DocumentationPopover, EditTagsButton, EmptyState, FeatureCard, FeatureCardHeader, FeatureCardHeaderActions, FeatureFlagCheckbox, FilterPopover, FindUsersButton, FormAutocomplete, FormCheckbox, FormDatePicker, FormDateRangePicker, FormDateRangePickerList, FormDateTimePicker, FormInput, FormRadio, FormSelect, FormSlider, FormSwitch, GridContainer, GridItem, Icon, Illustration, InfoCard, InfoModal, InfoPopover, Input, LearnMoreButton, Link, Loading, Logo, NotFound, PageContainer, PageHeader, PageHeaderV2, Popover, Radio, RoleBanner, Select, Slider, StatusLight, Switch, Tab, TabGroup, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TablePagination, TableRow, TableSortLabel, Tabs, Tag, TagsList, ToggleButton, Typography, UpComponentsProvider, analytics, capitalize, copyToClipboard, formatDate, formatFileSize, formatNumber, generateQueryKey, objectToSearchParams, searchParamsToObject, theme, tokens, useAlert, useCursorPagination, useDebounce, useGsdSlider, useQueryParams, useRemotePagination, useToggle, useUrlParams, valueToString };
12225
+ /** Parse a comma-separated string into an array of strings. */
12226
+ function parseCommaSeparatedParam(value) {
12227
+ if (value === '') {
12228
+ return [];
12229
+ }
12230
+ const segments = value.split(',');
12231
+ const trimmedNonEmpty = segments.map((segment) => segment.trim()).filter(Boolean);
12232
+ return trimmedNonEmpty;
12233
+ }
12234
+ /** Get the default value for a Yup field. */
12235
+ function getDefaultValueForYupField(fieldDefinition) {
12236
+ const isYupSchema = isSchema(fieldDefinition);
12237
+ if (!isYupSchema) {
12238
+ return undefined;
12239
+ }
12240
+ const yupField = fieldDefinition;
12241
+ return yupField.getDefault();
12242
+ }
12243
+ /** Get the default values for a schema (`.default()` on each field). */
12244
+ function getDefaultValues(schema) {
12245
+ const objectSchema = schema;
12246
+ const { fields } = objectSchema;
12247
+ const defaultEntries = Object.keys(fields).map((fieldKey) => {
12248
+ const defaultForField = getDefaultValueForYupField(fields[fieldKey]);
12249
+ return [fieldKey, defaultForField];
12250
+ });
12251
+ const defaults = Object.fromEntries(defaultEntries);
12252
+ const castOptions = { stripUnknown: true };
12253
+ return schema.cast(defaults, castOptions);
12254
+ }
12255
+ /** Check if a field is an array and the value is a string. */
12256
+ function isArrayFieldWithStringFromUrl(fieldDefinition, paramValue) {
12257
+ const isYupSchema = isSchema(fieldDefinition);
12258
+ if (!isYupSchema || fieldDefinition.type !== 'array') {
12259
+ return false;
12260
+ }
12261
+ const valueIsString = typeof paramValue === 'string';
12262
+ return valueIsString;
12263
+ }
12264
+ /** Get the URL parameter name for a field. */
12265
+ function urlParamNameForField(fieldKey, urlKeyMap) {
12266
+ const mappedName = urlKeyMap?.[fieldKey];
12267
+ if (mappedName === undefined) {
12268
+ return fieldKey;
12269
+ }
12270
+ return mappedName;
12271
+ }
12272
+ /** Build a raw record of URL parameters from a schema and URL parameters. */
12273
+ function buildRawParamsFromUrl(schema, urlParams, urlKeyMap) {
12274
+ const objectSchema = schema;
12275
+ const { fields } = objectSchema;
12276
+ const presentFieldEntries = Object.keys(fields)
12277
+ .map((fieldKey) => {
12278
+ const searchParamKey = urlParamNameForField(fieldKey, urlKeyMap);
12279
+ const paramValue = urlParams[searchParamKey];
12280
+ if (paramValue === undefined) {
12281
+ return null;
12282
+ }
12283
+ const fieldDefinition = fields[fieldKey];
12284
+ const shouldSplitCommaSeparatedString = isArrayFieldWithStringFromUrl(fieldDefinition, paramValue);
12285
+ if (shouldSplitCommaSeparatedString) {
12286
+ const stringFromQuery = paramValue;
12287
+ const parsedList = parseCommaSeparatedParam(stringFromQuery);
12288
+ return [fieldKey, parsedList];
12289
+ }
12290
+ return [fieldKey, paramValue];
12291
+ })
12292
+ .filter((entry) => entry !== null);
12293
+ return Object.fromEntries(presentFieldEntries);
12294
+ }
12295
+ /** Validate a raw record of URL parameters against a schema. */
12296
+ function validateRawAgainstSchema(schema, raw) {
12297
+ const castOptions = { stripUnknown: true };
12298
+ try {
12299
+ const castedSchema = schema.cast(raw, castOptions);
12300
+ return schema.validateSync(castedSchema, castOptions);
12301
+ }
12302
+ catch {
12303
+ return null;
12304
+ }
12305
+ }
12306
+ /** Parse URL params into a validated filter object using the schema and optional URL key map. */
12307
+ function parseFromUrl(schema, urlParams, urlKeyMap) {
12308
+ const raw = buildRawParamsFromUrl(schema, urlParams, urlKeyMap);
12309
+ const validated = validateRawAgainstSchema(schema, raw);
12310
+ // If the validation failed, return the default values
12311
+ if (validated === null) {
12312
+ return getDefaultValues(schema);
12313
+ }
12314
+ return validated;
12315
+ }
12316
+ /** Check if two arrays of filter values are equal. */
12317
+ function arrayFilterValuesEqual(left, right) {
12318
+ const { length: leftLength } = left;
12319
+ const { length: rightLength } = right;
12320
+ if (leftLength !== rightLength) {
12321
+ return false;
12322
+ }
12323
+ return left.every((leftItem, index) => {
12324
+ const rightItem = right[index];
12325
+ return sameFilterValue(leftItem, rightItem);
12326
+ });
12327
+ }
12328
+ /** Check if two plain record of filter values are equal. */
12329
+ function plainRecordFilterValuesEqual(leftRecord, rightRecord) {
12330
+ const keySet = new Set([...Object.keys(leftRecord), ...Object.keys(rightRecord)]);
12331
+ const allKeys = [...keySet];
12332
+ return allKeys.every((key) => {
12333
+ const leftValue = leftRecord[key];
12334
+ const rightValue = rightRecord[key];
12335
+ return sameFilterValue(leftValue, rightValue);
12336
+ });
12337
+ }
12338
+ /** Deep value equality for filter field values (primitives, arrays, plain objects, Date). */
12339
+ function sameFilterValue(left, right) {
12340
+ // Check if the values are the same object
12341
+ if (Object.is(left, right)) {
12342
+ return true;
12343
+ }
12344
+ // Check if the values are null or undefined
12345
+ const leftIsNullish = left == null;
12346
+ const rightIsNullish = right == null;
12347
+ if (leftIsNullish || rightIsNullish) {
12348
+ const bothNullish = leftIsNullish && rightIsNullish;
12349
+ return bothNullish;
12350
+ }
12351
+ // Check if the values are arrays
12352
+ const leftIsArray = Array.isArray(left);
12353
+ const rightIsArray = Array.isArray(right);
12354
+ const bothAreArrays = leftIsArray && rightIsArray;
12355
+ if (bothAreArrays) {
12356
+ return arrayFilterValuesEqual(left, right);
12357
+ }
12358
+ // Check if the values are dates
12359
+ const leftIsDate = left instanceof Date;
12360
+ const rightIsDate = right instanceof Date;
12361
+ const bothAreDates = leftIsDate && rightIsDate;
12362
+ if (bothAreDates) {
12363
+ const leftTime = left.getTime();
12364
+ const rightTime = right.getTime();
12365
+ return leftTime === rightTime;
12366
+ }
12367
+ // Check if the values are objects
12368
+ const leftIsObject = typeof left === 'object';
12369
+ const rightIsObject = typeof right === 'object';
12370
+ const bothAreObjects = leftIsObject && rightIsObject;
12371
+ if (!bothAreObjects) {
12372
+ return false;
12373
+ }
12374
+ // Check if the values are plain records
12375
+ const leftRecord = left;
12376
+ const rightRecord = right;
12377
+ return plainRecordFilterValuesEqual(leftRecord, rightRecord);
12378
+ }
12379
+ /**
12380
+ * True when `draft` and `applied` match on every key in `schema`.
12381
+ * Unrelated URL search params do not appear on these snapshots, so this stays accurate when other code adds params.
12382
+ */
12383
+ function areFilterSnapshotsEqual(schema, draft, applied) {
12384
+ const objectSchema = schema;
12385
+ const { fields } = objectSchema;
12386
+ const schemaFieldKeys = Object.keys(fields);
12387
+ return schemaFieldKeys.every((fieldKey) => {
12388
+ const filterKey = fieldKey;
12389
+ const draftValue = draft[filterKey];
12390
+ const appliedValue = applied[filterKey];
12391
+ return sameFilterValue(draftValue, appliedValue);
12392
+ });
12393
+ }
12394
+
12395
+ /**
12396
+ * Draft vs applied filter state: the UI edits `filters` (draft); `apply` writes to the URL via {@link useUrlParams}.
12397
+ * Applied filters are always derived from the current search string and validated with Yup.
12398
+ *
12399
+ * When the query string changes, the draft syncs from the URL only if **schema-defined** filter values changed.
12400
+ * Extra params from other features do not reset the draft or affect **`isDirty`** by themselves.
12401
+ */
12402
+ function useFilterState(schema, options = {}) {
12403
+ const { urlKeyMap } = options;
12404
+ const { setParams, stringParams } = useUrlParams();
12405
+ const defaults = useMemo(() => getDefaultValues(schema), [schema]);
12406
+ const appliedFilters = useMemo(() => {
12407
+ const raw = searchParamsToObject(new URLSearchParams(stringParams));
12408
+ return parseFromUrl(schema, raw, urlKeyMap);
12409
+ }, [schema, stringParams, urlKeyMap]);
12410
+ const [draft, setDraft] = useState(() => parseFromUrl(schema, searchParamsToObject(new URLSearchParams(stringParams)), urlKeyMap));
12411
+ const draftRef = useRef(draft);
12412
+ draftRef.current = draft;
12413
+ const prevAppliedRef = useRef(null);
12414
+ useEffect(() => {
12415
+ const prevApplied = prevAppliedRef.current;
12416
+ if (prevApplied !== null && areFilterSnapshotsEqual(schema, prevApplied, appliedFilters)) {
12417
+ prevAppliedRef.current = appliedFilters;
12418
+ return;
12419
+ }
12420
+ prevAppliedRef.current = appliedFilters;
12421
+ draftRef.current = appliedFilters;
12422
+ setDraft(appliedFilters);
12423
+ }, [appliedFilters, schema]);
12424
+ const setFilter = useCallback((key, value) => {
12425
+ const next = { ...draftRef.current, [key]: value };
12426
+ draftRef.current = next;
12427
+ setDraft(next);
12428
+ }, []);
12429
+ const apply = useCallback(() => {
12430
+ const rawParams = searchParamsToObject(new URLSearchParams(stringParams));
12431
+ const nextParams = { ...rawParams };
12432
+ Object.keys(schema.fields).map((fieldKey) => {
12433
+ const mappedUrlKey = urlKeyMap?.[fieldKey];
12434
+ const urlKey = mappedUrlKey ?? fieldKey;
12435
+ const fieldValue = draftRef.current[fieldKey];
12436
+ nextParams[urlKey] = fieldValue;
12437
+ });
12438
+ setParams(nextParams);
12439
+ }, [schema, setParams, stringParams, urlKeyMap]);
12440
+ const reset = useCallback(() => {
12441
+ draftRef.current = defaults;
12442
+ setDraft(defaults);
12443
+ }, [defaults]);
12444
+ const isDirty = useMemo(() => !areFilterSnapshotsEqual(schema, draft, appliedFilters), [schema, draft, appliedFilters]);
12445
+ return {
12446
+ filters: draft,
12447
+ setFilter,
12448
+ apply,
12449
+ reset,
12450
+ isDirty,
12451
+ defaults,
12452
+ };
12453
+ }
12454
+
12455
+ export { ActionToolbar, Alert, Avatar, Badge, Banner, Button, Checkbox, CodeBlock, CodeInline, CodeSnippet, ContactBox, ControlButton, CopyButton, DataGrid, DateTime, Divider, DocumentationPopover, EditTagsButton, EmptyState, FeatureCard, FeatureCardHeader, FeatureCardHeaderActions, FeatureFlagCheckbox, FilterPopover, FindUsersButton, FormAutocomplete, FormCheckbox, FormDatePicker, FormDateRangePicker, FormDateRangePickerList, FormDateTimePicker, FormInput, FormRadio, FormSelect, FormSlider, FormSwitch, GridContainer, GridItem, Icon, Illustration, InfoCard, InfoModal, InfoPopover, Input, LearnMoreButton, Link, Loading, Logo, NotFound, PageContainer, PageHeader, PageHeaderV2, Popover, Radio, RoleBanner, Select, Slider, StatusLight, Switch, Tab, TabGroup, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TablePagination, TableRow, TableSortLabel, Tabs, Tag, TagsList, ToggleButton, Typography, UpComponentsProvider, analytics, areFilterSnapshotsEqual, capitalize, copyToClipboard, formatDate, formatFileSize, formatNumber, generateQueryKey, objectToSearchParams, searchParamsToObject, theme, tokens, useAlert, useCursorPagination, useDebounce, useFilterState, useGsdSlider, useQueryParams, useRemotePagination, useToggle, useUrlParams, valueToString };
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ import { TableFooterProps as TableFooterProps$1 } from '@mui/material/TableFoote
23
23
  import { DataGridPremiumProps } from '@mui/x-data-grid-premium';
24
24
  export { GRID_DETAIL_PANEL_TOGGLE_COL_DEF, GRID_DETAIL_PANEL_TOGGLE_FIELD, GridCell, GridCellModes, GridCellModesModel, GridCellParams, GridColDef, GridColumnHeaderParams, GridEditCellProps, GridEditInputCell, GridFooter, GridInitialState, GridLoadingOverlay, GridNoRowsOverlay, GridPagination, GridPreProcessEditCellProps, GridRenderCellParams, GridRenderEditCellParams, GridRow, GridRowCount, GridRowId, GridRowParams, GridRowSelectionModel, GridRowsProp, GridSelectedRowCount, GridSortModel, GridTreeNodeWithRender, gridColumnDefinitionsSelector, gridColumnVisibilityModelSelector, gridDetailPanelExpandedRowIdsSelector, gridDetailPanelExpandedRowsContentCacheSelector, gridRowsLookupSelector, useGridApiContext, useGridApiRef, useGridSelector } from '@mui/x-data-grid-premium';
25
25
  import { ToggleButtonProps as ToggleButtonProps$1 } from '@mui/material/ToggleButton';
26
+ import { AnyObjectSchema, InferType } from 'yup';
26
27
 
27
28
  interface UpComponentsProviderProps extends Omit<ThemeProviderProps, 'theme'> {
28
29
  licenseKey: string;
@@ -4696,7 +4697,8 @@ type PopoverProps = Omit<PopoverProps$1, 'content' | 'open' | 'onClose'> & {
4696
4697
  */
4697
4698
  primaryButton?: PopoverButtonProps;
4698
4699
  /**
4699
- * Secondary button props (optional)
4700
+ * Secondary button props (optional).
4701
+ * If `onClick` is omitted, clicking the button only closes the popover. If `onClick` is set, it runs and the popover stays open unless you call `onClose`.
4700
4702
  */
4701
4703
  secondaryButton?: PopoverButtonProps;
4702
4704
  /**
@@ -11826,18 +11828,21 @@ declare const useDebounce: <T>(value: T, delay?: number) => T;
11826
11828
  type UseGsdSliderProps = {
11827
11829
  /**
11828
11830
  * Minimum GSD value (meters). Controlled by parent.
11831
+ * When omitted, the range thumb uses the axis minimum (lowest mark used for the logarithm).
11829
11832
  */
11830
- minValue: number;
11833
+ minValue?: number;
11831
11834
  /**
11832
11835
  * Maximum GSD value (meters). Controlled by parent.
11836
+ * When omitted, the range thumb uses the axis maximum (highest mark used for the logarithm).
11833
11837
  */
11834
- maxValue: number;
11838
+ maxValue?: number;
11835
11839
  /**
11836
11840
  * Callback when the range changes.
11837
11841
  */
11838
11842
  onChange: (minValue: number, maxValue: number) => void;
11839
11843
  /**
11840
11844
  * GSD values for the slider marks.
11845
+ * Pass an empty array if you want no marks and `minGsd` / `maxGsd` returned as `undefined` (axis still uses the default scale internally).
11841
11846
  * @default [0, 2.5, 10, 30]
11842
11847
  */
11843
11848
  markValues?: number[];
@@ -11846,7 +11851,7 @@ type UseGsdSliderProps = {
11846
11851
  * Hook for GSD (Ground Sampling Distance) range slider with logarithmic scale.
11847
11852
  * Handles conversion between linear slider values and logarithmic GSD values.
11848
11853
  */
11849
- declare function useGsdSlider({ minValue, maxValue, onChange, markValues }: UseGsdSliderProps): {
11854
+ declare function useGsdSlider({ minValue: minValueProp, maxValue: maxValueProp, markValues: markValuesProp, onChange, }: UseGsdSliderProps): {
11850
11855
  sliderValue: [number, number];
11851
11856
  marks: {
11852
11857
  value: number;
@@ -11854,8 +11859,8 @@ declare function useGsdSlider({ minValue, maxValue, onChange, markValues }: UseG
11854
11859
  }[];
11855
11860
  handleSliderChange: (_event: unknown, newValue?: number | number[]) => void;
11856
11861
  scale: (n: number) => number;
11857
- minGsd: number;
11858
- maxGsd: number;
11862
+ minGsd: number | undefined;
11863
+ maxGsd: number | undefined;
11859
11864
  };
11860
11865
 
11861
11866
  type CreateAlertProps = {
@@ -11963,5 +11968,32 @@ declare function valueToString(value: unknown): string;
11963
11968
  */
11964
11969
  declare function objectToSearchParams<T extends ParamsObject = ParamsObject>(object: T): string;
11965
11970
 
11966
- export { ActionToolbar, Alert, Avatar, Badge, Banner, Button, Checkbox, CodeBlock, CodeInline, CodeSnippet, ContactBox, ControlButton, CopyButton, DataGrid, DateTime, Divider, DocumentationPopover, EditTagsButton, EmptyState, FeatureCard, FeatureCardHeader, FeatureCardHeaderActions, FeatureFlagCheckbox, FilterPopover, FindUsersButton, FormAutocomplete, FormCheckbox, FormDatePicker, FormDateRangePicker, FormDateRangePickerList, FormDateTimePicker, FormInput, FormRadio, FormSelect, FormSlider, FormSwitch, GridContainer, GridItem, Icon, Illustration, InfoCard, InfoModal, InfoPopover, Input, LearnMoreButton, Link, Loading, Logo, NotFound, PageContainer, PageHeader, PageHeaderV2, Popover, Radio, RoleBanner, Select, Slider, StatusLight, Switch, Tab, TabGroup, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TablePagination, TableRow, TableSortLabel, Tabs, Tag, TagsList, ToggleButton, Typography, UpComponentsProvider, analytics, capitalize, copyToClipboard, formatDate, formatFileSize, formatNumber, generateQueryKey, objectToSearchParams, searchParamsToObject, theme, useAlert, useCursorPagination, useDebounce, useGsdSlider, useQueryParams, useRemotePagination, useToggle, useUrlParams, valueToString };
11967
- export type { ActionToolbarProps, AlertProps, AnalyticsConfig, AvatarProps, BadgeProps, BannerProps, ButtonProps, CheckboxProps, CodeBlockProps, CodeInlineProps, CodeSnippetItemProps, CodeSnippetProps, ContactBoxProps, ControlButtonProps, CopyButtonProps, CreateAlertProps, CreateSnackbarProps, CursorPaginatedResponse, DateRange, DateTimeProps, DividerProps, DocumentationPopoverProps, EditTagsButtonProps, EmptyStateProps, FeatureCardHeaderActionsProps, FeatureCardHeaderProps, FeatureCardProps, FeatureFlagCheckboxProps, FilterPopoverProps, FindUsersButtonProps, FormAutocompleteMapper, FormAutocompleteProps, FormCheckboxProps, FormDatePickerDateType, FormDatePickerProps, FormDateRangePickerListProps, FormDateRangePickerProps, FormDateTimePickerProps, FormInputProps, FormRadioProps, FormSelectProps, FormSliderProps, FormSwitchProps, GridContainerProps, GridItemProps, IconAction, IconProps$1 as IconProps, IllustrationProps, InfoCardProps, InfoModalProps, InfoPopoverProps, InputProps, LearnMoreButtonProps, LinkProps, LoadingProps, LogoProps, MenuAction, NotFoundProps, PageContainerProps, PageHeaderProps, PageHeaderV2Props, PaginatedResponse, ParamsObject, PopoverProps, RadioProps, RemoveParamRules, RoleBannerProps, SearchParamObject, SelectProps, SliderProps$1 as SliderProps, StatusLightProps, SwitchProps, TabGroupProps, TabProps, TableBodyProps, TableCellProps, TableContainerProps, TableFooterProps, TableHeadProps, TablePaginationProps, TableProps, TableRowProps, TableSortLabelProps, TabsProps, TagItem, TagProps, TagsListProps, ToggleButtonProps, TypographyProps, UseGsdSliderProps, UseToggleResult };
11971
+ /** Maps internal filter keys to URL search-param names. */
11972
+ type UrlKeyMap<FilterSchema extends AnyObjectSchema> = Partial<Record<keyof InferType<FilterSchema>, string>>;
11973
+ /**
11974
+ * True when `draft` and `applied` match on every key in `schema`.
11975
+ * Unrelated URL search params do not appear on these snapshots, so this stays accurate when other code adds params.
11976
+ */
11977
+ declare function areFilterSnapshotsEqual<FilterSchema extends AnyObjectSchema>(schema: FilterSchema, draft: InferType<FilterSchema>, applied: InferType<FilterSchema>): boolean;
11978
+
11979
+ type UseFilterStateOptions<FilterSchema extends AnyObjectSchema> = {
11980
+ urlKeyMap?: UrlKeyMap<FilterSchema>;
11981
+ };
11982
+ /**
11983
+ * Draft vs applied filter state: the UI edits `filters` (draft); `apply` writes to the URL via {@link useUrlParams}.
11984
+ * Applied filters are always derived from the current search string and validated with Yup.
11985
+ *
11986
+ * When the query string changes, the draft syncs from the URL only if **schema-defined** filter values changed.
11987
+ * Extra params from other features do not reset the draft or affect **`isDirty`** by themselves.
11988
+ */
11989
+ declare function useFilterState<FilterSchema extends AnyObjectSchema>(schema: FilterSchema, options?: UseFilterStateOptions<FilterSchema>): {
11990
+ readonly filters: InferType<FilterSchema>;
11991
+ readonly setFilter: <K extends keyof InferType<FilterSchema>>(key: K, value: InferType<FilterSchema>[K]) => void;
11992
+ readonly apply: () => void;
11993
+ readonly reset: () => void;
11994
+ readonly isDirty: boolean;
11995
+ readonly defaults: InferType<FilterSchema>;
11996
+ };
11997
+
11998
+ export { ActionToolbar, Alert, Avatar, Badge, Banner, Button, Checkbox, CodeBlock, CodeInline, CodeSnippet, ContactBox, ControlButton, CopyButton, DataGrid, DateTime, Divider, DocumentationPopover, EditTagsButton, EmptyState, FeatureCard, FeatureCardHeader, FeatureCardHeaderActions, FeatureFlagCheckbox, FilterPopover, FindUsersButton, FormAutocomplete, FormCheckbox, FormDatePicker, FormDateRangePicker, FormDateRangePickerList, FormDateTimePicker, FormInput, FormRadio, FormSelect, FormSlider, FormSwitch, GridContainer, GridItem, Icon, Illustration, InfoCard, InfoModal, InfoPopover, Input, LearnMoreButton, Link, Loading, Logo, NotFound, PageContainer, PageHeader, PageHeaderV2, Popover, Radio, RoleBanner, Select, Slider, StatusLight, Switch, Tab, TabGroup, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TablePagination, TableRow, TableSortLabel, Tabs, Tag, TagsList, ToggleButton, Typography, UpComponentsProvider, analytics, areFilterSnapshotsEqual, capitalize, copyToClipboard, formatDate, formatFileSize, formatNumber, generateQueryKey, objectToSearchParams, searchParamsToObject, theme, useAlert, useCursorPagination, useDebounce, useFilterState, useGsdSlider, useQueryParams, useRemotePagination, useToggle, useUrlParams, valueToString };
11999
+ export type { ActionToolbarProps, AlertProps, AnalyticsConfig, AvatarProps, BadgeProps, BannerProps, ButtonProps, CheckboxProps, CodeBlockProps, CodeInlineProps, CodeSnippetItemProps, CodeSnippetProps, ContactBoxProps, ControlButtonProps, CopyButtonProps, CreateAlertProps, CreateSnackbarProps, CursorPaginatedResponse, DateRange, DateTimeProps, DividerProps, DocumentationPopoverProps, EditTagsButtonProps, EmptyStateProps, FeatureCardHeaderActionsProps, FeatureCardHeaderProps, FeatureCardProps, FeatureFlagCheckboxProps, FilterPopoverProps, FindUsersButtonProps, FormAutocompleteMapper, FormAutocompleteProps, FormCheckboxProps, FormDatePickerDateType, FormDatePickerProps, FormDateRangePickerListProps, FormDateRangePickerProps, FormDateTimePickerProps, FormInputProps, FormRadioProps, FormSelectProps, FormSliderProps, FormSwitchProps, GridContainerProps, GridItemProps, IconAction, IconProps$1 as IconProps, IllustrationProps, InfoCardProps, InfoModalProps, InfoPopoverProps, InputProps, LearnMoreButtonProps, LinkProps, LoadingProps, LogoProps, MenuAction, NotFoundProps, PageContainerProps, PageHeaderProps, PageHeaderV2Props, PaginatedResponse, ParamsObject, PopoverProps, RadioProps, RemoveParamRules, RoleBannerProps, SearchParamObject, SelectProps, SliderProps$1 as SliderProps, StatusLightProps, SwitchProps, TabGroupProps, TabProps, TableBodyProps, TableCellProps, TableContainerProps, TableFooterProps, TableHeadProps, TablePaginationProps, TableProps, TableRowProps, TableSortLabelProps, TabsProps, TagItem, TagProps, TagsListProps, ToggleButtonProps, TypographyProps, UrlKeyMap, UseFilterStateOptions, UseGsdSliderProps, UseToggleResult };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@up42/up-components",
3
- "version": "9.1.0",
3
+ "version": "9.2.1",
4
4
  "description": "UP42 Component Library",
5
5
  "author": "Axel Fuhrmann axel.fuhrmann@up42.com",
6
6
  "license": "ISC",
@@ -81,6 +81,7 @@
81
81
  "lint-staged": "^12.3.1",
82
82
  "prettier": "^2.5.1",
83
83
  "react": "^18.3.1",
84
+ "remark-gfm": "^4.0.1",
84
85
  "rimraf": "^6.1.2",
85
86
  "rollup": "^4.59.0",
86
87
  "rollup-plugin-dts": "^6.1.0",
@@ -90,7 +91,8 @@
90
91
  "typescript": "^4.5.4",
91
92
  "vite": "^7.1.12",
92
93
  "vite-plugin-svgr": "^4.2.0",
93
- "vitest": "^3.2.4"
94
+ "vitest": "^3.2.4",
95
+ "yup": "^0.32.9"
94
96
  },
95
97
  "peerDependencies": {
96
98
  "@emotion/react": "^11.7.1",
@@ -101,7 +103,8 @@
101
103
  "dayjs": "^1.11.7",
102
104
  "react": "^18.3.1",
103
105
  "react-dom": "^18.3.1",
104
- "react-router": "^7.12.0"
106
+ "react-router": "^7.12.0",
107
+ "yup": "^0.32.9"
105
108
  },
106
109
  "lint-staged": {
107
110
  "*.{js,tsx,ts}": "eslint --cache --fix",