@skalfa/skalfa-component 1.0.26 → 1.0.28

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.
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.TextareaComponent = TextareaComponent;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ const skalfa_icon_1 = require("@skalfa/skalfa-icon");
8
+ const _utils_1 = require("@utils");
9
+ function TextareaComponent({ label, tip, leftIcon, rightIcon, className = "", value, invalid, validations, onlyAlphabet, uppercase, lowercase, register, unregister, onChange, ref, ...props }) {
10
+ // =========================>
11
+ // ## Initial
12
+ // =========================>
13
+ const inputHandler = (0, _utils_1.useInputHandler)(props.name, value, validations, register, false, unregister);
14
+ const randomId = (0, _utils_1.useInputRandomId)();
15
+ // =========================>
16
+ // ## Invalid handler
17
+ // =========================>
18
+ const [invalidMessage] = (0, _utils_1.useValidation)(inputHandler.value, validations, invalid, inputHandler.idle);
19
+ // =========================>
20
+ // ## Change value handler
21
+ // =========================>
22
+ (0, react_1.useEffect)(() => {
23
+ if (inputHandler.value && typeof inputHandler.value === "string") {
24
+ let newVal = onlyAlphabet ? inputHandler.value.replace(/[^A-Za-z ]+/g, "") : inputHandler.value;
25
+ if (uppercase)
26
+ newVal = newVal.toUpperCase();
27
+ if (lowercase)
28
+ newVal = newVal.toLowerCase();
29
+ if (validations && _utils_1.validation.hasRules(validations, "max")) {
30
+ newVal = newVal.slice(0, parseInt(_utils_1.validation.getRules(validations, "max") || "0"));
31
+ }
32
+ inputHandler.setValue(newVal);
33
+ }
34
+ }, [inputHandler.value, onlyAlphabet, uppercase, lowercase, validations]);
35
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "relative flex flex-col gap-y-0.5 w-full", children: [label && ((0, jsx_runtime_1.jsxs)("label", { htmlFor: randomId, className: (0, _utils_1.cn)("input-label", props.disabled && "input-label-disabled", inputHandler.focus && "input-label-focus", !!invalidMessage && "input-label-error", (0, _utils_1.pcn)(className, "label"), props.disabled && (0, _utils_1.pcn)(className, "label", "disabled"), inputHandler.focus && (0, _utils_1.pcn)(className, "label", "focus"), !!invalidMessage && (0, _utils_1.pcn)(className, "label", "error")), children: [label, validations && _utils_1.validation.hasRules(validations, "required") && (0, jsx_runtime_1.jsx)("span", { className: "text-danger ml-1", children: "*" })] })), tip && ((0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-tip", props.disabled && "input-tip-disabled", (0, _utils_1.pcn)(className, "tip"), props.disabled && (0, _utils_1.pcn)(className, "tip", "disabled")), children: tip })), (0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("textarea", { ...props, ref: ref, id: randomId, value: inputHandler.value ?? "", onChange: (e) => {
36
+ inputHandler.setValue(e.target.value);
37
+ inputHandler.setIdle(false);
38
+ if (onChange)
39
+ onChange(e.target.value);
40
+ }, onFocus: (e) => {
41
+ props.onFocus?.(e);
42
+ inputHandler.setFocus(true);
43
+ }, onBlur: (e) => {
44
+ props.onBlur?.(e);
45
+ setTimeout(() => inputHandler.setFocus(false), 100);
46
+ }, className: (0, _utils_1.cn)("input textarea min-h-[80px] py-2", leftIcon && "input-with-left-icon", rightIcon && "input-with-right-icon", props.disabled && "input-disabled", !!invalidMessage && "input-error", (0, _utils_1.pcn)(className, "base"), !!invalidMessage && (0, _utils_1.pcn)(className, "base", "error")) }), leftIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-left", "top-3 -translate-y-0", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon"), props.disabled && (0, _utils_1.pcn)(className, "icon", "disabled"), inputHandler.focus && (0, _utils_1.pcn)(className, "icon", "focus")), icon: leftIcon })), rightIcon && ((0, jsx_runtime_1.jsx)(skalfa_icon_1.Icon, { className: (0, _utils_1.cn)("input-icon", "input-icon-right", "top-3 -translate-y-0", props.disabled && "input-icon-disabled", inputHandler.focus && "input-icon-focus", (0, _utils_1.pcn)(className, "icon"), props.disabled && (0, _utils_1.pcn)(className, "icon", "disabled"), inputHandler.focus && (0, _utils_1.pcn)(className, "icon", "focus")), icon: rightIcon }))] }), invalidMessage && ((0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-error-message", (0, _utils_1.pcn)(className, "error")), children: invalidMessage }))] }));
47
+ }
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from "react";
2
2
  import { ApiType, FormErrorType, FormRegisterType, FormValueType, ValidationRules, DBSchema } from "@utils";
3
- import { InputProps, InputCheckboxProps, InputCurrencyProps, InputDateProps, InputNumberProps, InputRadioProps, SelectProps, InputPasswordProps, InputOtpProps, InputTimeProps, InputImageProps, InputDateTimeProps } from "../";
3
+ import { InputProps, InputCheckboxProps, InputCurrencyProps, InputDateProps, InputNumberProps, InputRadioProps, SelectProps, InputPasswordProps, InputOtpProps, InputTimeProps, InputImageProps, InputDateTimeProps, TextareaProps, InputContentProps } from "../";
4
4
  type formCustomConstructionProps = ({ formControl, values, setValues, setRegister, errors, setErrors, }: {
5
5
  formControl: (name: string) => {
6
6
  register: (regName: string, regValidations?: ValidationRules | undefined) => void;
@@ -42,6 +42,8 @@ type ConstructionMap = {
42
42
  select: SelectProps;
43
43
  "enter-password": InputPasswordProps;
44
44
  otp: InputOtpProps;
45
+ textarea: TextareaProps;
46
+ content: InputContentProps;
45
47
  custom: formCustomConstructionProps;
46
48
  };
47
49
  type TypeKeys = keyof ConstructionMap;
@@ -181,6 +181,8 @@ function FormSupervisionComponent({ title, fields, submitControl, confirmation,
181
181
  "enter-password": __1.InputPasswordComponent,
182
182
  otp: __1.InputOtpComponent,
183
183
  image: __1.InputImageComponent,
184
+ textarea: __1.TextareaComponent,
185
+ content: __1.InputContentComponent,
184
186
  cluster: () => null,
185
187
  custom: () => null,
186
188
  };
@@ -235,7 +237,7 @@ function FormSupervisionComponent({ title, fields, submitControl, confirmation,
235
237
  setValues(updatedValues);
236
238
  };
237
239
  const clusterError = errors?.find((err) => err.name === groupKey || err.name === mapName)?.error;
238
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("flex flex-col gap-4", generateColClass(form.col || "12")), children: [label && (0, jsx_runtime_1.jsx)("p", { className: "input-label", children: label }), clusterError && (0, jsx_runtime_1.jsx)("small", { className: "input-error-message", children: clusterError }), group.map((gIndex) => ((0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("relative pr-8", wrap && "p-4 rounded border", className), children: [label && (0, jsx_runtime_1.jsxs)("p", { className: "input-label", children: [label, " ", gIndex + 1] }), tip && (0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-tip"), children: tip }), (label || tip) && (0, jsx_runtime_1.jsx)("div", { className: "mb-2" }), (0, jsx_runtime_1.jsx)("div", { className: "w-full grid grid-cols-12 gap-4", children: innerForms.map((inner, i) => renderInput(inner, i, `${groupKey}[${gIndex}]`)) }), showDeleteButton && ((0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/times", paint: "danger", variant: "outline", size: "xs", className: (0, _utils_1.cn)("absolute top-10 right-2 translate-x-[50%] -translate-y-[50%]", wrap && "translate-x-0 -translate-y-0 top-1 right-1"), onClick: () => removeGroup(gIndex) }))] }, gIndex))), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/plus", label: `${l.base.add ? l.base.add() : "Add"} ${label || mapName}`, variant: "outline", size: "sm", onClick: addGroup }) })] }, key));
240
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("flex flex-col gap-4", generateColClass(form.col || "12")), children: [label && (0, jsx_runtime_1.jsx)("p", { className: "input-label", children: label }), clusterError && (0, jsx_runtime_1.jsx)("small", { className: "input-error-message", children: clusterError }), group.map((gIndex) => ((0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("relative pr-8", wrap && "form-supervision-cluster-item-wrapped", className), children: [label && (0, jsx_runtime_1.jsxs)("p", { className: "input-label", children: [label, " ", gIndex + 1] }), tip && (0, jsx_runtime_1.jsx)("small", { className: (0, _utils_1.cn)("input-tip"), children: tip }), (label || tip) && (0, jsx_runtime_1.jsx)("div", { className: "mb-2" }), (0, jsx_runtime_1.jsx)("div", { className: "w-full grid grid-cols-12 gap-4", children: innerForms.map((inner, i) => renderInput(inner, i, `${groupKey}[${gIndex}]`)) }), showDeleteButton && ((0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/times", paint: "danger", variant: "outline", size: "xs", className: (0, _utils_1.cn)("form-supervision-cluster-remove-btn", wrap && "form-supervision-cluster-remove-btn-wrapped"), onClick: () => removeGroup(gIndex) }))] }, gIndex))), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(__1.ButtonComponent, { icon: "solid/plus", label: `${l.base.add ? l.base.add() : "Add"} ${label || "Lainnya"}`, variant: "outline", size: "sm", onClick: addGroup }) })] }, key));
239
241
  }
240
242
  if (inputType === "custom") {
241
243
  const customRender = form.construction;
@@ -0,0 +1,13 @@
1
+ export interface ContentWrapperProps {
2
+ content: string;
3
+ className?: string;
4
+ }
5
+ export declare const COLOR_MAP: Record<string, {
6
+ label: string;
7
+ tw: string;
8
+ css: string;
9
+ }>;
10
+ export declare function parseInlineFormats(text: string): string;
11
+ export declare function parseContentToHtml(content: string): string;
12
+ export declare function parseHtmlToContent(html: string): string;
13
+ export declare function ContentWrapperComponent({ content, className, }: ContentWrapperProps): import("@types/react").JSX.Element;
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.COLOR_MAP = void 0;
4
+ exports.parseInlineFormats = parseInlineFormats;
5
+ exports.parseContentToHtml = parseContentToHtml;
6
+ exports.parseHtmlToContent = parseHtmlToContent;
7
+ exports.ContentWrapperComponent = ContentWrapperComponent;
8
+ const jsx_runtime_1 = require("react/jsx-runtime");
9
+ const react_1 = require("react");
10
+ const _utils_1 = require("@utils");
11
+ exports.COLOR_MAP = {
12
+ normal: { label: "Normal", tw: "text-foreground", css: "var(--color-foreground, #1f2937)" },
13
+ light: { label: "Light", tw: "text-light-foreground", css: "var(--color-light-foreground, #9ca3af)" },
14
+ primary: { label: "Primary", tw: "text-primary", css: "var(--color-primary, #3b82f6)" },
15
+ secondary: { label: "Secondary", tw: "text-secondary", css: "var(--color-secondary, #8b5cf6)" },
16
+ warning: { label: "Warning", tw: "text-warning", css: "var(--color-warning, #f59e0b)" },
17
+ danger: { label: "Danger", tw: "text-danger", css: "var(--color-danger, #ef4444)" },
18
+ };
19
+ function parseInlineFormats(text) {
20
+ if (!text)
21
+ return "";
22
+ let result = text;
23
+ result = result.replace(/\[color:(\w+)\](.*?)\[color\]/g, (_, color, content) => {
24
+ const colorInfo = exports.COLOR_MAP[color];
25
+ const twClass = colorInfo?.tw || "text-foreground";
26
+ return `<span class="${twClass}" data-color="${color}">${content}</span>`;
27
+ });
28
+ result = result.replace(/\[size:(\d+)\](.*?)\[size\]/g, (_, size, content) => {
29
+ return `<span style="font-size:${size}px" data-size="${size}">${content}</span>`;
30
+ });
31
+ result = result.replace(/\[link:(.*?)\](.*?)\[link\]/g, (_, url, content) => {
32
+ return `<a href="${url}" class="text-primary underline" data-link="${url}" target="_blank" rel="noopener noreferrer">${content}</a>`;
33
+ });
34
+ result = result.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");
35
+ result = result.replace(/\/\/(.*?)\/\//g, "<em>$1</em>");
36
+ result = result.replace(/__(.*?)__/g, "<u>$1</u>");
37
+ result = result.replace(/--(.*?)--/g, "<s>$1</s>");
38
+ return result;
39
+ }
40
+ function parseContentToHtml(content) {
41
+ if (!content)
42
+ return "";
43
+ const lines = content.split("\n");
44
+ const htmlParts = [];
45
+ let i = 0;
46
+ while (i < lines.length) {
47
+ const line = lines[i];
48
+ if (line.trim() === "---") {
49
+ htmlParts.push('<div class="skcontent-divider" contenteditable="false"><hr /></div>');
50
+ i++;
51
+ continue;
52
+ }
53
+ const headerMatch = line.match(/^##(.+?)##$/);
54
+ if (headerMatch) {
55
+ htmlParts.push(`<h2>${parseInlineFormats(headerMatch[1])}</h2>`);
56
+ i++;
57
+ continue;
58
+ }
59
+ if (line.match(/^\[list:bullet\]/)) {
60
+ const items = [];
61
+ while (i < lines.length) {
62
+ const bm = lines[i].match(/^\[list:bullet\](.*?)\[list\]$/);
63
+ if (!bm)
64
+ break;
65
+ items.push(`<li>${parseInlineFormats(bm[1])}</li>`);
66
+ i++;
67
+ }
68
+ htmlParts.push(`<ul>${items.join("")}</ul>`);
69
+ continue;
70
+ }
71
+ if (line.match(/^\[list:number\]/)) {
72
+ const items = [];
73
+ while (i < lines.length) {
74
+ const nm = lines[i].match(/^\[list:number\](.*?)\[list\]$/);
75
+ if (!nm)
76
+ break;
77
+ items.push(`<li>${parseInlineFormats(nm[1])}</li>`);
78
+ i++;
79
+ }
80
+ htmlParts.push(`<ol>${items.join("")}</ol>`);
81
+ continue;
82
+ }
83
+ const alignMatch = line.match(/^\[align:(left|center|right|justify)\](.*?)\[align\]$/);
84
+ if (alignMatch) {
85
+ const alignVal = alignMatch[1];
86
+ const alignStyle = alignVal === "left" ? "text-align:left" : alignVal === "center" ? "text-align:center" : alignVal === "right" ? "text-align:right" : "text-align:justify";
87
+ htmlParts.push(`<p style="${alignStyle}" class="text-${alignVal}">${parseInlineFormats(alignMatch[2])}</p>`);
88
+ i++;
89
+ continue;
90
+ }
91
+ if (line.trim() === "") {
92
+ htmlParts.push("<p><br></p>");
93
+ i++;
94
+ continue;
95
+ }
96
+ htmlParts.push(`<p>${parseInlineFormats(line)}</p>`);
97
+ i++;
98
+ }
99
+ return htmlParts.join("");
100
+ }
101
+ function parseHtmlToContent(html) {
102
+ if (!html)
103
+ return "";
104
+ const parser = new DOMParser();
105
+ const doc = parser.parseFromString(`<div>${html}</div>`, "text/html");
106
+ const root = doc.body.firstElementChild;
107
+ if (!root)
108
+ return "";
109
+ const lines = [];
110
+ for (let i = 0; i < root.childNodes.length; i++) {
111
+ const node = root.childNodes[i];
112
+ if (node.nodeType === Node.TEXT_NODE) {
113
+ const text = node.textContent?.trim();
114
+ if (text)
115
+ lines.push(parseNodeToCustom(node));
116
+ continue;
117
+ }
118
+ if (node.nodeType !== Node.ELEMENT_NODE)
119
+ continue;
120
+ const el = node;
121
+ const tag = el.tagName.toLowerCase();
122
+ if (el.classList.contains("skcontent-divider") || tag === "hr") {
123
+ lines.push("---");
124
+ continue;
125
+ }
126
+ if (tag === "h2") {
127
+ lines.push(`##${parseChildrenToCustom(el)}##`);
128
+ continue;
129
+ }
130
+ if (tag === "ul") {
131
+ for (let j = 0; j < el.children.length; j++) {
132
+ const li = el.children[j];
133
+ lines.push(`[list:bullet]${parseChildrenToCustom(li)}[list]`);
134
+ }
135
+ continue;
136
+ }
137
+ if (tag === "ol") {
138
+ for (let j = 0; j < el.children.length; j++) {
139
+ const li = el.children[j];
140
+ lines.push(`[list:number]${parseChildrenToCustom(li)}[list]`);
141
+ }
142
+ continue;
143
+ }
144
+ if (tag === "p" || tag === "div") {
145
+ const align = el.style.textAlign || (el.classList.contains("text-center") ? "center" : el.classList.contains("text-right") ? "right" : el.classList.contains("text-justify") ? "justify" : "");
146
+ const content = parseChildrenToCustom(el);
147
+ if (!content || content === "\n" || el.innerHTML === "<br>" || el.innerHTML === "<br/>") {
148
+ lines.push("");
149
+ continue;
150
+ }
151
+ if (align && align !== "left" && align !== "start") {
152
+ lines.push(`[align:${align}]${content}[align]`);
153
+ }
154
+ else {
155
+ lines.push(content);
156
+ }
157
+ continue;
158
+ }
159
+ const fallback = parseChildrenToCustom(el);
160
+ if (fallback)
161
+ lines.push(fallback);
162
+ }
163
+ return lines.join("\n");
164
+ }
165
+ function parseChildrenToCustom(el) {
166
+ let result = "";
167
+ for (let i = 0; i < el.childNodes.length; i++) {
168
+ result += parseNodeToCustom(el.childNodes[i]);
169
+ }
170
+ return result;
171
+ }
172
+ function parseNodeToCustom(node) {
173
+ if (node.nodeType === Node.TEXT_NODE) {
174
+ return (node.textContent || "").replace(/\u200B/g, "");
175
+ }
176
+ if (node.nodeType !== Node.ELEMENT_NODE)
177
+ return "";
178
+ const el = node;
179
+ const tag = el.tagName.toLowerCase();
180
+ const inner = parseChildrenToCustom(el);
181
+ if (tag === "strong" || tag === "b")
182
+ return `**${inner}**`;
183
+ if (tag === "em" || tag === "i")
184
+ return `//${inner}//`;
185
+ if (tag === "u")
186
+ return `__${inner}__`;
187
+ if (tag === "s" || tag === "strike" || tag === "del")
188
+ return `--${inner}--`;
189
+ if (tag === "span" && el.dataset.color) {
190
+ return `[color:${el.dataset.color}]${inner}[color]`;
191
+ }
192
+ if (tag === "span" && el.dataset.size) {
193
+ return `[size:${el.dataset.size}]${inner}[size]`;
194
+ }
195
+ if (tag === "a") {
196
+ const href = el.getAttribute("href") || el.dataset.link || "";
197
+ return `[link:${href}]${inner}[link]`;
198
+ }
199
+ if (tag === "br")
200
+ return "";
201
+ if (tag === "hr")
202
+ return "---";
203
+ return inner;
204
+ }
205
+ function ContentWrapperComponent({ content, className, }) {
206
+ const renderedHtml = (0, react_1.useMemo)(() => parseContentToHtml(content), [content]);
207
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, _utils_1.cn)("skcontent-wrapper", className), dangerouslySetInnerHTML: { __html: renderedHtml } }));
208
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skalfa/skalfa-component",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "Reusable UI components for Skalfa App.",
5
5
  "license": "MIT",
6
6
  "keywords": [
package/src/index.ts CHANGED
@@ -25,6 +25,8 @@ export * from "./input/InputTime.component";
25
25
  export * from "./input/InputValues.component";
26
26
  export * from "./input/Radio.component";
27
27
  export * from "./input/Select.component";
28
+ export * from "./input/Textarea.component";
29
+ export * from "./input/InputContent.component";
28
30
  export * from "./modal/BottomSheet.component";
29
31
  export * from "./modal/FloatingPage.component";
30
32
  export * from "./modal/Modal.component";
@@ -47,6 +49,7 @@ export * from "./typography/TypographyArticle.component";
47
49
  export * from "./typography/TypographyColumn.component";
48
50
  export * from "./typography/TypographyContent.component";
49
51
  export * from "./typography/TypographyTips.component";
52
+ export * from "./wrap/ContentWrapper.component";
50
53
  export * from "./wrap/Draggable.component";
51
54
  export * from "./wrap/Image.component";
52
55
  export * from "./wrap/OutsideClick.component";