@up42/up-components 9.1.0 → 9.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.
package/dist/esm/index.js CHANGED
@@ -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
  };
@@ -12049,22 +12058,39 @@ const DEFAULT_MARK_VALUES = [0, 2.5, 10, 30];
12049
12058
  * Hook for GSD (Ground Sampling Distance) range slider with logarithmic scale.
12050
12059
  * Handles conversion between linear slider values and logarithmic GSD values.
12051
12060
  */
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]);
12061
+ function useGsdSlider({ minValue: minValueProp, maxValue: maxValueProp, markValues: markValuesProp = DEFAULT_MARK_VALUES, onChange, }) {
12062
+ const hasMarks = markValuesProp.length > 0;
12063
+ // If there are marks, use the prop mark values, otherwise use the default mark values
12064
+ const markValues = hasMarks ? markValuesProp : DEFAULT_MARK_VALUES;
12065
+ const minMarkValue = Math.min(...markValues);
12066
+ const maxMarkValue = Math.max(...markValues);
12067
+ // If there are marks, use the min and max mark values, otherwise use undefined
12068
+ const minGsd = hasMarks ? minMarkValue : undefined;
12069
+ const maxGsd = hasMarks ? maxMarkValue : undefined;
12070
+ // If the min or max value is not provided, use the min and max mark values
12071
+ const minSliderValue = minValueProp ?? minMarkValue;
12072
+ const maxSliderValue = maxValueProp ?? maxMarkValue;
12073
+ // Get the slider value from the min and max slider values
12074
+ const sliderValue = useMemo(() => [
12075
+ getSliderValueFromGsd(minSliderValue, minMarkValue, maxMarkValue),
12076
+ getSliderValueFromGsd(maxSliderValue, minMarkValue, maxMarkValue),
12077
+ ], [minSliderValue, maxSliderValue, minMarkValue, maxMarkValue]);
12078
+ const marks = useMemo(() => {
12079
+ if (!hasMarks)
12080
+ return [];
12081
+ return markValuesProp.map((mark) => ({
12082
+ value: getSliderValueFromGsd(mark, minMarkValue, maxMarkValue),
12083
+ label: `${mark} m`,
12084
+ }));
12085
+ }, [hasMarks, markValuesProp, minMarkValue, maxMarkValue]);
12060
12086
  const handleSliderChange = (_event, newValue) => {
12061
12087
  if (newValue === undefined)
12062
12088
  return;
12063
12089
  const values = Array.isArray(newValue) ? newValue : [newValue, newValue];
12064
- const [min, max] = values.map((v) => getGsdFromSliderValue(v, minGsd, maxGsd));
12090
+ const [min, max] = values.map((v) => getGsdFromSliderValue(v, minMarkValue, maxMarkValue));
12065
12091
  onChange(min, max);
12066
12092
  };
12067
- const scale = (n) => getGsdFromSliderValue(n, minGsd, maxGsd);
12093
+ const scale = (n) => getGsdFromSliderValue(n, minMarkValue, maxMarkValue);
12068
12094
  return {
12069
12095
  sliderValue,
12070
12096
  marks,
@@ -12195,4 +12221,234 @@ function useUrlParams(options = {}) {
12195
12221
  };
12196
12222
  }
12197
12223
 
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 };
12224
+ /** Parse a comma-separated string into an array of strings. */
12225
+ function parseCommaSeparatedParam(value) {
12226
+ if (value === '') {
12227
+ return [];
12228
+ }
12229
+ const segments = value.split(',');
12230
+ const trimmedNonEmpty = segments.map((segment) => segment.trim()).filter(Boolean);
12231
+ return trimmedNonEmpty;
12232
+ }
12233
+ /** Get the default value for a Yup field. */
12234
+ function getDefaultValueForYupField(fieldDefinition) {
12235
+ const isYupSchema = isSchema(fieldDefinition);
12236
+ if (!isYupSchema) {
12237
+ return undefined;
12238
+ }
12239
+ const yupField = fieldDefinition;
12240
+ return yupField.getDefault();
12241
+ }
12242
+ /** Get the default values for a schema (`.default()` on each field). */
12243
+ function getDefaultValues(schema) {
12244
+ const objectSchema = schema;
12245
+ const { fields } = objectSchema;
12246
+ const defaultEntries = Object.keys(fields).map((fieldKey) => {
12247
+ const defaultForField = getDefaultValueForYupField(fields[fieldKey]);
12248
+ return [fieldKey, defaultForField];
12249
+ });
12250
+ const defaults = Object.fromEntries(defaultEntries);
12251
+ const castOptions = { stripUnknown: true };
12252
+ return schema.cast(defaults, castOptions);
12253
+ }
12254
+ /** Check if a field is an array and the value is a string. */
12255
+ function isArrayFieldWithStringFromUrl(fieldDefinition, paramValue) {
12256
+ const isYupSchema = isSchema(fieldDefinition);
12257
+ if (!isYupSchema || fieldDefinition.type !== 'array') {
12258
+ return false;
12259
+ }
12260
+ const valueIsString = typeof paramValue === 'string';
12261
+ return valueIsString;
12262
+ }
12263
+ /** Get the URL parameter name for a field. */
12264
+ function urlParamNameForField(fieldKey, urlKeyMap) {
12265
+ const mappedName = urlKeyMap?.[fieldKey];
12266
+ if (mappedName === undefined) {
12267
+ return fieldKey;
12268
+ }
12269
+ return mappedName;
12270
+ }
12271
+ /** Build a raw record of URL parameters from a schema and URL parameters. */
12272
+ function buildRawParamsFromUrl(schema, urlParams, urlKeyMap) {
12273
+ const objectSchema = schema;
12274
+ const { fields } = objectSchema;
12275
+ const presentFieldEntries = Object.keys(fields)
12276
+ .map((fieldKey) => {
12277
+ const searchParamKey = urlParamNameForField(fieldKey, urlKeyMap);
12278
+ const paramValue = urlParams[searchParamKey];
12279
+ if (paramValue === undefined) {
12280
+ return null;
12281
+ }
12282
+ const fieldDefinition = fields[fieldKey];
12283
+ const shouldSplitCommaSeparatedString = isArrayFieldWithStringFromUrl(fieldDefinition, paramValue);
12284
+ if (shouldSplitCommaSeparatedString) {
12285
+ const stringFromQuery = paramValue;
12286
+ const parsedList = parseCommaSeparatedParam(stringFromQuery);
12287
+ return [fieldKey, parsedList];
12288
+ }
12289
+ return [fieldKey, paramValue];
12290
+ })
12291
+ .filter((entry) => entry !== null);
12292
+ return Object.fromEntries(presentFieldEntries);
12293
+ }
12294
+ /** Validate a raw record of URL parameters against a schema. */
12295
+ function validateRawAgainstSchema(schema, raw) {
12296
+ const castOptions = { stripUnknown: true };
12297
+ try {
12298
+ const castedSchema = schema.cast(raw, castOptions);
12299
+ return schema.validateSync(castedSchema, castOptions);
12300
+ }
12301
+ catch {
12302
+ return null;
12303
+ }
12304
+ }
12305
+ /** Parse URL params into a validated filter object using the schema and optional URL key map. */
12306
+ function parseFromUrl(schema, urlParams, urlKeyMap) {
12307
+ const raw = buildRawParamsFromUrl(schema, urlParams, urlKeyMap);
12308
+ const validated = validateRawAgainstSchema(schema, raw);
12309
+ // If the validation failed, return the default values
12310
+ if (validated === null) {
12311
+ return getDefaultValues(schema);
12312
+ }
12313
+ return validated;
12314
+ }
12315
+ /** Check if two arrays of filter values are equal. */
12316
+ function arrayFilterValuesEqual(left, right) {
12317
+ const { length: leftLength } = left;
12318
+ const { length: rightLength } = right;
12319
+ if (leftLength !== rightLength) {
12320
+ return false;
12321
+ }
12322
+ return left.every((leftItem, index) => {
12323
+ const rightItem = right[index];
12324
+ return sameFilterValue(leftItem, rightItem);
12325
+ });
12326
+ }
12327
+ /** Check if two plain record of filter values are equal. */
12328
+ function plainRecordFilterValuesEqual(leftRecord, rightRecord) {
12329
+ const keySet = new Set([...Object.keys(leftRecord), ...Object.keys(rightRecord)]);
12330
+ const allKeys = [...keySet];
12331
+ return allKeys.every((key) => {
12332
+ const leftValue = leftRecord[key];
12333
+ const rightValue = rightRecord[key];
12334
+ return sameFilterValue(leftValue, rightValue);
12335
+ });
12336
+ }
12337
+ /** Deep value equality for filter field values (primitives, arrays, plain objects, Date). */
12338
+ function sameFilterValue(left, right) {
12339
+ // Check if the values are the same object
12340
+ if (Object.is(left, right)) {
12341
+ return true;
12342
+ }
12343
+ // Check if the values are null or undefined
12344
+ const leftIsNullish = left == null;
12345
+ const rightIsNullish = right == null;
12346
+ if (leftIsNullish || rightIsNullish) {
12347
+ const bothNullish = leftIsNullish && rightIsNullish;
12348
+ return bothNullish;
12349
+ }
12350
+ // Check if the values are arrays
12351
+ const leftIsArray = Array.isArray(left);
12352
+ const rightIsArray = Array.isArray(right);
12353
+ const bothAreArrays = leftIsArray && rightIsArray;
12354
+ if (bothAreArrays) {
12355
+ return arrayFilterValuesEqual(left, right);
12356
+ }
12357
+ // Check if the values are dates
12358
+ const leftIsDate = left instanceof Date;
12359
+ const rightIsDate = right instanceof Date;
12360
+ const bothAreDates = leftIsDate && rightIsDate;
12361
+ if (bothAreDates) {
12362
+ const leftTime = left.getTime();
12363
+ const rightTime = right.getTime();
12364
+ return leftTime === rightTime;
12365
+ }
12366
+ // Check if the values are objects
12367
+ const leftIsObject = typeof left === 'object';
12368
+ const rightIsObject = typeof right === 'object';
12369
+ const bothAreObjects = leftIsObject && rightIsObject;
12370
+ if (!bothAreObjects) {
12371
+ return false;
12372
+ }
12373
+ // Check if the values are plain records
12374
+ const leftRecord = left;
12375
+ const rightRecord = right;
12376
+ return plainRecordFilterValuesEqual(leftRecord, rightRecord);
12377
+ }
12378
+ /**
12379
+ * True when `draft` and `applied` match on every key in `schema`.
12380
+ * Unrelated URL search params do not appear on these snapshots, so this stays accurate when other code adds params.
12381
+ */
12382
+ function areFilterSnapshotsEqual(schema, draft, applied) {
12383
+ const objectSchema = schema;
12384
+ const { fields } = objectSchema;
12385
+ const schemaFieldKeys = Object.keys(fields);
12386
+ return schemaFieldKeys.every((fieldKey) => {
12387
+ const filterKey = fieldKey;
12388
+ const draftValue = draft[filterKey];
12389
+ const appliedValue = applied[filterKey];
12390
+ return sameFilterValue(draftValue, appliedValue);
12391
+ });
12392
+ }
12393
+
12394
+ /**
12395
+ * Draft vs applied filter state: the UI edits `filters` (draft); `apply` writes to the URL via {@link useUrlParams}.
12396
+ * Applied filters are always derived from the current search string and validated with Yup.
12397
+ *
12398
+ * When the query string changes, the draft syncs from the URL only if **schema-defined** filter values changed.
12399
+ * Extra params from other features do not reset the draft or affect **`isDirty`** by themselves.
12400
+ */
12401
+ function useFilterState(schema, options = {}) {
12402
+ const { urlKeyMap } = options;
12403
+ const { setParams, stringParams } = useUrlParams();
12404
+ const defaults = useMemo(() => getDefaultValues(schema), [schema]);
12405
+ const appliedFilters = useMemo(() => {
12406
+ const raw = searchParamsToObject(new URLSearchParams(stringParams));
12407
+ return parseFromUrl(schema, raw, urlKeyMap);
12408
+ }, [schema, stringParams, urlKeyMap]);
12409
+ const [draft, setDraft] = useState(() => parseFromUrl(schema, searchParamsToObject(new URLSearchParams(stringParams)), urlKeyMap));
12410
+ const draftRef = useRef(draft);
12411
+ draftRef.current = draft;
12412
+ const prevAppliedRef = useRef(null);
12413
+ useEffect(() => {
12414
+ const prevApplied = prevAppliedRef.current;
12415
+ if (prevApplied !== null && areFilterSnapshotsEqual(schema, prevApplied, appliedFilters)) {
12416
+ prevAppliedRef.current = appliedFilters;
12417
+ return;
12418
+ }
12419
+ prevAppliedRef.current = appliedFilters;
12420
+ draftRef.current = appliedFilters;
12421
+ setDraft(appliedFilters);
12422
+ }, [appliedFilters, schema]);
12423
+ const setFilter = useCallback((key, value) => {
12424
+ const next = { ...draftRef.current, [key]: value };
12425
+ draftRef.current = next;
12426
+ setDraft(next);
12427
+ }, []);
12428
+ const apply = useCallback(() => {
12429
+ const rawParams = searchParamsToObject(new URLSearchParams(stringParams));
12430
+ const nextParams = { ...rawParams };
12431
+ Object.keys(schema.fields).map((fieldKey) => {
12432
+ const mappedUrlKey = urlKeyMap?.[fieldKey];
12433
+ const urlKey = mappedUrlKey ?? fieldKey;
12434
+ const fieldValue = draftRef.current[fieldKey];
12435
+ nextParams[urlKey] = fieldValue;
12436
+ });
12437
+ setParams(nextParams);
12438
+ }, [schema, setParams, stringParams, urlKeyMap]);
12439
+ const reset = useCallback(() => {
12440
+ draftRef.current = defaults;
12441
+ setDraft(defaults);
12442
+ }, [defaults]);
12443
+ const isDirty = useMemo(() => !areFilterSnapshotsEqual(schema, draft, appliedFilters), [schema, draft, appliedFilters]);
12444
+ return {
12445
+ filters: draft,
12446
+ setFilter,
12447
+ apply,
12448
+ reset,
12449
+ isDirty,
12450
+ defaults,
12451
+ };
12452
+ }
12453
+
12454
+ 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.0",
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",