@uxf/ui 11.80.6 → 11.85.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.
@@ -1,6 +1,3 @@
1
1
  import React from "react";
2
- import { CheckboxListProps } from "./types";
3
- export declare const CheckboxList: {
4
- (props: CheckboxListProps): React.JSX.Element;
5
- displayName: string;
6
- };
2
+ import { CheckboxListProps, CheckboxListValueId } from "./types";
3
+ export declare const CheckboxList: React.ForwardRefExoticComponent<CheckboxListProps<CheckboxListValueId, import("./types").CheckboxListOption<CheckboxListValueId>> & React.RefAttributes<HTMLDivElement>>;
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ "use-client";
2
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
4
  if (k2 === undefined) k2 = k;
4
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -29,7 +30,7 @@ const cx_1 = require("@uxf/core/utils/cx");
29
30
  const react_1 = __importStar(require("react"));
30
31
  const checkbox_input_1 = require("../checkbox-input");
31
32
  const form_component_1 = require("../form-component");
32
- const CheckboxList = (props) => {
33
+ exports.CheckboxList = (0, react_1.forwardRef)((props, ref) => {
33
34
  const generatedId = (0, react_1.useId)();
34
35
  const id = props.id || generatedId;
35
36
  const errorId = props.isInvalid && props.id ? `${props.id}--errormessage` : undefined;
@@ -41,12 +42,11 @@ const CheckboxList = (props) => {
41
42
  props.onChange(newValue);
42
43
  };
43
44
  const rootClassName = (0, cx_1.cx)("uxf-checkbox-list", props.isDisabled && classes_1.CLASSES.IS_DISABLED, props.isInvalid && classes_1.CLASSES.IS_INVALID, props.isReadOnly && classes_1.CLASSES.IS_READONLY, props.isRequired && classes_1.CLASSES.IS_REQUIRED, props.className);
44
- return (react_1.default.createElement(form_component_1.FormComponent, { className: rootClassName, errorId: errorId, helperText: props.helperText, hiddenLabel: props.hasHiddenLabel, inputId: id, isRequired: props.isRequired, label: props.label, name: props.name },
45
+ return (react_1.default.createElement(form_component_1.FormComponent, { className: rootClassName, errorId: errorId, helperText: props.helperText, hiddenLabel: props.hasHiddenLabel, inputId: id, isRequired: props.isRequired, label: props.label, name: props.name, ref: ref },
45
46
  react_1.default.createElement("ul", { className: "uxf-checkbox-list__grid" }, props.options.map((option) => {
46
47
  var _a;
47
48
  return (react_1.default.createElement("li", { key: option.id },
48
49
  react_1.default.createElement(checkbox_input_1.CheckboxInput, { id: `${id}-${option.id}`, isDisabled: option.isDisabled || props.isDisabled, isInvalid: props.isInvalid, isReadOnly: props.isReadOnly, isRequired: props.isRequired, key: option.id, label: option.label, name: props.name, onBlur: props.onBlur, onChange: (value) => onChange(option.id, value || false), onFocus: props.onFocus, value: (_a = props.value) === null || _a === void 0 ? void 0 : _a.includes(option.id) })));
49
50
  }))));
50
- };
51
- exports.CheckboxList = CheckboxList;
51
+ });
52
52
  exports.CheckboxList.displayName = "UxfUiCheckboxList";
@@ -1,2 +1,2 @@
1
1
  export { CheckboxList } from "./checkbox-list";
2
- export type { CheckboxListProps } from "./types";
2
+ export type { CheckboxListOption, CheckboxListProps, CheckboxListValueId } from "./types";
@@ -7,7 +7,6 @@ export interface DialogProps {
7
7
  children?: ReactNode;
8
8
  className?: string;
9
9
  context: UseFloatingReturn["context"];
10
- isBackdropCloseDisabled?: boolean;
11
10
  forwardedRef: Ref<HTMLDivElement>;
12
11
  getFloatingProps: GetFloatingElementProps;
13
12
  isOpen: boolean;
@@ -17,8 +16,9 @@ type DialogProviderProps = Pick<DialogProps, "variant" | "className" | "children
17
16
  export declare const Dialog: React.NamedExoticComponent<DialogProps>;
18
17
  type DialogCloseHandler = () => void;
19
18
  export declare function useDialog(dialogConfig?: {
20
- onDialogClose?: DialogCloseHandler;
21
19
  isBackdropCloseDisabled?: boolean;
20
+ isEscapeKeyCloseDisabled?: boolean;
21
+ onDialogClose?: DialogCloseHandler;
22
22
  }): {
23
23
  openDialog: (children: ReactNode) => void;
24
24
  DialogProvider: FC<DialogProviderProps>;
package/dialog/dialog.js CHANGED
@@ -60,7 +60,11 @@ function useDialog(dialogConfig) {
60
60
  });
61
61
  const click = (0, react_1.useClick)(context);
62
62
  const role = (0, react_1.useRole)(context);
63
- const dismiss = (0, react_1.useDismiss)(context, { outsidePressEvent: "mousedown" });
63
+ const dismiss = (0, react_1.useDismiss)(context, {
64
+ escapeKey: !(dialogConfig === null || dialogConfig === void 0 ? void 0 : dialogConfig.isEscapeKeyCloseDisabled),
65
+ outsidePress: !(dialogConfig === null || dialogConfig === void 0 ? void 0 : dialogConfig.isBackdropCloseDisabled),
66
+ outsidePressEvent: "mousedown",
67
+ });
64
68
  const { getReferenceProps, getFloatingProps } = (0, react_1.useInteractions)([click, role, dismiss]);
65
69
  const latestContext = (0, use_latest_1.useLatest)(context);
66
70
  const DialogProvider = (0, react_2.useCallback)((config) => {
@@ -31,8 +31,9 @@ exports.ModalProvider = (0, react_1.forwardRef)((props, ref) => {
31
31
  var _a, _b;
32
32
  const [content, setContent] = (0, react_1.useState)();
33
33
  const { openDialog, DialogProvider, closeDialog } = (0, dialog_1.useDialog)({
34
- onDialogClose: content === null || content === void 0 ? void 0 : content.onClose,
35
34
  isBackdropCloseDisabled: content === null || content === void 0 ? void 0 : content.isBackdropCloseDisabled,
35
+ isEscapeKeyCloseDisabled: content === null || content === void 0 ? void 0 : content.isEscapeKeyCloseDisabled,
36
+ onDialogClose: content === null || content === void 0 ? void 0 : content.onClose,
36
37
  });
37
38
  const openModalHandler = (modal) => {
38
39
  openDialog(modal.children);
package/modal/modal.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ "use client";
2
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
5
  };
@@ -14,13 +15,17 @@ function Modal(props) {
14
15
  open: props.isOpen,
15
16
  onOpenChange(nextOpen, event, reason) {
16
17
  if (!props.isBackdropCloseDisabled && (reason === "escape-key" || reason === "outside-press")) {
17
- props.close();
18
+ props.onClose();
18
19
  }
19
20
  },
20
21
  });
21
22
  const click = (0, react_1.useClick)(context);
22
23
  const role = (0, react_1.useRole)(context);
23
- const dismiss = (0, react_1.useDismiss)(context, { outsidePressEvent: "mousedown", escapeKey: true });
24
+ const dismiss = (0, react_1.useDismiss)(context, {
25
+ escapeKey: !props.isEscKeyCloseDisabled,
26
+ outsidePress: !props.isBackdropCloseDisabled,
27
+ outsidePressEvent: "mousedown",
28
+ });
24
29
  const { getFloatingProps } = (0, react_1.useInteractions)([click, role, dismiss]);
25
30
  const latestContext = (0, use_latest_1.useLatest)(context);
26
31
  return (react_2.default.createElement(dialog_1.Dialog, { className: `uxf-modal uxf-modal--variant-${(_a = props.variant) !== null && _a !== void 0 ? _a : "default"} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}`, context: latestContext.current, forwardedRef: refs.setFloating, getFloatingProps: getFloatingProps, isOpen: props.isOpen, variant: props.variant }, props.children));
@@ -37,15 +37,15 @@ function Default() {
37
37
  return (react_1.default.createElement("div", { className: "space-y-2 p-20 lg:w-1/2" },
38
38
  react_1.default.createElement("div", null,
39
39
  react_1.default.createElement(button_1.Button, { onClick: () => setIsOpenXs(true) }, "Click to open modal xs"),
40
- react_1.default.createElement(modal_1.Modal, { close: () => setIsOpenXs(false), isOpen: isOpenXs },
40
+ react_1.default.createElement(modal_1.Modal, { isOpen: isOpenXs, onClose: () => setIsOpenXs(false) },
41
41
  react_1.default.createElement(dialog_1.DialogPanel, { width: "xs" }, "Whatever content inside Modal xs"))),
42
42
  react_1.default.createElement("div", null,
43
43
  react_1.default.createElement(button_1.Button, { onClick: () => setIsOpenRightDrawer(true) }, "Click to open modal drawer-right"),
44
- react_1.default.createElement(modal_1.Modal, { close: () => setIsOpenRightDrawer(false), isOpen: isOpenRightDrawer, variant: "drawer-right" },
44
+ react_1.default.createElement(modal_1.Modal, { isOpen: isOpenRightDrawer, onClose: () => setIsOpenRightDrawer(false), variant: "drawer-right" },
45
45
  react_1.default.createElement(dialog_1.DialogPanel, null, "Whatever content inside Modal drawer-right"))),
46
46
  react_1.default.createElement("div", null,
47
47
  react_1.default.createElement(button_1.Button, { onClick: () => setIsOpenDisableBackdrop(true) }, "Click to open modal DisableBackdrop"),
48
- react_1.default.createElement(modal_1.Modal, { close: () => setIsOpenDisableBackdrop(false), isBackdropCloseDisabled: true, isOpen: isOpenDisableBackdrop },
48
+ react_1.default.createElement(modal_1.Modal, { isBackdropCloseDisabled: true, isOpen: isOpenDisableBackdrop, onClose: () => setIsOpenDisableBackdrop(false) },
49
49
  react_1.default.createElement(dialog_1.DialogPanel, null,
50
50
  react_1.default.createElement("div", { className: "p-4" },
51
51
  react_1.default.createElement("p", null, "Whatever content inside DisableBackdrop:"),
package/modal/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Noop } from "@uxf/core/types";
1
2
  import { ModalVariants } from "@uxf/ui/modal/theme";
2
3
  import { ReactNode } from "react";
3
4
  import type { DialogProps } from "../dialog";
@@ -5,19 +6,21 @@ export type ModalVariant = keyof ModalVariants;
5
6
  export interface ModalProviderProps {
6
7
  children: ReactNode;
7
8
  className?: DialogProps["className"];
8
- isBackdropCloseDisabled?: DialogProps["isBackdropCloseDisabled"];
9
- onClose?: () => void;
9
+ isBackdropCloseDisabled?: boolean;
10
+ isEscapeKeyCloseDisabled?: boolean;
11
+ onClose?: Noop;
10
12
  variant?: ModalVariant;
11
13
  }
12
14
  export interface ModalRef {
13
- close: () => void;
15
+ close: Noop;
14
16
  open: (modal: ModalProviderProps) => void;
15
17
  }
16
18
  export interface ModalProps {
17
19
  children: ReactNode;
18
20
  className?: DialogProps["className"];
19
- close: () => void;
20
21
  isBackdropCloseDisabled?: boolean;
22
+ isEscKeyCloseDisabled?: boolean;
21
23
  isOpen: boolean;
24
+ onClose: Noop;
22
25
  variant?: ModalVariant;
23
26
  }
@@ -71,7 +71,7 @@ function Options(props) {
71
71
  return (react_3.default.createElement(react_2.Combobox.Options, { className: (0, cx_1.cx)("uxf-dropdown uxf-multi-combobox__dropdown", `uxf-dropdown--size-${(_a = props.size) !== null && _a !== void 0 ? _a : "default"}`, `uxf-dropdown--variant-${(_b = props.variant) !== null && _b !== void 0 ? _b : "default"}`, props.matchWidth ? "uxf-dropdown--match-width" : "uxf-dropdown--not-match-width", props.placement === "bottom" && "uxf-dropdown--bottom", props.placement === "top" && "uxf-dropdown--top", props.className), ref: props.forwardRef, static: true, style: props.style }, (0, is_not_empty_1.isNotEmpty)(props.options) ? (props.options.map((option) => {
72
72
  var _a, _b;
73
73
  const optionTyped = option;
74
- return (react_3.default.createElement(Option, { handleCheckboxChange: props.onCheckboxChange(optionTyped.id), key: (_b = (_a = props.keyExtractor) === null || _a === void 0 ? void 0 : _a.call(props, option)) !== null && _b !== void 0 ? _b : optionTyped.id, option: optionTyped, withCheckboxes: props.withCheckboxes }));
74
+ return (react_3.default.createElement(Option, { handleCheckboxChange: props.onCheckboxChange(optionTyped.id), key: (_b = (_a = props.keyExtractor) === null || _a === void 0 ? void 0 : _a.call(props, option)) !== null && _b !== void 0 ? _b : optionTyped.id, option: option, renderOption: props.renderOption, withCheckboxes: props.withCheckboxes }));
75
75
  })) : (react_3.default.createElement("div", { className: "uxf-dropdown__empty-wrapper" }, props.emptyMessage))));
76
76
  }
77
77
  Options.displayName = "UxfUiMultiComboboxOptions";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uxf/ui",
3
- "version": "11.80.6",
3
+ "version": "11.85.0",
4
4
  "description": "",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -11,8 +11,8 @@
11
11
  "css-tokens:gen": "bin/generate-css-tokens.js --source=./_tokens --output=./_tokens/generated/",
12
12
  "css:prepare": "rm -rf ./css && mkdir css && cp ./**/*.css ./css/ && cp ./_tokens/generated/figma-colors.css ./css/",
13
13
  "tokens:prepare": "rm -rf ./tokens && mkdir tokens && cp ./_tokens/generated/figma-colors.js ./tokens/",
14
- "tw-tokens:check": "ts-node ./scripts/generate-tw-tokens.js --mode=\"check\" --twConfig=\"./utils/tailwind-config.js\" --outputPath=\"./tw-tokens/\"",
15
- "tw-tokens:gen": "ts-node ./scripts/generate-tw-tokens.js --twConfig=\"./utils/tailwind-config.js\" --outputPath=\"./tw-tokens/\"",
14
+ "tw-tokens:check": "tsx ./scripts/generate-tw-tokens.js --mode=\"check\" --twConfig=\"./utils/tailwind-config.js\" --outputPath=\"./tw-tokens/\"",
15
+ "tw-tokens:gen": "tsx ./scripts/generate-tw-tokens.js --twConfig=\"./utils/tailwind-config.js\" --outputPath=\"./tw-tokens/\"",
16
16
  "typecheck": "../../node_modules/.bin/tsc --noEmit --skipLibCheck"
17
17
  },
18
18
  "author": "UX Fans s.r.o",
@@ -23,10 +23,10 @@
23
23
  "dependencies": {
24
24
  "@floating-ui/react": "0.26.28",
25
25
  "@headlessui/react": "1.7.19",
26
- "@uxf/core": "11.80.4",
27
- "@uxf/core-react": "11.80.4",
26
+ "@uxf/core": "11.85.0",
27
+ "@uxf/core-react": "11.85.0",
28
28
  "@uxf/datepicker": "11.80.4",
29
- "@uxf/styles": "11.80.4",
29
+ "@uxf/styles": "11.85.0",
30
30
  "color2k": "2.0.3",
31
31
  "dayjs": "1.11.13",
32
32
  "jump.js": "1.0.2",
@@ -38,6 +38,7 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/jump.js": "1.0.6",
41
+ "@types/node": "22",
41
42
  "@types/react": "18.3.5",
42
43
  "@types/react-dom": "18.3.0"
43
44
  }
@@ -1,7 +1,143 @@
1
1
  # RasterImage
2
2
 
3
- ## CSS dependencies
3
+ A React component for rendering raster images (JPG/PNG/WebP/AVIF, etc.) with optional responsive sources and graceful fallback when no image is available. SVG sources are detected automatically and rendered as-is. The component integrates with the UXF image resizer utilities to generate optimized images for different breakpoints.
4
+
5
+ ## Installation / CSS dependency
6
+
7
+ Add the package stylesheet to your global CSS if you haven't already:
4
8
 
5
9
  ```css
6
10
  @import url("@uxf/ui/raster-image/raster-image.css");
7
- ```
11
+ ```
12
+
13
+ ## When to use
14
+ - You need a drop-in `<img/`>-like component that works with UXF's resizer service.
15
+ - You want responsive images via `<picture/>` + `<source/>` with different widths/heights per breakpoint.
16
+ - You want consistent empty-state rendering when the image is missing.
17
+
18
+ ## Basic usage
19
+ ```tsx
20
+ import { RasterImage } from "@uxf/ui/raster-image";
21
+
22
+ export function CardThumb() {
23
+ return (
24
+ <RasterImage
25
+ alt="A beautiful landscape"
26
+ src="images/landscape.jpg"
27
+ width={400}
28
+ height={300}
29
+ loading="lazy"
30
+ />
31
+ );
32
+ }
33
+ ```
34
+
35
+ - Provide at least one of width or height. If you provide both, the exact dimensions will be requested from the resizer.
36
+ - If the provided source is an SVG, the component will render a simple `<img/>` with the SVG URL (no resizer involved).
37
+
38
+ ## Modes (object-fit helpers)
39
+ Use the mode prop to control fit behavior via CSS classes:
40
+
41
+ - mode="contain" → adds classes to make the image fit entirely inside the box without cropping.
42
+ - mode="cover" → adds classes to cover the box, possibly cropping.
43
+ - mode="responsive" → reserved for responsive layouts; adds a class you can target. Actual responsiveness is configured via widths/heights props below.
44
+
45
+ Example:
46
+ ```tsx
47
+ <RasterImage alt="Product" src="p/1.jpg" width={320} height={320} mode="cover" />
48
+ ```
49
+
50
+ ## Responsive images (sizes/srcset)
51
+ To provide multiple sources for different viewports, pass widths and/or heights arrays and optionally breakpoints. (Default breakpoints are passed from `UiContext`.) The component will render a `<picture/>` with `<source/>` tags via ResponsiveImgSources.
52
+
53
+ ```tsx
54
+ import { em } from "./em";
55
+
56
+ <RasterImage
57
+ alt="Hero"
58
+ src="images/hero.jpg"
59
+ // Desired element box in CSS might be responsive; request multiple candidate widths
60
+ widths={[360, 640, 960, 1280]}
61
+ // Optional heights if you need height-based variants
62
+ // heights={[180, 320, 480, 640]}
63
+ // Optional map of named breakpoints to CSS media queries (passed through to `<source media="..."/>`)
64
+ breakpoints={{
65
+ sm: em(576),
66
+ md: em(768),
67
+ lg: em(1024)
68
+ }}
69
+ // You can still provide width/height to define the fallback `<img/>` dimensions
70
+ width={960}
71
+ height={540}
72
+ />
73
+ ```
74
+
75
+ Notes:
76
+ - If a resizer URL can be constructed for the provided src, the component renders a `<picture/>` with responsive sources plus a fallback `<img/>`.
77
+ - If resizer cannot be used for the src, or it is an SVG, a simple `<img/>` is rendered.
78
+
79
+ ## Empty state
80
+ If src is null/undefined or cannot be resolved, the component renders an EmptyImage placeholder. You can customize its content:
81
+
82
+ ```tsx
83
+ <RasterImage
84
+ alt="Missing"
85
+ src={null}
86
+ width={200}
87
+ height={150}
88
+ noImageContent={<span>No image</span>}
89
+ />
90
+ ```
91
+
92
+ ## Props
93
+ ```ts
94
+ export type RasterImageProps = {
95
+ alt: string; // Required alt text for accessibility
96
+ breakpoints?: Record<string, string>; // Named breakpoints -> media queries used by `<source/>`
97
+ className?: string; // Wrapper `<picture/>` class
98
+ heights?: number[]; // Candidate heights for responsive sources
99
+ imgClassName?: string; // `<img/>` element class
100
+ loading?: ImgHTMLAttributes<HTMLImageElement>["loading"]; // "lazy" | "eager"
101
+ mode?: "contain" | "cover" | "responsive"; // Adds CSS helper classes
102
+ noImageContent?: React.ReactNode; // Custom placeholder content when no image
103
+ options?: ImageSourcesOptions; // Resizer/source generation options (passed through)
104
+ quality?: Quality; // Preferred quality; defaults to original if not specified
105
+ role?: ImgHTMLAttributes<HTMLImageElement>["role"]; // Optional ARIA role override
106
+ src: ImageSource | null | undefined; // Image source compatible with @uxf/core/utils/resizer
107
+ widths?: number[]; // Candidate widths for responsive sources
108
+ } & (
109
+ | { width: number; height: number }
110
+ | { width?: never; height: number }
111
+ | { width: number; height?: never }
112
+ );
113
+ ```
114
+
115
+ ### Sizing behavior
116
+ - The fallback `<img/>` receives width/height computed via getImgElementWidth/Height based on your provided width/height and the intrinsic ratio when available.
117
+ - When both width and height are omitted, TypeScript will error by design (you must provide at least one dimension).
118
+
119
+ ### Quality and options
120
+ - quality controls the requested resizer quality; when omitted, the component uses getImgQuality(..., "original").
121
+ - options are passed to the underlying resizer/source helpers. See @uxf/core/utils/resizer and ImageSourcesOptions for details.
122
+
123
+ ### Accessibility
124
+ - alt is required. Use an empty string ("") only for purely decorative images.
125
+ - role is rarely needed; rely on native img semantics where possible.
126
+ - Consider loading="lazy" for below-the-fold images to improve performance.
127
+
128
+ ## Styling
129
+ Wrapper and image classes you can target:
130
+ - Wrapper `<picture/>`: "uxf-raster-image"
131
+ - Modifiers: "uxf-raster-image--contain", "uxf-raster-image--cover"
132
+ - `<img/>`: "uxf-raster-image__img"
133
+ - Modifiers: "uxf-raster-image__img--contain", "uxf-raster-image__img--cover"
134
+
135
+ You can also pass className and imgClassName to append your own classes.
136
+
137
+ ## SVG handling
138
+ If getSvgImgUrl(src) returns a URL, the component renders a plain `<img src="...svg"/>` inside `<picture/>`, bypassing the resizer.
139
+
140
+ ## Notes on integration with the resizer
141
+ - resizerImageUrl is used to produce the fallback `<img/>` source when possible.
142
+ - Responsive `<source/>` tags are produced by ResponsiveImgSources using widths/heights and breakpoints.
143
+ - If resizerSrc cannot be generated, the component falls back to the EmptyImage placeholder.