@uxf/ui 11.80.4 → 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.
- package/checkbox-list/checkbox-list.d.ts +2 -5
- package/checkbox-list/checkbox-list.js +4 -4
- package/checkbox-list/index.d.ts +1 -1
- package/dialog/dialog.d.ts +2 -2
- package/dialog/dialog.js +5 -1
- package/icon/icon.stories.js +1 -1
- package/icon/theme.d.ts +1 -1
- package/modal/modal-provider.js +2 -1
- package/modal/modal.js +7 -2
- package/modal/modal.stories.js +3 -3
- package/modal/types.d.ts +7 -4
- package/multi-combobox/multi-combobox.js +1 -1
- package/package.json +7 -6
- package/raster-image/README.md +138 -2
|
@@ -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
|
-
|
|
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";
|
package/checkbox-list/index.d.ts
CHANGED
|
@@ -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";
|
package/dialog/dialog.d.ts
CHANGED
|
@@ -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, {
|
|
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) => {
|
package/icon/icon.stories.js
CHANGED
package/icon/theme.d.ts
CHANGED
package/modal/modal-provider.js
CHANGED
|
@@ -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.
|
|
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, {
|
|
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));
|
package/modal/modal.stories.js
CHANGED
|
@@ -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, {
|
|
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, {
|
|
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, {
|
|
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?:
|
|
9
|
-
|
|
9
|
+
isBackdropCloseDisabled?: boolean;
|
|
10
|
+
isEscapeKeyCloseDisabled?: boolean;
|
|
11
|
+
onClose?: Noop;
|
|
10
12
|
variant?: ModalVariant;
|
|
11
13
|
}
|
|
12
14
|
export interface ModalRef {
|
|
13
|
-
close:
|
|
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:
|
|
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.
|
|
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": "
|
|
15
|
-
"tw-tokens:gen": "
|
|
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.
|
|
27
|
-
"@uxf/core-react": "11.
|
|
26
|
+
"@uxf/core": "11.85.0",
|
|
27
|
+
"@uxf/core-react": "11.85.0",
|
|
28
28
|
"@uxf/datepicker": "11.80.4",
|
|
29
|
-
"@uxf/styles": "11.
|
|
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
|
}
|
package/raster-image/README.md
CHANGED
|
@@ -1,7 +1,143 @@
|
|
|
1
1
|
# RasterImage
|
|
2
2
|
|
|
3
|
-
|
|
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.
|