@skalfa/skalfa-component 1.0.33 → 1.0.35

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.
@@ -458,6 +458,68 @@ function InputContentComponent({ label, tip, className = "", value, invalid, val
458
458
  }
459
459
  }
460
460
  }, [handleBold, handleItalic, handleUnderline, handleStrikethrough, handleLink, handleEditorChange, updateActiveStates]);
461
+ const handlePaste = (0, react_1.useCallback)((e) => {
462
+ const pastedText = e.clipboardData.getData("text/plain");
463
+ if (!pastedText)
464
+ return;
465
+ const urlRegex = /(\b(?:https?:\/\/|www\.)[^\s()<>]+(?:\([\w\d]+\)|[^.,?;:"')\s]))/gi;
466
+ if (urlRegex.test(pastedText)) {
467
+ e.preventDefault();
468
+ const escapeHtml = (text) => {
469
+ return text
470
+ .replace(/&/g, "&amp;")
471
+ .replace(/</g, "&lt;")
472
+ .replace(/>/g, "&gt;")
473
+ .replace(/"/g, "&quot;")
474
+ .replace(/'/g, "&#039;");
475
+ };
476
+ const tokens = [];
477
+ let lastIndex = 0;
478
+ let match;
479
+ urlRegex.lastIndex = 0;
480
+ while ((match = urlRegex.exec(pastedText)) !== null) {
481
+ if (match.index > lastIndex) {
482
+ tokens.push(escapeHtml(pastedText.substring(lastIndex, match.index)));
483
+ }
484
+ const url = match[0];
485
+ let href = url;
486
+ if (url.toLowerCase().startsWith("www.")) {
487
+ href = "https://" + url;
488
+ }
489
+ const escapedHref = escapeHtml(href);
490
+ const escapedUrl = escapeHtml(url);
491
+ tokens.push(`<a href="${escapedHref}" class="text-primary underline" data-link="${escapedHref}" target="_blank" rel="noopener">${escapedUrl}</a>`);
492
+ lastIndex = urlRegex.lastIndex;
493
+ }
494
+ if (lastIndex < pastedText.length) {
495
+ tokens.push(escapeHtml(pastedText.substring(lastIndex)));
496
+ }
497
+ const htmlContent = tokens.join("").replace(/\n/g, "<br>");
498
+ const sel = window.getSelection();
499
+ if (!sel || sel.rangeCount === 0)
500
+ return;
501
+ const range = sel.getRangeAt(0);
502
+ range.deleteContents();
503
+ const tempDiv = document.createElement("div");
504
+ tempDiv.innerHTML = htmlContent;
505
+ const fragment = document.createDocumentFragment();
506
+ let child;
507
+ while ((child = tempDiv.firstChild)) {
508
+ fragment.appendChild(child);
509
+ }
510
+ const lastNode = fragment.lastChild;
511
+ range.insertNode(fragment);
512
+ if (lastNode) {
513
+ const newRange = document.createRange();
514
+ newRange.setStartAfter(lastNode);
515
+ newRange.collapse(true);
516
+ sel.removeAllRanges();
517
+ sel.addRange(newRange);
518
+ }
519
+ saveSelection();
520
+ handleEditorChange();
521
+ }
522
+ }, [saveSelection, handleEditorChange]);
461
523
  const renderControlItem = (0, react_1.useCallback)((item, index) => {
462
524
  if (typeof item !== "string") {
463
525
  return (0, jsx_runtime_1.jsx)("div", { children: item }, index);
@@ -555,7 +617,7 @@ function InputContentComponent({ label, tip, className = "", value, invalid, val
555
617
  saveSelection();
556
618
  handleEditorChange();
557
619
  updateActiveStates();
558
- }, onKeyDown: handleKeyDown, onMouseUp: () => {
620
+ }, onKeyDown: handleKeyDown, onPaste: handlePaste, onMouseUp: () => {
559
621
  saveSelection();
560
622
  updateActiveStates();
561
623
  }, onKeyUp: () => {
@@ -58,11 +58,12 @@ export type TableSupervisionProps = {
58
58
  description: string;
59
59
  };
60
60
  on?: (item: Record<string, any>) => boolean;
61
- } | ((row: object, setModal?: (type: "EDIT" | "DELETE") => void, setDataSelected?: () => void, reset?: () => void, size?: string) => ReactNode))[];
61
+ } | ((row: Record<string, any>, setModal?: (type: "EDIT" | "DELETE") => void, setDataSelected?: () => void, reset?: () => void, size?: string) => ReactNode))[];
62
62
  block?: boolean;
63
63
  noIndex?: boolean;
64
64
  actionBulkingControl?: TableProps["actionBulking"];
65
65
  controlBar?: (ControlBarOptionType | "CREATE" | "IMPORT" | "EXPORT" | "PRINT")[];
66
+ filterBar?: ReactNode;
66
67
  responsiveControl?: {
67
68
  mobile?: boolean | {
68
69
  item?: (item: Record<string, any>, key: number) => ReactNode;
@@ -75,4 +76,4 @@ export type TableSupervisionProps = {
75
76
  };
76
77
  };
77
78
  };
78
- export declare function TableSupervisionComponent({ title, id, fetchControl, columnControl, formControl, onRowClick, detailControl, actionControl, actionBulkingControl, block, controlBar, noIndex, responsiveControl, urlParam, }: TableSupervisionProps): import("@types/react").JSX.Element;
79
+ export declare function TableSupervisionComponent({ title, id, fetchControl, columnControl, formControl, onRowClick, detailControl, actionControl, actionBulkingControl, block, controlBar, filterBar, noIndex, responsiveControl, urlParam, }: TableSupervisionProps): import("@types/react").JSX.Element;
@@ -12,7 +12,7 @@ const ExportExcel = _utils_1.registry.get("ExportExcel");
12
12
  const ImportExcel = _utils_1.registry.get("ImportExcel");
13
13
  ;
14
14
  ;
15
- function TableSupervisionComponent({ title, id, fetchControl, columnControl, formControl, onRowClick, detailControl, actionControl, actionBulkingControl, block, controlBar, noIndex, responsiveControl, urlParam, }) {
15
+ function TableSupervisionComponent({ title, id, fetchControl, columnControl, formControl, onRowClick, detailControl, actionControl, actionBulkingControl, block, controlBar, filterBar, noIndex, responsiveControl, urlParam, }) {
16
16
  const l = (0, skalfa_lang_1.useLang)();
17
17
  const { tableKey, tableControl, data, selected, setSelected, checks, setChecks, reset, focus, setFocus } = (0, _utils_1.useTable)(fetchControl, id, title, (urlParam || true));
18
18
  const { setToggle, toggle } = (0, _contexts_1.useToggleContext)();
@@ -243,7 +243,7 @@ function TableSupervisionComponent({ title, id, fetchControl, columnControl, for
243
243
  ...(columns?.filter((c) => !!c.filterable)?.length ? ["FILTER"] : []),
244
244
  ...(columns?.filter((c) => !!c.sortable)?.length ? ["SORT"] : []),
245
245
  "SELECTABLE", "REFRESH",
246
- ], columns: columns, data: dataTables, onRowClick: onRowClick ? onRowClick : detailControl != false ? (e) => {
246
+ ], filterBar: filterBar, columns: columns, data: dataTables, onRowClick: onRowClick ? onRowClick : detailControl != false ? (e) => {
247
247
  setToggle(`MODAL_SHOW_${toggleKey}`);
248
248
  setSelected(e);
249
249
  } : undefined, actionBulking: actionBulkingControl, checks: checks || [], onChangeChecks: (e) => setChecks(e), block: block, focus: focus, noIndex: noIndex, responsiveControl: responsiveControl ? {
@@ -26,6 +26,7 @@ export interface TableColumnType {
26
26
  export interface TableProps {
27
27
  id?: string;
28
28
  controlBar?: false | ControlBarOptionType[];
29
+ filterBar?: ReactNode;
29
30
  columns: TableColumnType[];
30
31
  data: Record<string, any>[];
31
32
  pagination?: PaginationProps | false;
@@ -61,4 +62,4 @@ export interface TableProps {
61
62
  /** Use custom class with: "controller-bar::", "head-column::", "column::", "floating-action::", "row::". */
62
63
  className?: string;
63
64
  }
64
- export declare function TableComponent({ id, controlBar, columns, data, pagination, loading, sortBy, onChangeSortBy, search, onChangeSearch, searchableColumn, onChangeSearchableColumn, filter, onChangeFilter, checks, onChangeChecks, actionBulking, focus, onRowClick, onRefresh, block, noIndex, responsiveControl, className, }: TableProps): import("@types/react").JSX.Element;
65
+ export declare function TableComponent({ id, controlBar, filterBar, columns, data, pagination, loading, sortBy, onChangeSortBy, search, onChangeSearch, searchableColumn, onChangeSearchableColumn, filter, onChangeFilter, checks, onChangeChecks, actionBulking, focus, onRowClick, onRefresh, block, noIndex, responsiveControl, className, }: TableProps): import("@types/react").JSX.Element;
@@ -12,7 +12,7 @@ const ScrollContainer_component_1 = require("../wrap/ScrollContainer.component")
12
12
  const Checkbox_component_1 = require("../input/Checkbox.component");
13
13
  const Swipe_component_1 = require("../wrap/Swipe.component");
14
14
  const skalfa_lang_1 = require("@skalfa/skalfa-lang");
15
- function TableComponent({ id, controlBar, columns, data, pagination, loading, sortBy, onChangeSortBy, search, onChangeSearch, searchableColumn, onChangeSearchableColumn, filter, onChangeFilter, checks, onChangeChecks, actionBulking, focus, onRowClick, onRefresh, block, noIndex, responsiveControl, className = "", }) {
15
+ function TableComponent({ id, controlBar, filterBar, columns, data, pagination, loading, sortBy, onChangeSortBy, search, onChangeSearch, searchableColumn, onChangeSearchableColumn, filter, onChangeFilter, checks, onChangeChecks, actionBulking, focus, onRowClick, onRefresh, block, noIndex, responsiveControl, className = "", }) {
16
16
  const l = (0, skalfa_lang_1.useLang)();
17
17
  const [displayColumns, setDisplayColumns] = (0, react_1.useState)([]);
18
18
  const [showFloatingAction, setShowFloatingAction] = (0, react_1.useState)(false);
@@ -97,7 +97,7 @@ function TableComponent({ id, controlBar, columns, data, pagination, loading, so
97
97
  selector: c.selector,
98
98
  type: typeof c?.filterable == "object" ? c?.filterable?.type : "text",
99
99
  options: typeof c?.filterable == "object" && c?.filterable?.type == "select" ? c?.filterable?.options : undefined
100
- })), onFilter: (filters) => onChangeFilter?.(filters), filter: filter, className: (0, _utils_1.pcn)(className, "controller-bar") || "" })), (0, jsx_runtime_1.jsx)("div", { className: "relative", children: (0, jsx_runtime_1.jsx)(ScrollContainer_component_1.ScrollContainerComponent, { scrollFloating: !isSm && block, className: `w-full ${block ? "pb-12" : ""}`, onScroll: (e) => {
100
+ })), onFilter: (filters) => onChangeFilter?.(filters), filter: filter, className: (0, _utils_1.pcn)(className, "controller-bar") || "" })), filterBar, (0, jsx_runtime_1.jsx)("div", { className: "relative", children: (0, jsx_runtime_1.jsx)(ScrollContainer_component_1.ScrollContainerComponent, { scrollFloating: !isSm && block, className: `w-full ${block ? "pb-12" : ""}`, onScroll: (e) => {
101
101
  actionColumnRef.current?.clientWidth && e.scrollLeft &&
102
102
  setShowFloatingAction(e.scrollLeft + e.clientWidth <= e.scrollWidth - actionColumnRef.current?.clientWidth);
103
103
  }, footer: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: block && pagination && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: "py-6" }), (0, jsx_runtime_1.jsx)("div", { className: "my-2 absolute bottom-0 w-full", children: (0, jsx_runtime_1.jsx)(Pagination_component_1.PaginationComponent, { ...pagination }) })] })) }), children: loading ? ((0, jsx_runtime_1.jsx)("div", { className: "w-max min-w-full", children: (0, jsx_runtime_1.jsx)("div", { className: "table-loading-container", children: (0, jsx_runtime_1.jsx)("h1", { className: "table-loading-text", children: l.base.tableLoading ? l.base.tableLoading() : "" }) }) })) : !data || !data.length ? ((0, jsx_runtime_1.jsx)("div", { className: "table-empty-container", children: (0, jsx_runtime_1.jsx)("h1", { className: "table-empty-text", children: l.base.tableEmpty ? l.base.tableEmpty() : "Data empty" }) })) : ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: !isSm || !responsiveControl?.mobile ? ((0, jsx_runtime_1.jsxs)("div", { className: "w-max min-w-full", children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, _utils_1.cn)("table-head-row", (0, _utils_1.pcn)(className, "row")), children: [!!actionBulking && ((0, jsx_runtime_1.jsx)("div", { className: "table-head-column w-max", children: (0, jsx_runtime_1.jsx)(Checkbox_component_1.CheckboxComponent, { name: "selected_table", className: "w-5 h-5", checked: data.length > 0 && checks?.length === data.length, onChange: () => data.length > 0 && checks?.length === data.length ? onChangeChecks?.([]) : onChangeChecks?.(data.map((d) => d.id)) }) })), !noIndex && (0, jsx_runtime_1.jsx)("div", { className: (0, _utils_1.cn)("table-head-column w-8", (0, _utils_1.pcn)(className, "head-column")), children: "#" }), renderHead()] }), (0, jsx_runtime_1.jsx)("div", { className: "table-body", children: data.map((item, key) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skalfa/skalfa-component",
3
- "version": "1.0.33",
3
+ "version": "1.0.35",
4
4
  "description": "Reusable UI components for Skalfa App.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -574,6 +574,83 @@ export function InputContentComponent({
574
574
  }
575
575
  }, [handleBold, handleItalic, handleUnderline, handleStrikethrough, handleLink, handleEditorChange, updateActiveStates]);
576
576
 
577
+ const handlePaste = useCallback((e: React.ClipboardEvent<HTMLDivElement>) => {
578
+ const pastedText = e.clipboardData.getData("text/plain");
579
+ if (!pastedText) return;
580
+
581
+ const urlRegex = /(\b(?:https?:\/\/|www\.)[^\s()<>]+(?:\([\w\d]+\)|[^.,?;:"')\s]))/gi;
582
+
583
+ if (urlRegex.test(pastedText)) {
584
+ e.preventDefault();
585
+
586
+ const escapeHtml = (text: string) => {
587
+ return text
588
+ .replace(/&/g, "&amp;")
589
+ .replace(/</g, "&lt;")
590
+ .replace(/>/g, "&gt;")
591
+ .replace(/"/g, "&quot;")
592
+ .replace(/'/g, "&#039;");
593
+ };
594
+
595
+ const tokens: string[] = [];
596
+ let lastIndex = 0;
597
+ let match;
598
+ urlRegex.lastIndex = 0;
599
+
600
+ while ((match = urlRegex.exec(pastedText)) !== null) {
601
+ if (match.index > lastIndex) {
602
+ tokens.push(escapeHtml(pastedText.substring(lastIndex, match.index)));
603
+ }
604
+
605
+ const url = match[0];
606
+ let href = url;
607
+ if (url.toLowerCase().startsWith("www.")) {
608
+ href = "https://" + url;
609
+ }
610
+
611
+ const escapedHref = escapeHtml(href);
612
+ const escapedUrl = escapeHtml(url);
613
+ tokens.push(`<a href="${escapedHref}" class="text-primary underline" data-link="${escapedHref}" target="_blank" rel="noopener">${escapedUrl}</a>`);
614
+
615
+ lastIndex = urlRegex.lastIndex;
616
+ }
617
+
618
+ if (lastIndex < pastedText.length) {
619
+ tokens.push(escapeHtml(pastedText.substring(lastIndex)));
620
+ }
621
+
622
+ const htmlContent = tokens.join("").replace(/\n/g, "<br>");
623
+
624
+ const sel = window.getSelection();
625
+ if (!sel || sel.rangeCount === 0) return;
626
+ const range = sel.getRangeAt(0);
627
+ range.deleteContents();
628
+
629
+ const tempDiv = document.createElement("div");
630
+ tempDiv.innerHTML = htmlContent;
631
+
632
+ const fragment = document.createDocumentFragment();
633
+ let child: Node | null;
634
+ while ((child = tempDiv.firstChild)) {
635
+ fragment.appendChild(child);
636
+ }
637
+
638
+ const lastNode = fragment.lastChild;
639
+ range.insertNode(fragment);
640
+
641
+ if (lastNode) {
642
+ const newRange = document.createRange();
643
+ newRange.setStartAfter(lastNode);
644
+ newRange.collapse(true);
645
+ sel.removeAllRanges();
646
+ sel.addRange(newRange);
647
+ }
648
+
649
+ saveSelection();
650
+ handleEditorChange();
651
+ }
652
+ }, [saveSelection, handleEditorChange]);
653
+
577
654
  const renderControlItem = useCallback((item: ToolbarControlItem, index: number) => {
578
655
  if (typeof item !== "string") {
579
656
  return <div key={index}>{item}</div>;
@@ -945,6 +1022,7 @@ export function InputContentComponent({
945
1022
  updateActiveStates();
946
1023
  }}
947
1024
  onKeyDown={handleKeyDown}
1025
+ onPaste={handlePaste}
948
1026
  onMouseUp={() => {
949
1027
  saveSelection();
950
1028
  updateActiveStates();
@@ -64,7 +64,7 @@ export type TableSupervisionProps = {
64
64
  shortcut ?: { key: string, description: string },
65
65
  on ?: (item: Record<string, any>) => boolean,
66
66
  } | ((
67
- row : object,
67
+ row : Record<string, any>,
68
68
  setModal ?: (type: "EDIT" | "DELETE") => void,
69
69
  setDataSelected ?: () => void,
70
70
  reset ?: () => void,
@@ -75,6 +75,7 @@ export type TableSupervisionProps = {
75
75
  noIndex ?: boolean;
76
76
  actionBulkingControl ?: TableProps["actionBulking"],
77
77
  controlBar ?: (ControlBarOptionType | "CREATE" | "IMPORT" | "EXPORT" | "PRINT")[];
78
+ filterBar ?: ReactNode;
78
79
  responsiveControl ?: {
79
80
  mobile ?: boolean | {
80
81
  item ?: (item: Record<string, any>, key: number) => ReactNode,
@@ -98,6 +99,7 @@ export function TableSupervisionComponent({
98
99
  actionBulkingControl,
99
100
  block,
100
101
  controlBar,
102
+ filterBar,
101
103
  noIndex,
102
104
  responsiveControl,
103
105
  urlParam,
@@ -500,6 +502,7 @@ export function TableSupervisionComponent({
500
502
  ...(columns?.filter((c) => !!(c as { sortable?: any }).sortable)?.length ? ["SORT"] : []),
501
503
  "SELECTABLE", "REFRESH",
502
504
  ]}
505
+ filterBar={filterBar}
503
506
  columns={columns as TableColumnType[]}
504
507
  data={dataTables}
505
508
  onRowClick={onRowClick ? onRowClick : detailControl != false ? (e) => {
@@ -36,6 +36,8 @@ export interface TableProps {
36
36
  id ?: string;
37
37
 
38
38
  controlBar ?: false | ControlBarOptionType[];
39
+ filterBar ?: ReactNode;
40
+
39
41
 
40
42
  columns : TableColumnType[];
41
43
  data : Record<string, any>[];
@@ -76,6 +78,7 @@ export interface TableProps {
76
78
  export function TableComponent({
77
79
  id,
78
80
  controlBar,
81
+ filterBar,
79
82
  columns,
80
83
  data,
81
84
  pagination,
@@ -290,6 +293,8 @@ export function TableComponent({
290
293
  />
291
294
  )}
292
295
 
296
+ {filterBar}
297
+
293
298
  <div className="relative">
294
299
  <ScrollContainerComponent
295
300
  scrollFloating={!isSm && block}