sgod-library-frontend
Version:
Thư viện component UI Frontend
913 lines (888 loc) • 99.6 kB
JavaScript
'use strict';
var React = require('react');
var classNames = require('classnames');
var mitt = require('mitt');
var clsx = require('clsx');
var tailwindMerge = require('tailwind-merge');
var sonner = require('sonner');
var jwt = require('jsonwebtoken');
var z = require('zod');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var Button = function (_a) {
var children = _a.children, _b = _a.variant, variant = _b === void 0 ? 'primary' : _b, _c = _a.size, size = _c === void 0 ? 'md' : _c, _d = _a.isLoading, isLoading = _d === void 0 ? false : _d, _e = _a.isFullWidth, isFullWidth = _e === void 0 ? false : _e, leftIcon = _a.leftIcon, rightIcon = _a.rightIcon, className = _a.className, disabled = _a.disabled, rest = __rest(_a, ["children", "variant", "size", "isLoading", "isFullWidth", "leftIcon", "rightIcon", "className", "disabled"]);
var baseClasses = "inline-flex items-center justify-center font-medium rounded transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2";
var variantClasses = {
primary: "bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500",
secondary: "bg-secondary-200 text-secondary-800 hover:bg-secondary-300 focus:ring-secondary-400",
outline: "border border-primary-600 text-primary-600 hover:bg-primary-50 focus:ring-primary-500",
ghost: "text-primary-600 hover:bg-primary-50 focus:ring-primary-500",
error: "bg-error text-white hover:bg-red-600 focus:ring-red-500",
};
var sizeClasses = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg",
};
var loadingClasses = isLoading ? "opacity-80 cursor-not-allowed" : "";
var widthClasses = isFullWidth ? "w-full" : "";
var disabledClasses = disabled ? "opacity-50 cursor-not-allowed" : "";
var buttonClasses = classNames(baseClasses, variantClasses[variant], sizeClasses[size], loadingClasses, widthClasses, disabledClasses, className);
return (React.createElement("button", __assign({ className: buttonClasses, disabled: disabled || isLoading }, rest),
isLoading && (React.createElement("svg", { className: "animate-spinner -ml-1 mr-2 h-4 w-4 text-current", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24" },
React.createElement("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
React.createElement("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" }))),
!isLoading && leftIcon && React.createElement("span", { className: "mr-2" }, leftIcon),
children,
!isLoading && rightIcon && React.createElement("span", { className: "ml-2" }, rightIcon)));
};
var Input = React.forwardRef(function (_a, ref) {
var className = _a.className, label = _a.label, error = _a.error, helperText = _a.helperText, _b = _a.size, size = _b === void 0 ? 'md' : _b, _c = _a.fullWidth, fullWidth = _c === void 0 ? false : _c, leftIcon = _a.leftIcon, rightIcon = _a.rightIcon, _d = _a.isLoading, isLoading = _d === void 0 ? false : _d, disabled = _a.disabled, required = _a.required, id = _a.id, rest = __rest(_a, ["className", "label", "error", "helperText", "size", "fullWidth", "leftIcon", "rightIcon", "isLoading", "disabled", "required", "id"]);
var inputId = id || "input-".concat(Math.random().toString(36).substr(2, 9));
var sizeClasses = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-4 py-3 text-lg',
};
var containerClasses = classNames('flex flex-col', {
'w-full': fullWidth,
'opacity-60 cursor-not-allowed': disabled,
});
var inputContainerClasses = classNames('relative flex items-center border rounded focus-within:ring-2 focus-within:ring-offset-1 transition-all', {
'border-red-500 focus-within:ring-red-500': error,
'border-gray-300 focus-within:border-primary-500 focus-within:ring-primary-500': !error,
'bg-gray-100': disabled,
});
var inputClasses = classNames('w-full outline-none bg-transparent', sizeClasses[size], {
'pl-9': leftIcon,
'pr-9': rightIcon || isLoading,
}, className);
return (React.createElement("div", { className: containerClasses },
label && (React.createElement("label", { htmlFor: inputId, className: "mb-1 text-sm font-medium text-gray-700" },
label,
required && React.createElement("span", { className: "text-red-500 ml-1" }, "*"))),
React.createElement("div", { className: inputContainerClasses },
leftIcon && (React.createElement("span", { className: "absolute left-3 flex items-center pointer-events-none text-gray-500" }, leftIcon)),
React.createElement("input", __assign({ ref: ref, id: inputId, disabled: disabled || isLoading, required: required, className: inputClasses, "aria-invalid": error ? 'true' : 'false', "aria-describedby": error ? "".concat(inputId, "-error") : helperText ? "".concat(inputId, "-helper") : undefined }, rest)),
isLoading ? (React.createElement("span", { className: "absolute right-3 flex items-center pointer-events-none text-gray-500" },
React.createElement("svg", { className: "animate-spinner h-4 w-4", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24" },
React.createElement("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
React.createElement("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })))) : rightIcon ? (React.createElement("span", { className: "absolute right-3 flex items-center pointer-events-none text-gray-500" }, rightIcon)) : null),
error && (React.createElement("p", { id: "".concat(inputId, "-error"), className: "mt-1 text-sm text-red-500" }, error)),
helperText && !error && (React.createElement("p", { id: "".concat(inputId, "-helper"), className: "mt-1 text-sm text-gray-500" }, helperText))));
});
Input.displayName = 'Input';
var Skeleton = function (_a) {
var className = _a.className, _b = _a.width, width = _b === void 0 ? '100%' : _b, _c = _a.height, height = _c === void 0 ? '1rem' : _c, _d = _a.borderRadius, borderRadius = _d === void 0 ? '0.25rem' : _d;
var style = {
width: typeof width === 'number' ? "".concat(width, "px") : width,
height: typeof height === 'number' ? "".concat(height, "px") : height,
borderRadius: borderRadius,
};
return (React.createElement("div", { className: classNames('shimmer', className), style: style, role: "status", "aria-busy": "true", "aria-live": "polite" },
React.createElement("span", { className: "sr-only" }, "\u0110ang t\u1EA3i...")));
};
var Grid = function (_a) {
var children = _a.children, _b = _a.container, container = _b === void 0 ? false : _b, _c = _a.item, item = _c === void 0 ? false : _c, cols = _a.cols, sm = _a.sm, md = _a.md, lg = _a.lg, xl = _a.xl, _d = _a.spacing, spacing = _d === void 0 ? 4 : _d, justifyContent = _a.justifyContent, alignItems = _a.alignItems, _e = _a.direction, direction = _e === void 0 ? 'row' : _e, _f = _a.wrap, wrap = _f === void 0 ? 'wrap' : _f, className = _a.className;
var spacingClass = container ? "gap-".concat(spacing) : '';
// Cột mặc định (cho màn hình nhỏ nhất)
var colsClasses = cols ? "grid-cols-".concat(cols) : '';
// Responsive grid columns
var smColsClasses = sm ? "sm:grid-cols-".concat(sm) : '';
var mdColsClasses = md ? "md:grid-cols-".concat(md) : '';
var lgColsClasses = lg ? "lg:grid-cols-".concat(lg) : '';
var xlColsClasses = xl ? "xl:grid-cols-".concat(xl) : '';
// Item spanning
var itemColSpan = item && cols ? "col-span-".concat(cols) : '';
var itemSmColSpan = item && sm ? "sm:col-span-".concat(sm) : '';
var itemMdColSpan = item && md ? "md:col-span-".concat(md) : '';
var itemLgColSpan = item && lg ? "lg:col-span-".concat(lg) : '';
var itemXlColSpan = item && xl ? "xl:col-span-".concat(xl) : '';
// Alignment
var justifyContentClass = justifyContent ? "justify-".concat(justifyContent) : '';
var alignItemsClass = alignItems ? "items-".concat(alignItems) : '';
var directionClass = "flex-".concat(direction);
var wrapClass = "flex-".concat(wrap);
return (React.createElement("div", { className: classNames(
// Nếu là container, sử dụng grid
container && 'grid',
// Nếu là container, áp dụng gap
spacingClass,
// Áp dụng số cột nếu là container
container && colsClasses, container && smColsClasses, container && mdColsClasses, container && lgColsClasses, container && xlColsClasses,
// Áp dụng span columns nếu là item
item && itemColSpan, item && itemSmColSpan, item && itemMdColSpan, item && itemLgColSpan, item && itemXlColSpan,
// Alignment
justifyContentClass, alignItemsClass, container && directionClass, container && wrapClass, className) }, children));
};
var getIconSize = function (size) {
switch (size) {
case 'xs':
return 'w-3 h-3'; // 12px
case 'sm':
return 'w-4 h-4'; // 16px
case 'md':
return 'w-5 h-5'; // 20px
case 'lg':
return 'w-6 h-6'; // 24px
case 'xl':
return 'w-8 h-8'; // 32px
case '2xl':
return 'w-10 h-10'; // 40px
default:
return 'w-5 h-5'; // 20px
}
};
// Bộ icons được định nghĩa ở đây
// Sử dụng cách tiếp cận "render props" để có thể dễ dàng mở rộng
var icons = {
'check': {
outline: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", className: className },
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" })));
},
solid: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: className },
React.createElement("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" })));
},
},
'x': {
outline: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", className: className },
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" })));
},
solid: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: className },
React.createElement("path", { fillRule: "evenodd", d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z", clipRule: "evenodd" })));
},
},
'info': {
outline: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", className: className },
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" })));
},
solid: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: className },
React.createElement("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", clipRule: "evenodd" })));
},
},
'warning': {
outline: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", className: className },
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" })));
},
solid: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: className },
React.createElement("path", { fillRule: "evenodd", d: "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z", clipRule: "evenodd" })));
},
},
'error': {
outline: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", className: className },
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" })));
},
solid: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: className },
React.createElement("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z", clipRule: "evenodd" })));
},
},
'success': {
outline: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", className: className },
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" })));
},
solid: function (_a) {
var className = _a.className;
return (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", className: className },
React.createElement("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", clipRule: "evenodd" })));
},
},
};
var Icon = function (_a) {
var _b;
var name = _a.name, _c = _a.size, size = _c === void 0 ? 'md' : _c, _d = _a.variant, variant = _d === void 0 ? 'outline' : _d, color = _a.color, className = _a.className;
// Chọn icon từ bộ icons
var IconComponent = (_b = icons[name]) === null || _b === void 0 ? void 0 : _b[variant];
if (!IconComponent) {
console.warn("Icon \"".concat(name, "\" with variant \"").concat(variant, "\" not found"));
return null;
}
var sizeClass = getIconSize(size);
var colorClass = color ? "text-".concat(color) : '';
return (React.createElement(IconComponent, { className: classNames(sizeClass, colorClass, className) }));
};
var Typography = function (_a) {
var _b = _a.variant, variant = _b === void 0 ? 'body1' : _b, _c = _a.align, align = _c === void 0 ? 'inherit' : _c, _d = _a.color, color = _d === void 0 ? 'inherit' : _d, weight = _a.weight, children = _a.children, className = _a.className, component = _a.component, _e = _a.gutterBottom, gutterBottom = _e === void 0 ? false : _e, _f = _a.noWrap, noWrap = _f === void 0 ? false : _f;
// Xác định component mặc định dựa vào variant
var Component = component || getComponentFromVariant(variant);
var variantClasses = {
h1: 'text-5xl font-heading font-bold leading-tight',
h2: 'text-4xl font-heading font-bold leading-tight',
h3: 'text-3xl font-heading font-bold leading-tight',
h4: 'text-2xl font-heading font-bold leading-tight',
h5: 'text-xl font-heading font-medium leading-tight',
h6: 'text-lg font-heading font-medium leading-tight',
subtitle1: 'text-lg font-body leading-normal',
subtitle2: 'text-base font-body font-medium leading-normal',
body1: 'text-base font-body leading-normal',
body2: 'text-sm font-body leading-normal',
caption: 'text-xs font-body leading-normal',
overline: 'text-xs font-body uppercase tracking-wider leading-normal',
};
var alignClasses = {
inherit: '',
left: 'text-left',
center: 'text-center',
right: 'text-right',
justify: 'text-justify',
};
var colorClasses = {
inherit: '',
primary: 'text-primary-600',
secondary: 'text-secondary-600',
success: 'text-success-600',
error: 'text-error-600',
warning: 'text-warning-600',
info: 'text-info-600',
neutral: 'text-neutral-600',
};
var weightClasses = {
regular: 'font-regular',
medium: 'font-medium',
bold: 'font-bold',
};
var classes = classNames(variantClasses[variant], alignClasses[align], colorClasses[color], weight && weightClasses[weight], gutterBottom && 'mb-2', noWrap && 'truncate', className);
return (React.createElement(Component, { className: classes }, children));
};
// Helper function để xác định component mặc định dựa vào variant
function getComponentFromVariant(variant) {
switch (variant) {
case 'h1':
return 'h1';
case 'h2':
return 'h2';
case 'h3':
return 'h3';
case 'h4':
return 'h4';
case 'h5':
return 'h5';
case 'h6':
return 'h6';
case 'subtitle1':
case 'subtitle2':
return 'h6';
case 'body1':
case 'body2':
return 'p';
case 'caption':
case 'overline':
return 'span';
default:
return 'p';
}
}
var Card = function (_a) {
var children = _a.children, className = _a.className, title = _a.title, subtitle = _a.subtitle, footer = _a.footer, _b = _a.elevation, elevation = _b === void 0 ? 'sm' : _b, _c = _a.bordered, bordered = _c === void 0 ? false : _c, _d = _a.rounded, rounded = _d === void 0 ? 'md' : _d, _e = _a.padding, padding = _e === void 0 ? 'md' : _e;
var elevationClasses = {
none: '',
sm: 'shadow-sm',
md: 'shadow-md',
lg: 'shadow-lg',
};
var roundedClasses = {
none: 'rounded-none',
sm: 'rounded-sm',
md: 'rounded-md',
lg: 'rounded-lg',
full: 'rounded-full',
};
var paddingClasses = {
none: 'p-0',
sm: 'p-3',
md: 'p-4',
lg: 'p-6',
};
var headerClasses = classNames('border-b border-gray-200', {
'pb-3 mb-3': padding !== 'none',
'pb-2 mb-2': padding === 'sm',
'pb-4 mb-4': padding === 'lg',
});
var containerClasses = classNames('bg-white', elevationClasses[elevation], roundedClasses[rounded], paddingClasses[padding], {
'border border-gray-200': bordered,
}, className);
return (React.createElement("div", { className: containerClasses },
(title || subtitle) && (React.createElement("div", { className: headerClasses },
title && React.createElement("h3", { className: "text-lg font-medium text-gray-900" }, title),
subtitle && React.createElement("p", { className: "text-sm text-gray-500" }, subtitle))),
React.createElement("div", { className: "card-content" }, children),
footer && (React.createElement("div", { className: classNames('border-t border-gray-200', {
'pt-3 mt-3': padding !== 'none',
'pt-2 mt-2': padding === 'sm',
'pt-4 mt-4': padding === 'lg',
}) }, footer))));
};
var EmptyState = function (_a) {
var title = _a.title, description = _a.description, icon = _a.icon, action = _a.action, className = _a.className, _b = _a.size, size = _b === void 0 ? 'md' : _b;
var sizeClasses = {
sm: {
container: 'p-4',
icon: 'w-10 h-10',
title: 'text-base',
description: 'text-xs',
},
md: {
container: 'p-6',
icon: 'w-16 h-16',
title: 'text-lg',
description: 'text-sm',
},
lg: {
container: 'p-8',
icon: 'w-20 h-20',
title: 'text-xl',
description: 'text-base',
},
};
var selectedSize = sizeClasses[size];
return (React.createElement("div", { className: classNames('flex flex-col items-center justify-center text-center bg-gray-50 border border-dashed border-gray-300 rounded-lg', selectedSize.container, className) },
icon && (React.createElement("div", { className: classNames('text-gray-400 mb-3', selectedSize.icon) }, icon)),
React.createElement("h3", { className: classNames('font-medium text-gray-900', selectedSize.title) }, title),
description && (React.createElement("p", { className: classNames('text-gray-500 mt-1', selectedSize.description) }, description)),
action && (React.createElement("div", { className: "mt-4" },
React.createElement(Button, { variant: "primary", size: size === 'lg' ? 'md' : 'sm', onClick: action.onClick }, action.label)))));
};
var ErrorState = function (_a) {
var _b = _a.title, title = _b === void 0 ? 'Đã xảy ra lỗi' : _b, _c = _a.message, message = _c === void 0 ? 'Có lỗi xảy ra khi tải dữ liệu. Vui lòng thử lại sau.' : _c, onRetry = _a.onRetry, onHelp = _a.onHelp, className = _a.className, icon = _a.icon;
return (React.createElement("div", { className: classNames('bg-red-50 border border-red-100 rounded-lg p-4 text-center', className) },
icon ? (React.createElement("div", { className: "mx-auto text-red-500 mb-3" }, icon)) : (React.createElement("div", { className: "mx-auto w-12 h-12 text-red-500 mb-3" },
React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor" },
React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" })))),
React.createElement("h3", { className: "text-lg font-medium text-red-800" }, title),
React.createElement("p", { className: "mt-1 text-sm text-red-700" }, message),
React.createElement("div", { className: "mt-4 flex flex-col sm:flex-row justify-center space-y-2 sm:space-y-0 sm:space-x-2" },
onRetry && (React.createElement(Button, { onClick: onRetry, variant: "primary" }, "Th\u1EED l\u1EA1i")),
onHelp && (React.createElement(Button, { onClick: onHelp, variant: "outline" }, "Tr\u1EE3 gi\u00FAp")))));
};
var FormField = function (_a) {
var name = _a.name, children = _a.children, props = __rest(_a, ["name", "children"]);
return children ? (React.createElement("div", { className: "mb-4" },
props.label && (React.createElement("label", { htmlFor: name, className: "block mb-1 text-sm font-medium text-gray-700" },
props.label,
props.required && React.createElement("span", { className: "text-red-500 ml-1" }, "*"))),
children,
props.error && (React.createElement("p", { className: "mt-1 text-sm text-red-500" }, props.error)),
props.helperText && !props.error && (React.createElement("p", { className: "mt-1 text-sm text-gray-500" }, props.helperText)))) : (React.createElement(Input, __assign({ name: name, className: "mb-4" }, props)));
};
var severityIconMap = {
info: 'info',
success: 'success',
warning: 'warning',
error: 'error',
};
var Alert = function (_a) {
var _b = _a.severity, severity = _b === void 0 ? 'info' : _b, _c = _a.variant, variant = _c === void 0 ? 'standard' : _c, title = _a.title, children = _a.children, className = _a.className, _d = _a.icon, icon = _d === void 0 ? true : _d, onClose = _a.onClose;
// Cấu hình màu sắc dựa trên severity và variant
var getColorClasses = function () {
switch (variant) {
case 'filled':
return {
info: 'bg-info-600 text-white',
success: 'bg-success-600 text-white',
warning: 'bg-warning-600 text-white',
error: 'bg-error-600 text-white',
}[severity];
case 'outlined':
return {
info: 'border border-info-300 bg-info-50 text-info-800',
success: 'border border-success-300 bg-success-50 text-success-800',
warning: 'border border-warning-300 bg-warning-50 text-warning-800',
error: 'border border-error-300 bg-error-50 text-error-800',
}[severity];
case 'standard':
default:
return {
info: 'bg-info-50 text-info-800',
success: 'bg-success-50 text-success-800',
warning: 'bg-warning-50 text-warning-800',
error: 'bg-error-50 text-error-800',
}[severity];
}
};
// Lấy màu biểu tượng
var getIconColor = function () {
if (variant === 'filled')
return 'white';
return {
info: 'info-600',
success: 'success-600',
warning: 'warning-600',
error: 'error-600',
}[severity];
};
// Render biểu tượng
var renderIcon = function () {
if (icon === false)
return null;
if (icon === true) {
return (React.createElement("div", { className: "flex-shrink-0" },
React.createElement(Icon, { name: severityIconMap[severity], color: getIconColor(), size: "md" })));
}
return React.createElement("div", { className: "flex-shrink-0" }, icon);
};
return (React.createElement("div", { className: classNames('rounded-md p-4 flex', getColorClasses(), className), role: "alert" },
renderIcon(),
React.createElement("div", { className: "ml-3 flex-grow" },
title && (React.createElement("h3", { className: classNames('text-sm font-medium', variant === 'filled' ? 'text-white' : '') }, title)),
React.createElement("div", { className: classNames('text-sm', title ? 'mt-2' : '', variant === 'filled' ? 'text-white' : '') }, children)),
onClose && (React.createElement("button", { type: "button", className: classNames('ml-auto -mx-1.5 -my-1.5 p-1.5 rounded-md focus:outline-none focus:ring-2', variant === 'filled'
? 'text-white hover:bg-opacity-10 hover:bg-white focus:ring-white'
: "text-".concat(severity, "-600 hover:bg-").concat(severity, "-100 focus:ring-").concat(severity, "-500")), onClick: onClose, "aria-label": "\u0110\u00F3ng" },
React.createElement("span", { className: "sr-only" }, "\u0110\u00F3ng"),
React.createElement(Icon, { name: "x", size: "sm" })))));
};
function DataTable(_a) {
var columns = _a.columns, _b = _a.data, data = _b === void 0 ? [] : _b, keyExtractor = _a.keyExtractor, _c = _a.isLoading, isLoading = _c === void 0 ? false : _c, _d = _a.error, error = _d === void 0 ? null : _d, onRetry = _a.onRetry, _e = _a.emptyStateProps, emptyStateProps = _e === void 0 ? {
title: 'Không có dữ liệu',
description: 'Hiện không có dữ liệu nào. Vui lòng thử lại sau hoặc thêm dữ liệu mới.'
} : _e, className = _a.className, rowClassName = _a.rowClassName;
var renderSkeletonRows = function () {
return Array(5)
.fill(0)
.map(function (_, index) { return (React.createElement("tr", { key: "skeleton-".concat(index), className: "animate-pulse" }, columns.map(function (column) { return (React.createElement("td", { key: "skeleton-cell-".concat(column.key, "-").concat(index), className: "px-4 py-3 border-b border-gray-200" },
React.createElement(Skeleton, { height: 20, width: "80%" }))); }))); });
};
var getRowClassName = function (item, index) {
if (typeof rowClassName === 'function') {
return rowClassName(item, index);
}
return rowClassName || '';
};
if (error) {
return (React.createElement("div", { className: "border border-gray-200 rounded-lg overflow-hidden" },
React.createElement(ErrorState, { title: "Kh\u00F4ng th\u1EC3 t\u1EA3i d\u1EEF li\u1EC7u", message: error.message || 'Đã xảy ra lỗi khi tải dữ liệu. Vui lòng thử lại.', onRetry: onRetry })));
}
if (!isLoading && (!data || data.length === 0)) {
return (React.createElement("div", { className: "border border-gray-200 rounded-lg overflow-hidden" },
React.createElement(EmptyState, __assign({}, emptyStateProps))));
}
return (React.createElement("div", { className: classNames('overflow-x-auto rounded-lg border border-gray-200', className) },
React.createElement("table", { className: "min-w-full divide-y divide-gray-200" },
React.createElement("thead", { className: "bg-gray-50" },
React.createElement("tr", null, columns.map(function (column) { return (React.createElement("th", { key: "header-".concat(column.key), className: "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", style: { width: column.width } }, column.header)); }))),
React.createElement("tbody", { className: "bg-white divide-y divide-gray-200" }, isLoading
? renderSkeletonRows()
: data.map(function (item, index) { return (React.createElement("tr", { key: keyExtractor(item), className: classNames('hover:bg-gray-50', getRowClassName(item, index)) }, columns.map(function (column) { return (React.createElement("td", { key: "".concat(keyExtractor(item), "-").concat(column.key), className: "px-4 py-3 border-b border-gray-200" }, column.render ? column.render(item, index) : String(item[column.key]))); }))); })))));
}
var Form = function (_a) {
var children = _a.children, onSubmit = _a.onSubmit, _b = _a.isLoading, isLoading = _b === void 0 ? false : _b, _c = _a.submitLabel, submitLabel = _c === void 0 ? 'Lưu' : _c, _d = _a.resetLabel, resetLabel = _d === void 0 ? 'Huỷ' : _d, onReset = _a.onReset, error = _a.error, success = _a.success, actionButtons = _a.actionButtons, _e = _a.alignActions, alignActions = _e === void 0 ? 'right' : _e, className = _a.className, rest = __rest(_a, ["children", "onSubmit", "isLoading", "submitLabel", "resetLabel", "onReset", "error", "success", "actionButtons", "alignActions", "className"]);
var handleSubmit = function (e) {
e.preventDefault();
onSubmit(e);
};
var alignmentClasses = {
left: 'justify-start',
center: 'justify-center',
right: 'justify-end',
};
return (React.createElement("form", __assign({ className: classNames('w-full', className), onSubmit: handleSubmit }, rest),
React.createElement("div", { className: "space-y-4" },
children,
error && (React.createElement("div", { className: "p-3 bg-red-50 border border-red-100 rounded text-red-700 text-sm" }, error)),
success && (React.createElement("div", { className: "p-3 bg-green-50 border border-green-100 rounded text-green-700 text-sm" }, success)),
React.createElement("div", { className: classNames('flex flex-wrap gap-3 pt-2', alignmentClasses[alignActions]) }, actionButtons || (React.createElement(React.Fragment, null,
React.createElement(Button, { type: "submit", isLoading: isLoading, disabled: isLoading }, submitLabel),
onReset && (React.createElement(Button, { type: "button", variant: "outline", onClick: onReset, disabled: isLoading }, resetLabel))))))));
};
// Fluid Responsive Design Utilities using Viewport-based Tailwind CSS
// Thay đổi từ pixel cố định sang responsive theo tỉ lệ viewport
// Viewport-based breakpoint values (rem units scale with root font-size)
var FLUID_BREAKPOINTS = {
xs: '20rem', // ~320px equivalent, scales with font-size
sm: '30rem', // ~480px equivalent, scales with font-size
md: '48rem', // ~768px equivalent, scales with font-size
lg: '64rem', // ~1024px equivalent, scales with font-size
xl: '80rem', // ~1280px equivalent, scales with font-size
'2xl': '96rem', // ~1536px equivalent, scales with font-size
'3xl': '120rem', // ~1920px equivalent, scales with font-size
};
// Legacy pixel breakpoints (để compatibility với code cũ)
var TAILWIND_BREAKPOINTS = {
xs: 320,
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
'2xl': 1536,
};
// ========================================
// FLUID GRID UTILITIES (Grid tự động scale)
// ========================================
var createFluidGrid = function (config) {
var _a = config.type, type = _a === void 0 ? 'auto-fit' : _a, _b = config.minSize, minSize = _b === void 0 ? 'md' : _b, maxCols = config.maxCols, _c = config.gap, gap = _c === void 0 ? 'md' : _c, _d = config.aspectRatio, aspectRatio = _d === void 0 ? 'auto' : _d;
var gapClasses = {
xs: 'gap-fluid-xs',
sm: 'gap-fluid-sm',
base: 'gap-fluid-md',
md: 'gap-fluid-md',
lg: 'gap-fluid-lg',
xl: 'gap-fluid-xl',
'2xl': 'gap-fluid-2xl',
'3xl': 'gap-fluid-3xl',
};
var minSizeMap = {
xs: '15rem', // ~240px
sm: '18rem', // ~288px
base: '20rem', // ~320px (same as md)
md: '20rem', // ~320px
lg: '25rem', // ~400px
xl: '30rem', // ~480px
'2xl': '35rem', // ~560px
'3xl': '40rem', // ~640px
};
var classes = ['grid'];
// Grid template
if (type === 'fixed' && maxCols) {
classes.push("grid-cols-1 sm:grid-cols-2 md:grid-cols-".concat(Math.min(maxCols, 3), " lg:grid-cols-").concat(maxCols));
}
else {
var minSizeValue = typeof minSize === 'string' && minSize in minSizeMap
? minSizeMap[minSize]
: minSize;
classes.push("grid-cols-[repeat(".concat(type, ",minmax(min(100%,").concat(minSizeValue, "),1fr))]"));
}
// Gap
classes.push(gapClasses[gap]);
// Aspect ratio
if (aspectRatio !== 'auto') {
var aspectClasses = {
square: '[&>*]:aspect-square',
video: '[&>*]:aspect-video',
photo: '[&>*]:aspect-[4/3]',
};
classes.push(aspectClasses[aspectRatio]);
}
return classes.join(' ');
};
// ========================================
// FLUID TYPOGRAPHY UTILITIES
// ========================================
var createFluidText = function (config) {
var _a = config.size, size = _a === void 0 ? 'base' : _a, weight = config.weight, _b = config.responsive, responsive = _b === void 0 ? true : _b;
var classes = [];
// Sử dụng fluid font sizes
if (responsive) {
var fluidSizes = {
xs: 'text-fluid-xs',
sm: 'text-fluid-sm',
base: 'text-fluid-base',
md: 'text-fluid-base',
lg: 'text-fluid-lg',
xl: 'text-fluid-xl',
'2xl': 'text-fluid-2xl',
'3xl': 'text-fluid-3xl',
};
classes.push(fluidSizes[size] || fluidSizes.base);
}
else {
classes.push("text-".concat(size));
}
if (weight) {
classes.push("font-".concat(weight));
}
return classes.join(' ');
};
// Display typography cho hero sections
var createDisplayText = function (config) {
var _a = config.size, size = _a === void 0 ? 'md' : _a, _b = config.weight, weight = _b === void 0 ? 'bold' : _b;
var displaySizes = {
sm: 'text-display-sm',
md: 'text-display-md',
lg: 'text-display-lg',
xl: 'text-display-xl',
};
return "".concat(displaySizes[size], " font-").concat(weight, " leading-tight tracking-tight");
};
// ========================================
// FLUID SPACING UTILITIES
// ========================================
var createFluidSpacing = function (config) {
var _a = config.size, size = _a === void 0 ? 'md' : _a, _b = config.type, type = _b === void 0 ? 'p' : _b, _c = config.context, context = _c === void 0 ? 'element' : _c;
var contextMap = {
element: {
xs: 'fluid-xs', sm: 'fluid-sm', base: 'fluid-md', md: 'fluid-md',
lg: 'fluid-lg', xl: 'fluid-xl', '2xl': 'fluid-2xl', '3xl': 'fluid-3xl'
},
container: {
xs: 'container-xs', sm: 'container-sm', base: 'container-sm', md: 'container-sm',
lg: 'container-lg', xl: 'container-lg', '2xl': 'container-lg', '3xl': 'container-lg'
},
section: {
xs: 'section-xs', sm: 'section-xs', base: 'section-sm', md: 'section-sm',
lg: 'section-sm', xl: 'section-lg', '2xl': 'section-lg', '3xl': 'section-lg'
}
};
var spacingValue = contextMap[context][size];
return "".concat(type, "-").concat(spacingValue);
};
// ========================================
// FLUID CONTAINER UTILITIES
// ========================================
var createFluidContainer = function (config) {
var _a = config || {}, _b = _a.maxWidth, maxWidth = _b === void 0 ? 'fluid' : _b, _c = _a.padding, padding = _c === void 0 ? true : _c, _d = _a.center, center = _d === void 0 ? true : _d;
var classes = [];
if (center) {
classes.push('mx-auto');
}
// Fluid container với clamp
if (maxWidth === 'fluid') {
classes.push('w-full max-w-[clamp(20rem,90vw,100rem)]'); // 320px - 1600px
}
else if (maxWidth !== 'full') {
classes.push("max-w-".concat(maxWidth));
}
else {
classes.push('w-full');
}
if (padding) {
classes.push('px-container-xs md:px-container-sm lg:px-container-lg');
}
return classes.join(' ');
};
// ========================================
// FLUID CARD UTILITIES
// ========================================
var createFluidCard = function (config) {
var _a = config || {}, _b = _a.padding, padding = _b === void 0 ? 'md' : _b, _c = _a.shadow, shadow = _c === void 0 ? 'md' : _c, _d = _a.rounded, rounded = _d === void 0 ? 'lg' : _d, _e = _a.border, border = _e === void 0 ? false : _e, _f = _a.hover, hover = _f === void 0 ? true : _f;
var classes = [
'bg-white',
createFluidSpacing({ size: padding, type: 'p' }),
"shadow-".concat(shadow),
"rounded-".concat(rounded),
];
if (border) {
classes.push('border border-gray-200');
}
if (hover) {
classes.push('transition-all duration-300 hover:shadow-lg hover:scale-[1.02]');
}
return classes.join(' ');
};
// ========================================
// VIEWPORT RESPONSIVE UTILITIES
// ========================================
var createViewportGrid = function (config) {
var _a = config.minWidth, minWidth = _a === void 0 ? '25vw' : _a; config.maxCols; var _c = config.gap, gap = _c === void 0 ? 'md' : _c;
return createFluidGrid({
type: 'auto-fit',
minSize: minWidth,
gap: gap,
});
};
// Responsive theo aspect ratio màn hình
var createAspectRatioResponsive = function (config) {
var widescreen = config.widescreen, standard = config.standard, mobile = config.mobile;
var classes = [];
if (mobile) {
classes.push(mobile);
}
if (standard) {
classes.push("portrait-tall:".concat(mobile || '', " landscape:").concat(standard));
}
if (widescreen) {
classes.push("landscape-wide:".concat(widescreen));
}
return classes.join(' ');
};
// ========================================
// FLUID PRESETS (Các bộ class có sẵn)
// ========================================
var fluidPresets = {
// Container presets
container: {
narrow: createFluidContainer({ maxWidth: 'lg' }),
default: createFluidContainer(),
wide: createFluidContainer({ maxWidth: '2xl' }),
fullwidth: createFluidContainer({ maxWidth: 'full', padding: false }),
fluid: createFluidContainer({ maxWidth: 'fluid' }),
},
// Typography presets
typography: {
// Display text cho hero
hero: createDisplayText({ size: 'xl', weight: 'bold' }),
title: createDisplayText({ size: 'lg', weight: 'bold' }),
subtitle: createDisplayText({ size: 'md', weight: 'bold' }),
// Body text
heading: createFluidText({ size: '2xl', weight: 'bold' }),
subheading: createFluidText({ size: 'xl', weight: 'semibold' }),
body: createFluidText({ size: 'base' }),
caption: createFluidText({ size: 'sm' }),
small: createFluidText({ size: 'xs' }),
},
// Grid presets
grid: {
// Cards với auto-fit
cards: createFluidGrid({ type: 'auto-fit', minSize: 'lg', gap: 'md' }),
products: createFluidGrid({ type: 'auto-fit', minSize: 'md', gap: 'sm' }),
gallery: createFluidGrid({ type: 'auto-fit', minSize: 'sm', gap: 'xs', aspectRatio: 'square' }),
// Viewport-based grids
'viewport-cards': createViewportGrid({ minWidth: '25vw', gap: 'lg' }),
'viewport-products': createViewportGrid({ minWidth: '20vw', gap: 'md' }),
'viewport-gallery': createViewportGrid({ minWidth: '15vw', gap: 'sm' }),
},
// Spacing presets
spacing: {
section: createFluidSpacing({ size: 'lg', type: 'py', context: 'section' }),
container: createFluidSpacing({ size: 'md', type: 'px', context: 'container' }),
element: createFluidSpacing({ size: 'md', type: 'p', context: 'element' }),
// Margin presets
sectionMargin: createFluidSpacing({ size: 'xl', type: 'mb', context: 'section' }),
elementMargin: createFluidSpacing({ size: 'md', type: 'mb', context: 'element' }),
},
// Card presets
card: {
default: createFluidCard(),
compact: createFluidCard({ padding: 'sm' }),
spacious: createFluidCard({ padding: 'lg' }),
minimal: createFluidCard({ shadow: 'sm', border: true, hover: false }),
},
};
// ========================================
// UTILITY FUNCTIONS
// ========================================
var viewportUtils = {
// Check viewport width (client-side only)
getViewportWidth: function () {
if (typeof window === 'undefined')
return 1024;
return window.innerWidth;
},
// Check viewport height (client-side only)
getViewportHeight: function () {
if (typeof window === 'undefined')
return 768;
return window.innerHeight;
},
// Get viewport aspect ratio
getAspectRatio: function () {
if (typeof window === 'undefined')
return 16 / 9;
return window.innerWidth / window.innerHeight;
},
// Check if viewport is wide (≥16:9)
isWidescreen: function () {
return viewportUtils.getAspectRatio() >= 16 / 9;
},
// Get device type based on viewport
getDeviceType: function () {
var width = viewportUtils.getViewportWidth();
if (width < 768)
return 'mobile';
if (width < 1024)
return 'tablet';
return 'desktop';
},
// Generate media query string
mediaQuery: function (breakpoint) {
return "@media (min-width: ".concat(FLUID_BREAKPOINTS[breakpoint], ")");
},
};
// ========================================
// BACKWARD COMPATIBILITY (Giữ lại cho code cũ)
// ===============================