@dimaan/ui 0.0.23 → 0.0.26
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/README.md +1 -0
- package/dist/index.cjs +183 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -4
- package/dist/index.d.ts +107 -4
- package/dist/index.js +182 -28
- package/dist/index.js.map +1 -1
- package/dist/preset.css +282 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ Shared React UI component library for Diman company projects.
|
|
|
4
4
|
|
|
5
5
|
Built with **React 19**, **TypeScript**, **Tailwind CSS v4**, and a CSS-first design token system. Distributed as dual ESM/CJS with full type definitions, intended for Vite-based dashboard apps.
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
## Documentation
|
|
8
9
|
|
|
9
10
|
- **[USAGE.md](USAGE.md)** — full consumer guide: install, Tailwind setup, framework-specific examples (Next.js / Vite / Remix), theming, and pitfalls.
|
package/dist/index.cjs
CHANGED
|
@@ -136,7 +136,7 @@ var buttonSizeClass = {
|
|
|
136
136
|
icon: "h-9 w-9 shrink-0 rounded-md p-0",
|
|
137
137
|
"icon-sm": "h-8 w-8 shrink-0 rounded-md p-0"
|
|
138
138
|
};
|
|
139
|
-
var buttonBaseClass = "group/button relative inline-flex items-center justify-center font-medium select-none whitespace-nowrap outline-none transition-[background-color,color,box-shadow,opacity] focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0";
|
|
139
|
+
var buttonBaseClass = "group/button relative inline-flex items-center justify-center font-medium select-none whitespace-nowrap outline-none transition-[background-color,color,box-shadow,opacity,transform] active:scale-[0.98] focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 motion-reduce:transition-none motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:shrink-0";
|
|
140
140
|
var Button = react.forwardRef(function Button2({
|
|
141
141
|
variant = "primary",
|
|
142
142
|
size = "md",
|
|
@@ -2948,37 +2948,130 @@ function hasActiveFilters(filters, values) {
|
|
|
2948
2948
|
}
|
|
2949
2949
|
return false;
|
|
2950
2950
|
}
|
|
2951
|
+
function DebouncedFilterInput({
|
|
2952
|
+
value,
|
|
2953
|
+
onChange,
|
|
2954
|
+
debounceMs,
|
|
2955
|
+
ariaLabel,
|
|
2956
|
+
placeholder,
|
|
2957
|
+
wrapperClassName,
|
|
2958
|
+
disabled
|
|
2959
|
+
}) {
|
|
2960
|
+
const [local, setLocal] = react.useState(value);
|
|
2961
|
+
const timerRef = react.useRef(null);
|
|
2962
|
+
const onChangeRef = react.useRef(onChange);
|
|
2963
|
+
onChangeRef.current = onChange;
|
|
2964
|
+
react.useEffect(() => {
|
|
2965
|
+
setLocal(value);
|
|
2966
|
+
if (timerRef.current) {
|
|
2967
|
+
clearTimeout(timerRef.current);
|
|
2968
|
+
timerRef.current = null;
|
|
2969
|
+
}
|
|
2970
|
+
}, [value]);
|
|
2971
|
+
react.useEffect(
|
|
2972
|
+
() => () => {
|
|
2973
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
2974
|
+
},
|
|
2975
|
+
[]
|
|
2976
|
+
);
|
|
2977
|
+
const handleChange = (next) => {
|
|
2978
|
+
setLocal(next);
|
|
2979
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
2980
|
+
if (debounceMs <= 0) {
|
|
2981
|
+
onChangeRef.current(next);
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
timerRef.current = setTimeout(() => {
|
|
2985
|
+
timerRef.current = null;
|
|
2986
|
+
onChangeRef.current(next);
|
|
2987
|
+
}, debounceMs);
|
|
2988
|
+
};
|
|
2989
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2990
|
+
Input,
|
|
2991
|
+
{
|
|
2992
|
+
type: "search",
|
|
2993
|
+
"aria-label": ariaLabel,
|
|
2994
|
+
placeholder,
|
|
2995
|
+
value: local,
|
|
2996
|
+
onChange: (e) => handleChange(e.target.value),
|
|
2997
|
+
leadingIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "size-4" }),
|
|
2998
|
+
wrapperClassName,
|
|
2999
|
+
disabled
|
|
3000
|
+
}
|
|
3001
|
+
);
|
|
3002
|
+
}
|
|
3003
|
+
var DEFAULT_TEXT_DEBOUNCE_MS = 400;
|
|
2951
3004
|
function ListPageFilterBar({
|
|
2952
3005
|
filters,
|
|
2953
3006
|
values,
|
|
2954
3007
|
onChange,
|
|
2955
3008
|
disabled = false,
|
|
3009
|
+
mode = "live",
|
|
2956
3010
|
labels
|
|
2957
3011
|
}) {
|
|
3012
|
+
const manual = mode === "manual";
|
|
2958
3013
|
const active = hasActiveFilters(filters, values);
|
|
3014
|
+
const appliedKey = JSON.stringify(values ?? {});
|
|
3015
|
+
const [draft, setDraft] = react.useState(values ?? {});
|
|
3016
|
+
react.useEffect(() => {
|
|
3017
|
+
if (manual) setDraft(values ?? {});
|
|
3018
|
+
}, [appliedKey, manual]);
|
|
3019
|
+
const effectiveValues = manual ? draft : values ?? {};
|
|
3020
|
+
const handleChange = (key, value) => {
|
|
3021
|
+
if (manual) {
|
|
3022
|
+
setDraft((prev) => ({ ...prev, [key]: value }));
|
|
3023
|
+
} else {
|
|
3024
|
+
onChange?.(key, value);
|
|
3025
|
+
}
|
|
3026
|
+
};
|
|
3027
|
+
const dirty = manual && (filters ?? []).some((filter) => {
|
|
3028
|
+
const next = draft[filter.key] ?? filterDefaultValue(filter);
|
|
3029
|
+
const current = values?.[filter.key] ?? filterDefaultValue(filter);
|
|
3030
|
+
return next !== current;
|
|
3031
|
+
});
|
|
3032
|
+
const apply = (event) => {
|
|
3033
|
+
event.preventDefault();
|
|
3034
|
+
for (const filter of filters ?? []) {
|
|
3035
|
+
const next = draft[filter.key] ?? filterDefaultValue(filter);
|
|
3036
|
+
const current = values?.[filter.key] ?? filterDefaultValue(filter);
|
|
3037
|
+
if (next !== current) onChange?.(filter.key, next);
|
|
3038
|
+
}
|
|
3039
|
+
};
|
|
2959
3040
|
const reset = () => {
|
|
2960
3041
|
for (const filter of filters ?? []) {
|
|
2961
3042
|
onChange?.(filter.key, filterDefaultValue(filter));
|
|
2962
3043
|
}
|
|
2963
3044
|
};
|
|
3045
|
+
const controls = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: filters?.map((filter) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3046
|
+
FilterControl,
|
|
3047
|
+
{
|
|
3048
|
+
filter,
|
|
3049
|
+
value: effectiveValues[filter.key],
|
|
3050
|
+
onChange: handleChange,
|
|
3051
|
+
disabled,
|
|
3052
|
+
mode
|
|
3053
|
+
},
|
|
3054
|
+
filter.key
|
|
3055
|
+
)) });
|
|
3056
|
+
const resetButton = active && !disabled ? /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: reset, children: [
|
|
3057
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "size-4" }),
|
|
3058
|
+
labels.reset
|
|
3059
|
+
] }) : null;
|
|
3060
|
+
if (manual) {
|
|
3061
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { "data-slot": "list-page-filter-bar", className: "space-y-3", onSubmit: apply, children: [
|
|
3062
|
+
controls,
|
|
3063
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
3064
|
+
resetButton,
|
|
3065
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", size: "sm", disabled: disabled || !dirty, children: labels.apply ?? "Apply" })
|
|
3066
|
+
] })
|
|
3067
|
+
] });
|
|
3068
|
+
}
|
|
2964
3069
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "list-page-filter-bar", className: "space-y-3", children: [
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
{
|
|
2968
|
-
filter,
|
|
2969
|
-
value: values?.[filter.key],
|
|
2970
|
-
onChange,
|
|
2971
|
-
disabled
|
|
2972
|
-
},
|
|
2973
|
-
filter.key
|
|
2974
|
-
)) }),
|
|
2975
|
-
active && !disabled ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", onClick: reset, children: [
|
|
2976
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "size-4" }),
|
|
2977
|
-
labels.reset
|
|
2978
|
-
] }) }) : null
|
|
3070
|
+
controls,
|
|
3071
|
+
resetButton ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: resetButton }) : null
|
|
2979
3072
|
] });
|
|
2980
3073
|
}
|
|
2981
|
-
function FilterControl({ filter, value, onChange, disabled }) {
|
|
3074
|
+
function FilterControl({ filter, value, onChange, disabled, mode }) {
|
|
2982
3075
|
const spanClass = FILTER_SPAN_CLASS[filter.width ?? "default"];
|
|
2983
3076
|
const ariaLabel = typeof filter.label === "string" ? filter.label : filter.key;
|
|
2984
3077
|
switch (filter.type) {
|
|
@@ -2996,14 +3089,13 @@ function FilterControl({ filter, value, onChange, disabled }) {
|
|
|
2996
3089
|
);
|
|
2997
3090
|
case "text":
|
|
2998
3091
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2999
|
-
|
|
3092
|
+
DebouncedFilterInput,
|
|
3000
3093
|
{
|
|
3001
|
-
type: "search",
|
|
3002
|
-
"aria-label": ariaLabel,
|
|
3003
|
-
placeholder: filter.placeholder,
|
|
3004
3094
|
value: value ?? "",
|
|
3005
|
-
onChange: (
|
|
3006
|
-
|
|
3095
|
+
onChange: (v) => onChange?.(filter.key, v),
|
|
3096
|
+
debounceMs: mode === "live" ? filter.debounceMs ?? DEFAULT_TEXT_DEBOUNCE_MS : 0,
|
|
3097
|
+
ariaLabel,
|
|
3098
|
+
placeholder: filter.placeholder,
|
|
3007
3099
|
wrapperClassName: spanClass,
|
|
3008
3100
|
disabled
|
|
3009
3101
|
}
|
|
@@ -3035,13 +3127,22 @@ function FilterControl({ filter, value, onChange, disabled }) {
|
|
|
3035
3127
|
);
|
|
3036
3128
|
}
|
|
3037
3129
|
}
|
|
3038
|
-
var
|
|
3130
|
+
var EN_LABELS2 = {
|
|
3039
3131
|
reset: "Reset filters",
|
|
3132
|
+
apply: "Apply",
|
|
3040
3133
|
emptyTitle: "No results",
|
|
3041
3134
|
emptyDescription: "Try clearing the search or adjusting the filters.",
|
|
3042
3135
|
noDataTitle: "No data yet",
|
|
3043
3136
|
noDataDescription: "Nothing has been added here so far."
|
|
3044
3137
|
};
|
|
3138
|
+
var AR_LABELS2 = {
|
|
3139
|
+
reset: "\u0625\u0639\u0627\u062F\u0629 \u062A\u0639\u064A\u064A\u0646 \u0627\u0644\u0641\u0644\u0627\u062A\u0631",
|
|
3140
|
+
apply: "\u062A\u0637\u0628\u064A\u0642",
|
|
3141
|
+
emptyTitle: "\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C",
|
|
3142
|
+
emptyDescription: "\u062C\u0631\u0651\u0628 \u0645\u0633\u062D \u0627\u0644\u0628\u062D\u062B \u0623\u0648 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0641\u0644\u0627\u062A\u0631.",
|
|
3143
|
+
noDataTitle: "\u0644\u0627 \u062A\u0648\u062C\u062F \u0628\u064A\u0627\u0646\u0627\u062A \u0628\u0639\u062F",
|
|
3144
|
+
noDataDescription: "\u0644\u0645 \u062A\u062A\u0645 \u0625\u0636\u0627\u0641\u0629 \u0623\u064A \u0634\u064A\u0621 \u0647\u0646\u0627 \u062D\u062A\u0649 \u0627\u0644\u0622\u0646."
|
|
3145
|
+
};
|
|
3045
3146
|
function ListPage({
|
|
3046
3147
|
title,
|
|
3047
3148
|
description,
|
|
@@ -3055,6 +3156,7 @@ function ListPage({
|
|
|
3055
3156
|
filters,
|
|
3056
3157
|
filterValues,
|
|
3057
3158
|
onFilterChange,
|
|
3159
|
+
filterMode = "manual",
|
|
3058
3160
|
enableRowSelection,
|
|
3059
3161
|
bulkActions,
|
|
3060
3162
|
pagination,
|
|
@@ -3066,7 +3168,8 @@ function ListPage({
|
|
|
3066
3168
|
labels: labelsProp,
|
|
3067
3169
|
className
|
|
3068
3170
|
}) {
|
|
3069
|
-
const
|
|
3171
|
+
const dir = useDirection();
|
|
3172
|
+
const labels = { ...dir === "rtl" ? AR_LABELS2 : EN_LABELS2, ...labelsProp };
|
|
3070
3173
|
const showFilterBar = Boolean(filters?.length);
|
|
3071
3174
|
const hasActiveQuery = react.useMemo(
|
|
3072
3175
|
() => hasActiveFilters(filters, filterValues),
|
|
@@ -3086,8 +3189,8 @@ function ListPage({
|
|
|
3086
3189
|
filters,
|
|
3087
3190
|
values: filterValues,
|
|
3088
3191
|
onChange: onFilterChange,
|
|
3089
|
-
|
|
3090
|
-
labels: { reset: labels.reset }
|
|
3192
|
+
mode: filterMode,
|
|
3193
|
+
labels: { reset: labels.reset, apply: labels.apply }
|
|
3091
3194
|
}
|
|
3092
3195
|
) : null,
|
|
3093
3196
|
tableMode === "loading" || tableMode === "rows" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3273,6 +3376,57 @@ var RadioGroupItem = react.forwardRef(function RadioGroupItem2({ className, radi
|
|
|
3273
3376
|
);
|
|
3274
3377
|
});
|
|
3275
3378
|
|
|
3379
|
+
// src/components/row-actions/rowActionsVariants.ts
|
|
3380
|
+
var rowActionsBaseClass = "inline-flex items-center gap-1";
|
|
3381
|
+
var rowActionsDestructiveClass = "text-destructive hover:bg-destructive/10 hover:text-destructive focus-visible:ring-destructive/40";
|
|
3382
|
+
var PRESETS = {
|
|
3383
|
+
view: { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, {}), label: "View", destructive: false },
|
|
3384
|
+
edit: { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, {}), label: "Edit", destructive: false },
|
|
3385
|
+
delete: { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, {}), label: "Delete", destructive: true }
|
|
3386
|
+
};
|
|
3387
|
+
function isPreset(action) {
|
|
3388
|
+
return "kind" in action;
|
|
3389
|
+
}
|
|
3390
|
+
function resolveAction(action) {
|
|
3391
|
+
if (isPreset(action)) {
|
|
3392
|
+
const preset = PRESETS[action.kind];
|
|
3393
|
+
return {
|
|
3394
|
+
icon: preset.icon,
|
|
3395
|
+
label: action.label ?? preset.label,
|
|
3396
|
+
destructive: preset.destructive,
|
|
3397
|
+
onClick: action.onClick,
|
|
3398
|
+
disabled: action.disabled ?? false
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3401
|
+
return {
|
|
3402
|
+
icon: action.icon,
|
|
3403
|
+
label: action.label,
|
|
3404
|
+
destructive: action.variant === "destructive",
|
|
3405
|
+
onClick: action.onClick,
|
|
3406
|
+
disabled: action.disabled ?? false
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
function RowActions({ actions, size = "icon-sm", className }) {
|
|
3410
|
+
if (actions.length === 0) return null;
|
|
3411
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(rowActionsBaseClass, className), children: actions.map((action, index) => {
|
|
3412
|
+
const resolved = resolveAction(action);
|
|
3413
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3414
|
+
Button,
|
|
3415
|
+
{
|
|
3416
|
+
type: "button",
|
|
3417
|
+
variant: "ghost",
|
|
3418
|
+
size,
|
|
3419
|
+
"aria-label": resolved.label,
|
|
3420
|
+
disabled: resolved.disabled,
|
|
3421
|
+
onClick: resolved.onClick,
|
|
3422
|
+
className: resolved.destructive ? rowActionsDestructiveClass : void 0,
|
|
3423
|
+
children: resolved.icon
|
|
3424
|
+
},
|
|
3425
|
+
index
|
|
3426
|
+
);
|
|
3427
|
+
}) });
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3276
3430
|
// src/components/switch/switchVariants.ts
|
|
3277
3431
|
var switchTrackClass = {
|
|
3278
3432
|
sm: "h-4 w-7",
|
|
@@ -3571,6 +3725,7 @@ exports.MultiSelect = MultiSelect;
|
|
|
3571
3725
|
exports.PageHeader = PageHeader;
|
|
3572
3726
|
exports.RadioGroup = RadioGroup;
|
|
3573
3727
|
exports.RadioGroupItem = RadioGroupItem;
|
|
3728
|
+
exports.RowActions = RowActions;
|
|
3574
3729
|
exports.Select = Select;
|
|
3575
3730
|
exports.Sidebar = Sidebar;
|
|
3576
3731
|
exports.SidebarFooter = SidebarFooter;
|
|
@@ -3684,6 +3839,8 @@ exports.radioItemBaseClass = radioItemBaseClass;
|
|
|
3684
3839
|
exports.radioItemSizeClass = radioItemSizeClass;
|
|
3685
3840
|
exports.radioLabelSizeClass = radioLabelSizeClass;
|
|
3686
3841
|
exports.radioOptionRowClass = radioOptionRowClass;
|
|
3842
|
+
exports.rowActionsBaseClass = rowActionsBaseClass;
|
|
3843
|
+
exports.rowActionsDestructiveClass = rowActionsDestructiveClass;
|
|
3687
3844
|
exports.selectBaseClass = selectBaseClass;
|
|
3688
3845
|
exports.selectSizeClass = selectSizeClass;
|
|
3689
3846
|
exports.selectVariantClass = selectVariantClass;
|