@seedgrid/fe-components 2026.11.3 → 2026.11.5

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.
Files changed (47) hide show
  1. package/dist/ai/seedgrid-components.manifest.json +101 -11
  2. package/dist/buttons/SgButton.d.ts +41 -3
  3. package/dist/buttons/SgButton.d.ts.map +1 -1
  4. package/dist/buttons/SgButton.js +51 -5
  5. package/dist/buttons/SgButton.meta.d.ts.map +1 -1
  6. package/dist/buttons/SgButton.meta.js +27 -5
  7. package/dist/gadgets/calendar/SgCalendar.d.ts +6 -0
  8. package/dist/gadgets/calendar/SgCalendar.d.ts.map +1 -1
  9. package/dist/gadgets/calendar/SgCalendar.js +9 -5
  10. package/dist/gadgets/calendar/SgCalendar.meta.d.ts.map +1 -1
  11. package/dist/gadgets/calendar/SgCalendar.meta.js +1 -0
  12. package/dist/gadgets/clock/SgClock.d.ts +25 -1
  13. package/dist/gadgets/clock/SgClock.d.ts.map +1 -1
  14. package/dist/gadgets/clock/SgClock.js +18 -3
  15. package/dist/gadgets/clock/SgClock.meta.d.ts.map +1 -1
  16. package/dist/gadgets/clock/SgClock.meta.js +2 -1
  17. package/dist/generated/compiledStyles.d.ts +1 -1
  18. package/dist/generated/compiledStyles.d.ts.map +1 -1
  19. package/dist/generated/compiledStyles.js +1 -1
  20. package/dist/i18n/SgComponentsI18nProvider.meta.d.ts.map +1 -1
  21. package/dist/i18n/SgComponentsI18nProvider.meta.js +6 -3
  22. package/dist/i18n/useComponentsI18n.meta.d.ts.map +1 -1
  23. package/dist/i18n/useComponentsI18n.meta.js +8 -2
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -1
  27. package/dist/inputs/SgDatatable.d.ts +7 -0
  28. package/dist/inputs/SgDatatable.d.ts.map +1 -1
  29. package/dist/inputs/SgDatatable.js +23 -11
  30. package/dist/inputs/SgDatatable.meta.d.ts.map +1 -1
  31. package/dist/inputs/SgDatatable.meta.js +1 -0
  32. package/dist/layout/SgToolBar.d.ts +6 -0
  33. package/dist/layout/SgToolBar.d.ts.map +1 -1
  34. package/dist/layout/SgToolBar.js +107 -5
  35. package/dist/layout/SgToolBar.meta.d.ts.map +1 -1
  36. package/dist/layout/SgToolBar.meta.js +8 -0
  37. package/dist/layout/toolbar-logic.d.ts +11 -0
  38. package/dist/layout/toolbar-logic.d.ts.map +1 -1
  39. package/dist/layout/toolbar-logic.js +25 -0
  40. package/dist/sandbox.cjs +55 -55
  41. package/dist/style.css +2 -2
  42. package/dist/wizard/SgWizard.d.ts +7 -0
  43. package/dist/wizard/SgWizard.d.ts.map +1 -1
  44. package/dist/wizard/SgWizard.js +11 -2
  45. package/dist/wizard/SgWizard.meta.d.ts.map +1 -1
  46. package/dist/wizard/SgWizard.meta.js +1 -0
  47. package/package.json +3 -3
@@ -226,7 +226,7 @@ function SgDatatableBase(props, imperativeRef) {
226
226
  const i18n = useComponentsI18n();
227
227
  const hasEnvironmentProvider = useHasSgEnvironmentProvider();
228
228
  const persistence = useSgPersistence();
229
- const { id, title, value, columns, dataKey, lazy = false, totalRecords: controlledTotalRecords, paginator = false, rows = 10, first, rowsPerPageOptions, onPage, infiniteScroll, onLoadMore, sortField, sortOrder, onSort, removableSort = true, selectionMode, selection, onSelectionChange, showGlobalFilter = false, globalFilter, globalFilterPlaceholder, onGlobalFilterChange, showColumnFilters = false, filters, onFilter, showClearFiltersButton = false, stripedRows = false, showGridlines = false, hoverableRows = true, loading = false, emptyMessage, className = "", style, tableClassName = "", rowClassName, groupBoxProps = {}, showColumnManager = true, columnManagerLabel, columnManagerMaxHeight = 320, mobileMode } = props;
229
+ const { id, title, value, columns, dataKey, lazy = false, totalRecords: controlledTotalRecords, paginator = false, rows = 10, first, rowsPerPageOptions, onPage, infiniteScroll, onLoadMore, sortField, sortOrder, onSort, removableSort = true, enabled = true, selectionMode, selection, onSelectionChange, showGlobalFilter = false, globalFilter, globalFilterPlaceholder, onGlobalFilterChange, showColumnFilters = false, filters, onFilter, showClearFiltersButton = false, stripedRows = false, showGridlines = false, hoverableRows = true, loading = false, emptyMessage, className = "", style, tableClassName = "", rowClassName, groupBoxProps = {}, showColumnManager = true, columnManagerLabel, columnManagerMaxHeight = 320, mobileMode } = props;
230
230
  const isFirstControlled = first !== undefined;
231
231
  const isSortFieldControlled = sortField !== undefined;
232
232
  const isSortOrderControlled = sortOrder !== undefined;
@@ -609,18 +609,24 @@ function SgDatatableBase(props, imperativeRef) {
609
609
  return () => observer.disconnect();
610
610
  }, [infiniteHasMore, loadMore, pagedRows.length]);
611
611
  const commitSort = React.useCallback((nextSortField, nextSortOrder) => {
612
+ if (!enabled)
613
+ return;
612
614
  if (!isSortFieldControlled)
613
615
  setInternalSortField(nextSortField);
614
616
  if (!isSortOrderControlled)
615
617
  setInternalSortOrder(nextSortOrder);
616
618
  onSort?.({ sortField: nextSortField, sortOrder: nextSortOrder });
617
- }, [isSortFieldControlled, isSortOrderControlled, onSort]);
619
+ }, [enabled, isSortFieldControlled, isSortOrderControlled, onSort]);
618
620
  const commitSelection = React.useCallback((nextSelection) => {
621
+ if (!enabled)
622
+ return;
619
623
  if (!isSelectionControlled)
620
624
  setInternalSelection(nextSelection);
621
625
  onSelectionChange?.(nextSelection);
622
- }, [isSelectionControlled, onSelectionChange]);
626
+ }, [enabled, isSelectionControlled, onSelectionChange]);
623
627
  const commitFilters = React.useCallback((nextFilters, nextGlobalFilter) => {
628
+ if (!enabled)
629
+ return;
624
630
  if (!isFiltersControlled)
625
631
  setInternalFilters(nextFilters);
626
632
  if (!isGlobalFilterControlled)
@@ -744,6 +750,8 @@ function SgDatatableBase(props, imperativeRef) {
744
750
  const showAllColumnsLabel = resolveMessage(t(i18n, "components.datatable.showAllColumns"), "components.datatable.showAllColumns", "Show all");
745
751
  const dragHandleLabel = resolveMessage(t(i18n, "components.datatable.dragToReorder"), "components.datatable.dragToReorder", "Drag to reorder");
746
752
  const toggleColumnVisibility = React.useCallback((columnKey, forceVisible) => {
753
+ if (!enabled)
754
+ return;
747
755
  setHiddenColumnsMap((prev) => {
748
756
  const isCurrentlyHidden = Boolean(prev[columnKey]);
749
757
  const willHide = forceVisible === undefined ? !isCurrentlyHidden : !forceVisible;
@@ -766,6 +774,8 @@ function SgDatatableBase(props, imperativeRef) {
766
774
  });
767
775
  }, []);
768
776
  const restoreDefaultColumns = React.useCallback(() => {
777
+ if (!enabled)
778
+ return;
769
779
  setColumnOrder(columnDescriptors.map((item) => item.key));
770
780
  setHiddenColumnsMap(columnDescriptors.reduce((acc, item) => {
771
781
  acc[item.key] = Boolean(item.column.hidden);
@@ -773,6 +783,8 @@ function SgDatatableBase(props, imperativeRef) {
773
783
  }, {}));
774
784
  }, [columnDescriptors]);
775
785
  const showAllColumns = React.useCallback(() => {
786
+ if (!enabled)
787
+ return;
776
788
  setHiddenColumnsMap((prev) => {
777
789
  const next = { ...prev };
778
790
  for (const item of columnDescriptors) {
@@ -811,7 +823,7 @@ function SgDatatableBase(props, imperativeRef) {
811
823
  const sortDirectionLabel = resolveMessage(t(i18n, "components.datatable.sortDirection"), "components.datatable.sortDirection", "Reverse order");
812
824
  const filtersPanelLabel = resolveMessage(t(i18n, "components.datatable.filters"), "components.datatable.filters", "Filters");
813
825
  const resolvedTitle = (groupBoxProps.title ?? title ?? "").trim() || " ";
814
- return (_jsx("div", { ref: rootRef, className: className, style: style, children: _jsx(SgGroupBox, { ...groupBoxProps, title: resolvedTitle, children: _jsxs("div", { className: "space-y-3", children: [shouldShowToolbar ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [showGlobalFilter ? (_jsx("input", { value: currentGlobalFilter, onChange: (event) => commitFilters(currentFilters, event.target.value), placeholder: globalFilterLabel, className: "min-w-[220px] flex-1 rounded-md border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-input-bg,var(--sg-surface)))] px-3 py-2 text-sm text-[rgb(var(--sg-input-fg,var(--sg-text)))] placeholder:text-[rgb(var(--sg-input-placeholder,var(--sg-muted)))] outline-none focus:ring-2 focus:ring-[rgb(var(--sg-ring))]" })) : null, showClearFiltersButton ? (_jsx(SgButton, { size: "sm", appearance: "outline", onClick: clearFilters, children: clearFiltersLabel })) : null] })) : null, _jsxs("div", { className: "relative", ref: columnManagerRef, children: [showColumnManager ? (_jsx("div", { className: "mb-2 flex items-center justify-start", children: _jsx("button", { ref: columnManagerButtonRef, type: "button", "aria-label": resolvedColumnManagerLabel, title: resolvedColumnManagerLabel, onClick: () => setIsColumnManagerOpen((prev) => !prev), className: "inline-flex h-9 w-9 items-center justify-center rounded-md border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-surface))] text-[rgb(var(--sg-primary-600))] shadow-sm transition hover:bg-[rgb(var(--sg-primary-50))] hover:text-[rgb(var(--sg-primary-700))]", children: _jsx(SlidersHorizontal, { size: 18 }) }) })) : null, showColumnManager && isColumnManagerOpen && columnManagerPosition && typeof document !== "undefined"
826
+ return (_jsx("div", { ref: rootRef, className: enabled ? className : cn(className, "opacity-60"), style: style, "aria-disabled": enabled ? undefined : true, "data-sg-enabled": enabled ? undefined : "false", children: _jsx(SgGroupBox, { ...groupBoxProps, title: resolvedTitle, children: _jsxs("div", { className: "space-y-3", children: [shouldShowToolbar ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [showGlobalFilter ? (_jsx("input", { value: currentGlobalFilter, disabled: !enabled, onChange: (event) => commitFilters(currentFilters, event.target.value), placeholder: globalFilterLabel, className: "min-w-[220px] flex-1 rounded-md border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-input-bg,var(--sg-surface)))] px-3 py-2 text-sm text-[rgb(var(--sg-input-fg,var(--sg-text)))] placeholder:text-[rgb(var(--sg-input-placeholder,var(--sg-muted)))] outline-none focus:ring-2 focus:ring-[rgb(var(--sg-ring))]" })) : null, showClearFiltersButton ? (_jsx(SgButton, { size: "sm", appearance: "outline", enabled: enabled, onClick: clearFilters, children: clearFiltersLabel })) : null] })) : null, _jsxs("div", { className: "relative", ref: columnManagerRef, children: [showColumnManager ? (_jsx("div", { className: "mb-2 flex items-center justify-start", children: _jsx("button", { ref: columnManagerButtonRef, type: "button", "aria-label": resolvedColumnManagerLabel, title: resolvedColumnManagerLabel, disabled: !enabled, onClick: () => enabled && setIsColumnManagerOpen((prev) => !prev), className: "inline-flex h-9 w-9 items-center justify-center rounded-md border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-surface))] text-[rgb(var(--sg-primary-600))] shadow-sm transition hover:bg-[rgb(var(--sg-primary-50))] hover:text-[rgb(var(--sg-primary-700))]", children: _jsx(SlidersHorizontal, { size: 18 }) }) })) : null, showColumnManager && isColumnManagerOpen && columnManagerPosition && typeof document !== "undefined"
815
827
  ? createPortal(_jsxs("div", { ref: columnManagerPanelRef, className: "fixed z-[1400] w-[300px] rounded-xl border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-surface))] shadow-xl", style: {
816
828
  top: columnManagerPosition.top,
817
829
  left: columnManagerPosition.left
@@ -841,7 +853,7 @@ function SgDatatableBase(props, imperativeRef) {
841
853
  ? "text-[rgb(var(--sg-muted))]"
842
854
  : "text-[rgb(var(--sg-text))]", !canHideThisColumn ? "cursor-not-allowed" : ""), children: columnLabel }), _jsx("div", { className: cn("inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[rgb(var(--sg-muted))]", isReorderable ? "cursor-grab active:cursor-grabbing" : "opacity-40"), title: dragHandleLabel, children: _jsx(GripVertical, { size: 16 }) })] }, `manager-${item.key}`));
843
855
  }) }) }), _jsxs("div", { className: "flex items-center gap-2 border-t border-[rgb(var(--sg-border))] px-3 py-3", children: [_jsx(SgButton, { size: "sm", appearance: "outline", onClick: restoreDefaultColumns, children: restoreColumnsLabel }), _jsx(SgButton, { size: "sm", appearance: "outline", onClick: showAllColumns, children: showAllColumnsLabel })] })] }), document.body)
844
- : null, cardsActive ? (_jsxs("div", { "data-sg-datatable-cards": "true", className: "space-y-2", children: [cardSortColumns.length > 0 || (showColumnFilters && cardFilterColumns.length > 0) ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [cardSortColumns.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs("select", { "aria-label": sortByLabel, value: currentSortOrder === 0 || !currentSortField ? "" : currentSortField, onChange: (event) => {
856
+ : null, cardsActive ? (_jsxs("div", { "data-sg-datatable-cards": "true", className: "space-y-2", children: [cardSortColumns.length > 0 || (showColumnFilters && cardFilterColumns.length > 0) ? (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [cardSortColumns.length > 0 ? (_jsxs(_Fragment, { children: [_jsxs("select", { "aria-label": sortByLabel, disabled: !enabled, value: currentSortOrder === 0 || !currentSortField ? "" : currentSortField, onChange: (event) => {
845
857
  const next = event.target.value;
846
858
  if (!next)
847
859
  commitSort(null, 0);
@@ -850,14 +862,14 @@ function SgDatatableBase(props, imperativeRef) {
850
862
  }, className: "min-w-0 flex-1 rounded-md border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-input-bg,var(--sg-surface)))] px-2 py-2 text-sm text-[rgb(var(--sg-input-fg,var(--sg-text)))]", children: [_jsx("option", { value: "", children: sortNoneLabel }), cardSortColumns.map((column, columnIndex) => {
851
863
  const key = (column.sortField ?? column.field);
852
864
  return (_jsx("option", { value: key, children: cardHeaderLabel(column) }, `sort-${column.columnId ?? key}-${columnIndex}`));
853
- })] }), _jsx(SgButton, { size: "sm", appearance: "outline", iconOnly: true, "aria-label": sortDirectionLabel, title: sortDirectionLabel, enabled: !(!currentSortField || currentSortOrder === 0), onClick: () => {
865
+ })] }), _jsx(SgButton, { size: "sm", appearance: "outline", iconOnly: true, "aria-label": sortDirectionLabel, title: sortDirectionLabel, enabled: enabled && !(!currentSortField || currentSortOrder === 0), onClick: () => {
854
866
  if (!currentSortField)
855
867
  return;
856
868
  commitSort(currentSortField, currentSortOrder === 1 ? -1 : 1);
857
869
  }, leftIcon: currentSortOrder === -1 ? _jsx(ArrowDown, { size: 16 }) : _jsx(ArrowUp, { size: 16 }) })] })) : null, showColumnFilters && cardFilterColumns.length > 0 ? (_jsx(SgButton, { size: "sm", appearance: "outline", "aria-expanded": isCardFiltersOpen, onClick: () => setIsCardFiltersOpen((prev) => !prev), children: filtersPanelLabel })) : null] })) : null, showColumnFilters && isCardFiltersOpen && cardFilterColumns.length > 0 ? (_jsx("div", { "data-sg-datatable-filters-panel": "true", className: "space-y-2 rounded-lg border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-surface))] p-3", children: cardFilterColumns.map((column, columnIndex) => {
858
870
  const filterField = (column.filterField ?? column.field);
859
871
  const filterValue = currentFilters[filterField] ?? "";
860
- return (_jsxs("label", { className: "block space-y-1", children: [_jsx("span", { className: "text-xs font-semibold text-[rgb(var(--sg-muted))]", children: column.header }), _jsx("input", { value: filterValue, onChange: (event) => {
872
+ return (_jsxs("label", { className: "block space-y-1", children: [_jsx("span", { className: "text-xs font-semibold text-[rgb(var(--sg-muted))]", children: column.header }), _jsx("input", { value: filterValue, disabled: !enabled, onChange: (event) => {
861
873
  const normalized = normalizeFilters({ ...currentFilters, [filterField]: event.target.value });
862
874
  commitFilters(normalized, currentGlobalFilter);
863
875
  }, placeholder: column.filterPlaceholder ?? t(i18n, "components.datatable.columnFilterPlaceholder", { column: String(column.header ?? "") }), className: "w-full rounded-md border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-input-bg,var(--sg-surface)))] px-3 py-2 text-sm text-[rgb(var(--sg-input-fg,var(--sg-text)))] placeholder:text-[rgb(var(--sg-input-placeholder,var(--sg-muted)))] outline-none focus:ring-2 focus:ring-[rgb(var(--sg-ring))]" })] }, `cardfilter-${column.columnId ?? filterField}-${columnIndex}`));
@@ -871,7 +883,7 @@ function SgDatatableBase(props, imperativeRef) {
871
883
  return;
872
884
  }
873
885
  handleRowSelection(rowData);
874
- }, className: cn("rounded-lg border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-surface))] p-3", selectionMode ? "cursor-pointer" : "", stripedRows && absoluteRowIndex % 2 === 1 ? "bg-[rgb(var(--sg-primary-50))]" : "", selected ? "border-[rgb(var(--sg-primary-600))] bg-[rgb(var(--sg-primary-100))]" : "", customRowClass), children: [cardTitleColumn ? (_jsx("div", { "data-sg-card-title": "true", className: "mb-2 text-base font-semibold text-[rgb(var(--sg-text))]", children: renderCellContent(cardTitleColumn, rowData, absoluteRowIndex) })) : null, _jsx("dl", { className: "grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-sm", children: cardBodyColumns.map((column, columnIndex) => (_jsxs(React.Fragment, { children: [_jsx("dt", { className: "font-semibold text-[rgb(var(--sg-muted))]", children: column.header }), _jsx("dd", { className: cn("min-w-0 break-words text-[rgb(var(--sg-text))]", column.className), children: renderCellContent(column, rowData, absoluteRowIndex) })] }, `card-${column.columnId ?? column.field ?? columnIndex}-${absoluteRowIndex}`))) })] }, String(getRowIdentity(rowData, dataKey) ?? absoluteRowIndex)));
886
+ }, className: cn("rounded-lg border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-surface))] p-3", selectionMode && enabled ? "cursor-pointer" : "", stripedRows && absoluteRowIndex % 2 === 1 ? "bg-[rgb(var(--sg-primary-50))]" : "", selected ? "border-[rgb(var(--sg-primary-600))] bg-[rgb(var(--sg-primary-100))]" : "", customRowClass), children: [cardTitleColumn ? (_jsx("div", { "data-sg-card-title": "true", className: "mb-2 text-base font-semibold text-[rgb(var(--sg-text))]", children: renderCellContent(cardTitleColumn, rowData, absoluteRowIndex) })) : null, _jsx("dl", { className: "grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 text-sm", children: cardBodyColumns.map((column, columnIndex) => (_jsxs(React.Fragment, { children: [_jsx("dt", { className: "font-semibold text-[rgb(var(--sg-muted))]", children: column.header }), _jsx("dd", { className: cn("min-w-0 break-words text-[rgb(var(--sg-text))]", column.className), children: renderCellContent(column, rowData, absoluteRowIndex) })] }, `card-${column.columnId ?? column.field ?? columnIndex}-${absoluteRowIndex}`))) })] }, String(getRowIdentity(rowData, dataKey) ?? absoluteRowIndex)));
875
887
  }) })), hasFooter ? (_jsx("dl", { "data-sg-card-footer": "true", className: "grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1 rounded-lg bg-[rgb(var(--sg-primary-50))] p-3 text-sm font-semibold text-[rgb(var(--sg-text))]", children: visibleColumns
876
888
  .filter((column) => column.footer !== undefined && !column.cardHidden)
877
889
  .map((column, columnIndex) => (_jsxs(React.Fragment, { children: [_jsx("dt", { children: column.header }), _jsx("dd", { children: typeof column.footer === "function" ? column.footer(sortedRows) : column.footer })] }, `cardfoot-${column.columnId ?? column.field ?? columnIndex}`))) })) : null] })) : (_jsx("div", { className: "overflow-x-auto rounded-lg border border-[rgb(var(--sg-border))] bg-[rgb(var(--sg-surface))]", children: _jsxs("table", { id: id, className: cn("min-w-full border-collapse text-sm", tableClassName), children: [_jsxs("thead", { children: [_jsx("tr", { className: "bg-[rgb(var(--sg-primary-50))]", children: visibleColumns.map((column, columnIndex) => {
@@ -883,11 +895,11 @@ function SgDatatableBase(props, imperativeRef) {
883
895
  // direcao. `aria-hidden` no wrapper mantem o leitor de tela lendo so' o
884
896
  // rotulo, como antes.
885
897
  const sortIcon = !column.sortable ? null : isSorted && currentSortOrder === 1 ? (_jsx(ArrowUp, { size: 14 })) : isSorted && currentSortOrder === -1 ? (_jsx(ArrowDown, { size: 14 })) : (_jsx(ChevronsUpDown, { size: 14, className: "opacity-60" }));
886
- return (_jsx("th", { className: cn("px-3 py-2 font-semibold text-[rgb(var(--sg-text))]", resolveAlignmentClass(column.align), showGridlines ? "border-b border-r border-[rgb(var(--sg-border))] last:border-r-0" : "border-b border-[rgb(var(--sg-border))]", column.headerClassName, column.className), style: { width: toCssSize(column.width), minWidth: toCssSize(column.minWidth) }, children: column.sortable ? (_jsxs("button", { type: "button", onClick: () => handleHeaderSort(column), className: cn("inline-flex items-center gap-1 font-semibold", resolveAlignmentClass(column.align), "text-[rgb(var(--sg-text))] hover:text-[rgb(var(--sg-primary-600))]"), children: [_jsx("span", { children: column.header }), _jsx("span", { "aria-hidden": "true", className: "inline-flex items-center", children: sortIcon })] })) : (_jsx("span", { children: column.header })) }, `head-${column.columnId ?? column.field ?? columnIndex}`));
898
+ return (_jsx("th", { className: cn("px-3 py-2 font-semibold text-[rgb(var(--sg-text))]", resolveAlignmentClass(column.align), showGridlines ? "border-b border-r border-[rgb(var(--sg-border))] last:border-r-0" : "border-b border-[rgb(var(--sg-border))]", column.headerClassName, column.className), style: { width: toCssSize(column.width), minWidth: toCssSize(column.minWidth) }, children: column.sortable ? (_jsxs("button", { type: "button", disabled: !enabled, onClick: () => handleHeaderSort(column), className: cn("inline-flex items-center gap-1 font-semibold", resolveAlignmentClass(column.align), "text-[rgb(var(--sg-text))] hover:text-[rgb(var(--sg-primary-600))]"), children: [_jsx("span", { children: column.header }), _jsx("span", { "aria-hidden": "true", className: "inline-flex items-center", children: sortIcon })] })) : (_jsx("span", { children: column.header })) }, `head-${column.columnId ?? column.field ?? columnIndex}`));
887
899
  }) }), shouldShowFiltersRow ? (_jsx("tr", { children: visibleColumns.map((column, columnIndex) => {
888
900
  const filterField = column.filterField ?? column.field;
889
901
  const filterValue = filterField ? currentFilters[filterField] ?? "" : "";
890
- return (_jsx("th", { className: cn("px-3 py-2 align-top", showGridlines ? "border-b border-r border-[rgb(var(--sg-border))] last:border-r-0" : "border-b border-[rgb(var(--sg-border))]"), children: column.filter && filterField ? (_jsx("input", { value: filterValue, onChange: (event) => {
902
+ return (_jsx("th", { className: cn("px-3 py-2 align-top", showGridlines ? "border-b border-r border-[rgb(var(--sg-border))] last:border-r-0" : "border-b border-[rgb(var(--sg-border))]"), children: column.filter && filterField ? (_jsx("input", { value: filterValue, disabled: !enabled, onChange: (event) => {
891
903
  const nextFilters = {
892
904
  ...currentFilters,
893
905
  [filterField]: event.target.value
@@ -905,7 +917,7 @@ function SgDatatableBase(props, imperativeRef) {
905
917
  return;
906
918
  }
907
919
  handleRowSelection(rowData);
908
- }, className: cn(selectionMode ? "cursor-pointer" : "", stripedRows && absoluteRowIndex % 2 === 1 ? "bg-[rgb(var(--sg-primary-50))]" : "", hoverableRows ? "hover:bg-[rgb(var(--sg-primary-100))]" : "", selected ? "bg-[rgb(var(--sg-primary-100))]" : "", customRowClass), children: visibleColumns.map((column, columnIndex) => {
920
+ }, className: cn(selectionMode && enabled ? "cursor-pointer" : "", stripedRows && absoluteRowIndex % 2 === 1 ? "bg-[rgb(var(--sg-primary-50))]" : "", hoverableRows ? "hover:bg-[rgb(var(--sg-primary-100))]" : "", selected ? "bg-[rgb(var(--sg-primary-100))]" : "", customRowClass), children: visibleColumns.map((column, columnIndex) => {
909
921
  const cellField = column.field;
910
922
  const customCellClass = typeof column.bodyClassName === "function"
911
923
  ? column.bodyClassName(rowData, absoluteRowIndex)
@@ -1 +1 @@
1
- {"version":3,"file":"SgDatatable.meta.d.ts","sourceRoot":"","sources":["../../src/inputs/SgDatatable.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAoCpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAoBrB,CAAC"}
1
+ {"version":3,"file":"SgDatatable.meta.d.ts","sourceRoot":"","sources":["../../src/inputs/SgDatatable.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAqCpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAoBrB,CAAC"}
@@ -20,6 +20,7 @@ export const sgMeta = {
20
20
  { name: "rows", type: "number", description: "Linhas por pagina quando a paginacao esta ativa (os dados vao em value).", semanticRole: "behavior", bindable: true },
21
21
  { name: "selectionMode", type: "\"single\" | \"multiple\"", description: "Modo de selecao de linhas.", semanticRole: "behavior", bindable: true },
22
22
  { name: "loading", type: "boolean", default: false, description: "Indica estado de carregamento.", semanticRole: "appearance", bindable: true },
23
+ { name: "enabled", type: "boolean", default: true, description: "Quando false, bloqueia as acoes que mudam estado: ordenacao, selecao de linhas, filtros (global e por coluna), limpar filtros e gerenciador de colunas. Paginacao, rolagem e carga de mais linhas continuam funcionando. Aparencia de desabilitado e aria-disabled.", semanticRole: "behavior", bindable: true },
23
24
  { name: "mobileMode", type: "\"scroll\" | \"cards\"", default: "(omitido = automatico)", description: "Comportamento em tela estreita. Omitido (padrao): cartoes quando o container fica abaixo de 600 px, tabela acima; cards: forca cartoes em qualquer largura; scroll: forca a tabela com rolagem horizontal. Nos cartoes mantem ordenacao, selecao, acoes, rodape e paginacao.", semanticRole: "behavior", bindable: true },
24
25
  { name: "infiniteScroll", type: "boolean", default: "(true nos cartoes, false na tabela)", description: "Rolagem infinita: sem paginador, ao chegar perto do fim da lista avanca a pagina (rows) e acrescenta os proximos registros, com indicador de carregando e de fim da lista. Padrao ligado quando a lista e exibida como cartoes (abaixo de 600 px no modo automatico) e desligado na tabela. Exige paginator. Com lazy, o pai acrescenta a proxima pagina a value (first/rows/page vem em onPage/onLoadMore).", semanticRole: "behavior", bindable: true },
25
26
  { name: "onLoadMore", type: "(event: SgDatatablePageEvent) => void", description: "Chamado (alem de onPage) quando a rolagem infinita pede a proxima pagina.", semanticRole: "event", bindable: false },
@@ -27,6 +27,12 @@ export type SgToolBarProps = {
27
27
  collapsed?: boolean;
28
28
  defaultCollapsed?: boolean;
29
29
  onCollapsedChange?: (collapsed: boolean) => void;
30
+ /**
31
+ * Quando a barra nao comporta todos os itens na largura (ou altura, na vertical) disponivel, os
32
+ * `SgToolbarIconButton` que nao cabem vao para um menu "..." (SgMenu). Abaixo de 600px de largura
33
+ * de tela o menu abre como painel inferior. Padrao: false. Ignorado com `buttonsPerDirection`.
34
+ */
35
+ overflow?: boolean;
30
36
  children?: React.ReactNode;
31
37
  };
32
38
  export type SgToolbarIconButtonProps = {
@@ -1 +1 @@
1
- {"version":3,"file":"SgToolBar.d.ts","sourceRoot":"","sources":["../../src/layout/SgToolBar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAY/B,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,UAAU,CAAC;AAC7D,MAAM,MAAM,6BAA6B,GACrC,aAAa,GACb,eAAe,GACf,iBAAiB,GACjB,kBAAkB,CAAC;AACvB,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,WAAW,GACX,SAAS,GACT,MAAM,GACN,SAAS,GACT,MAAM,GACN,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG;IAC1B,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,oBAAoB,CAAC,EAAE,6BAA6B,CAAC;IACrD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAExD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAEjD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B,CAAC;AAgFF,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,2CAsjBxD;yBAtjBe,SAAS;;;AAwjBzB,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,CAAC,wBAAwB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,2CAiJpE;yBAlJe,mBAAmB"}
1
+ {"version":3,"file":"SgToolBar.d.ts","sourceRoot":"","sources":["../../src/layout/SgToolBar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAe/B,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,UAAU,CAAC;AAC7D,MAAM,MAAM,6BAA6B,GACrC,aAAa,GACb,eAAe,GACf,iBAAiB,GACjB,kBAAkB,CAAC;AACvB,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,WAAW,GACX,SAAS,GACT,MAAM,GACN,SAAS,GACT,MAAM,GACN,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG;IAC1B,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,oBAAoB,CAAC,EAAE,6BAA6B,CAAC;IACrD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAExD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAEjD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B,CAAC;AAgFF,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC,2CA6jBxD;yBA7jBe,SAAS;;;AA+jBzB,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,CAAC,wBAAwB,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,2CAiJpE;yBAlJe,mBAAmB"}
@@ -2,11 +2,14 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { createPortal } from "react-dom";
5
+ import { MoreHorizontal } from "lucide-react";
5
6
  import { useSgDockLayout } from "./SgDockLayout";
6
7
  import { useHasSgEnvironmentProvider, useSgPersistence } from "../environment/SgEnvironmentProvider";
7
8
  import { t, useComponentsI18n } from "../i18n";
8
9
  import { buildToolbarStorageKey, parseStoredPanelDragPosition } from "./drag-position";
9
- import { buildToolbarLayoutState } from "./toolbar-logic";
10
+ import { buildToolbarLayoutState, resolveToolbarOverflow } from "./toolbar-logic";
11
+ import { SgMenu } from "./SgMenu";
12
+ import { SgPopup } from "../overlay/SgPopup";
10
13
  function cn(...parts) {
11
14
  return parts.filter(Boolean).join(" ");
12
15
  }
@@ -72,7 +75,7 @@ export function SgToolBar(props) {
72
75
  const hasEnvironmentProvider = useHasSgEnvironmentProvider();
73
76
  const i18n = useComponentsI18n();
74
77
  const { load: loadPersistedState, save: savePersistedState, clear: clearPersistedState } = useSgPersistence();
75
- const { id, title, orientationDirection, buttonsPerDirection, bgColorTitle, bgColor, size, className, style, dockZone, draggable = false, freeDrag = false, defaultPosition, collapsible = true, collapsed, defaultCollapsed = false, onCollapsedChange, children } = props;
78
+ const { id, title, orientationDirection, buttonsPerDirection, bgColorTitle, bgColor, size, className, style, dockZone, draggable = false, freeDrag = false, defaultPosition, collapsible = true, collapsed, defaultCollapsed = false, onCollapsedChange, overflow = false, children } = props;
76
79
  const dock = useSgDockLayout();
77
80
  const inDock = !!dock;
78
81
  const assignedZone = inDock ? dock.getToolbarZone(id) : null;
@@ -441,7 +444,8 @@ export function SgToolBar(props) {
441
444
  const normalizedButtonsPerDirection = Number.isFinite(buttonsPerDirection) && typeof buttonsPerDirection === "number" && buttonsPerDirection > 0
442
445
  ? Math.floor(buttonsPerDirection)
443
446
  : undefined;
444
- const content = showContent ? (_jsx("div", { className: cn("gap-2 p-2", normalizedButtonsPerDirection
447
+ const overflowActive = overflow && !normalizedButtonsPerDirection && showContent;
448
+ const content = showContent ? (_jsx("div", { className: cn("gap-2 p-2", overflowActive && "min-h-0 min-w-0 flex-1", normalizedButtonsPerDirection
445
449
  ? "grid"
446
450
  : orientation === "horizontal"
447
451
  ? "flex flex-row"
@@ -452,7 +456,7 @@ export function SgToolBar(props) {
452
456
  gridTemplateRows: `repeat(${normalizedButtonsPerDirection}, max-content)`,
453
457
  gridAutoFlow: "column"
454
458
  }
455
- : undefined, children: children })) : null;
459
+ : undefined, children: overflowActive ? (_jsx(SgToolBarOverflowFlow, { horizontal: orientation === "horizontal", children: children })) : children })) : null;
456
460
  const toolbar = (_jsx(SgToolbarOrientationContext.Provider, { value: orientation, children: _jsxs("div", { ref: containerRef, "data-sg-toolbar-root": "true", "data-sg-toolbar-id": id, className: cn("select-none rounded-xl border border-border bg-background shadow-sm", orientation === "horizontal" ? "inline-flex flex-row items-center" : "inline-flex flex-col items-center", className), style: {
457
461
  width: size?.w,
458
462
  height: size?.h,
@@ -464,7 +468,7 @@ export function SgToolBar(props) {
464
468
  order: detachFromDockFlow ? undefined : toolbarRenderOrder,
465
469
  zIndex: dragPos ? 1000 : undefined,
466
470
  ...style
467
- }, onPointerDown: handlePointerDown, children: [openUp || openLeft ? content : null, (title || collapsible) && (_jsxs("div", { className: cn("flex items-center gap-2 px-2 py-1", orientation === "horizontal" ? "w-auto" : "w-full", orientation === "horizontal"
471
+ }, onPointerDown: handlePointerDown, children: [openUp || openLeft ? content : null, (title || collapsible) && (_jsxs("div", { className: cn("flex items-center gap-2 px-2 py-1", overflowActive && "shrink-0", orientation === "horizontal" ? "w-auto" : "w-full", orientation === "horizontal"
468
472
  ? showContent
469
473
  ? (openLeft ? "border-l border-border" : "border-r border-border")
470
474
  : ""
@@ -559,6 +563,104 @@ export function SgToolbarIconButton(props) {
559
563
  ...style
560
564
  }, children: [isLoading ? (_jsx("span", { className: "inline-flex size-4 shrink-0 animate-spin rounded-full border-2 border-current border-r-transparent" })) : icon && typeof icon !== "string" ? (_jsx("span", { className: "inline-flex shrink-0", children: icon })) : (_jsx("span", { className: "shrink-0 text-[10px] font-semibold", children: text?.slice(0, 2) })), hasVisibleLabel ? (_jsx("span", { className: "text-xs font-medium leading-none", children: label })) : null] }), hintNode] }));
561
565
  }
566
+ const OVERFLOW_GAP = 8;
567
+ const OVERFLOW_NARROW_WIDTH = 600;
568
+ const useIsoLayoutEffect = typeof window !== "undefined" ? React.useLayoutEffect : React.useEffect;
569
+ function sameMetrics(a, b) {
570
+ return (!!a &&
571
+ a.more === b.more &&
572
+ a.available === b.available &&
573
+ a.extents.length === b.extents.length &&
574
+ a.extents.every((v, i) => v === b.extents[i]));
575
+ }
576
+ /**
577
+ * Itens da toolbar com `overflow`: mede a largura (ResizeObserver) e move para o menu "..." os
578
+ * `SgToolbarIconButton` que nao cabem. Outros elementos que nao cabem ficam escondidos.
579
+ * Uma copia invisivel de todos os itens ("ghost") fornece os tamanhos naturais.
580
+ */
581
+ function SgToolBarOverflowFlow(props) {
582
+ const { horizontal, children } = props;
583
+ const i18n = useComponentsI18n();
584
+ const items = React.Children.toArray(children);
585
+ const hostRef = React.useRef(null);
586
+ const ghostRef = React.useRef(null);
587
+ const moreRef = React.useRef(null);
588
+ const [metrics, setMetrics] = React.useState(null);
589
+ const [menuOpen, setMenuOpen] = React.useState(false);
590
+ const [narrow, setNarrow] = React.useState(false);
591
+ const measure = React.useCallback(() => {
592
+ const host = hostRef.current;
593
+ const ghost = ghostRef.current;
594
+ if (!host || !ghost)
595
+ return;
596
+ const sizes = Array.from(ghost.children).map((el) => horizontal ? el.offsetWidth : el.offsetHeight);
597
+ const more = sizes.pop() ?? 0;
598
+ const next = { extents: sizes, more, available: horizontal ? host.clientWidth : host.clientHeight };
599
+ setMetrics((prev) => (sameMetrics(prev, next) ? prev : next));
600
+ }, [horizontal]);
601
+ useIsoLayoutEffect(() => {
602
+ measure();
603
+ });
604
+ React.useEffect(() => {
605
+ const host = hostRef.current;
606
+ const ghost = ghostRef.current;
607
+ if (typeof ResizeObserver === "undefined") {
608
+ window.addEventListener("resize", measure);
609
+ return () => window.removeEventListener("resize", measure);
610
+ }
611
+ const observer = new ResizeObserver(() => measure());
612
+ if (host)
613
+ observer.observe(host);
614
+ if (ghost)
615
+ observer.observe(ghost);
616
+ return () => observer.disconnect();
617
+ }, [measure]);
618
+ const count = metrics && metrics.extents.length === items.length
619
+ ? resolveToolbarOverflow({
620
+ extents: metrics.extents,
621
+ available: metrics.available,
622
+ gap: OVERFLOW_GAP,
623
+ overflowExtent: metrics.more
624
+ })
625
+ : items.length;
626
+ const showMore = count < items.length;
627
+ const hiddenNodes = [];
628
+ items.forEach((item, index) => {
629
+ if (index < count || !React.isValidElement(item) || item.type !== SgToolbarIconButton)
630
+ return;
631
+ const p = item.props;
632
+ hiddenNodes.push({
633
+ id: `sg-toolbar-overflow-${index}`,
634
+ label: p.label ?? p.hint ?? "",
635
+ hint: p.hint,
636
+ icon: p.icon,
637
+ enabled: p.enabled,
638
+ onClick: p.onClick ? () => { void p.onClick?.(); } : undefined
639
+ });
640
+ });
641
+ React.useEffect(() => {
642
+ if (!showMore && menuOpen)
643
+ setMenuOpen(false);
644
+ }, [showMore, menuOpen]);
645
+ const total = metrics ? metrics.extents.reduce((acc, v, i) => acc + v + (i > 0 ? OVERFLOW_GAP : 0), 0) : 0;
646
+ const openLabel = t(i18n, "components.menu.open");
647
+ const flexDir = horizontal ? "row" : "column";
648
+ return (_jsxs("div", { ref: hostRef, "data-sg-toolbar-overflow": "true", style: { position: "relative", minWidth: 0, minHeight: 0, flex: "1 1 0%", display: "flex" }, children: [metrics ? (_jsx("div", { "aria-hidden": "true", style: { flex: "none", width: horizontal ? total : 0, height: horizontal ? 0 : total } })) : null, _jsxs("div", { "data-sg-toolbar-overflow-items": "true", style: { display: "flex", flexDirection: flexDir, alignItems: "center", gap: OVERFLOW_GAP, minWidth: 0 }, children: [items.slice(0, count), showMore ? (_jsx("span", { ref: moreRef, "data-sg-toolbar-overflow-button": "true", style: { display: "inline-flex" }, children: _jsx(SgToolbarIconButton, { icon: _jsx(MoreHorizontal, { className: "size-4" }), hint: openLabel, onClick: () => {
649
+ setNarrow(typeof window !== "undefined" && window.innerWidth < OVERFLOW_NARROW_WIDTH);
650
+ setMenuOpen((open) => !open);
651
+ } }) })) : null] }), _jsx("div", { "aria-hidden": "true", style: {
652
+ position: "absolute",
653
+ left: 0,
654
+ top: 0,
655
+ width: 0,
656
+ height: 0,
657
+ overflow: "hidden",
658
+ visibility: "hidden",
659
+ pointerEvents: "none"
660
+ }, children: _jsxs("div", { ref: ghostRef, style: { display: "flex", flexDirection: flexDir, alignItems: "center", gap: OVERFLOW_GAP, width: "max-content" }, children: [items.map((item, index) => (_jsx("div", { style: { flex: "none" }, children: item }, index))), _jsx("div", { style: { flex: "none" }, children: _jsx(SgToolbarIconButton, { icon: _jsx(MoreHorizontal, { className: "size-4" }) }) })] }) }), showMore && menuOpen ? (_jsx(SgPopup, { open: true, onOpenChange: setMenuOpen, anchorRef: moreRef, placement: horizontal ? "bottom" : "right", align: "start", offset: 4, zIndex: 1300, minWidth: narrow ? 0 : 240, style: narrow
661
+ ? { top: "auto", left: 0, right: 0, bottom: 0, width: "100%", borderRadius: "12px 12px 0 0" }
662
+ : undefined, children: _jsx(SgMenu, { menu: hiddenNodes, menuStyle: "inline", border: false, elevation: "none", expandedWidth: narrow ? "100%" : 240, ariaLabel: openLabel, onItemClick: () => setMenuOpen(false) }) })) : null] }));
663
+ }
562
664
  function CollapseIcon(props) {
563
665
  const { direction, collapsed } = props;
564
666
  const arrowUp = "M6 15l6-6 6 6";
@@ -1 +1 @@
1
- {"version":3,"file":"SgToolBar.meta.d.ts","sourceRoot":"","sources":["../../src/layout/SgToolBar.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAyGpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAyBrB,CAAC"}
1
+ {"version":3,"file":"SgToolBar.meta.d.ts","sourceRoot":"","sources":["../../src/layout/SgToolBar.meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,MAAM,EAAE,QAiHpB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,WAyBrB,CAAC"}
@@ -65,6 +65,14 @@ export const sgMeta = {
65
65
  semanticRole: "behavior",
66
66
  bindable: true
67
67
  },
68
+ {
69
+ name: "overflow",
70
+ type: "boolean",
71
+ default: false,
72
+ description: "Move para um menu '...' os botoes que nao cabem na largura disponivel; abaixo de 600px abre como painel inferior.",
73
+ semanticRole: "behavior",
74
+ bindable: true
75
+ },
68
76
  {
69
77
  name: "children",
70
78
  type: "ReactNode",
@@ -23,4 +23,15 @@ export declare function buildToolbarLayoutState(args: {
23
23
  showLeadingCollapseButton: boolean;
24
24
  collapseIconDirection: "left" | "right" | "up" | "down";
25
25
  };
26
+ /**
27
+ * Overflow da SgToolBar (mesma regra do Flutter): quantos itens cabem no espaco principal.
28
+ * `extents` = tamanho de cada item no eixo principal; `overflowExtent` = tamanho do botao "..."
29
+ * (so conta se algum item ficar de fora). Devolve `extents.length` quando tudo cabe.
30
+ */
31
+ export declare function resolveToolbarOverflow(args: {
32
+ extents: number[];
33
+ available: number;
34
+ gap: number;
35
+ overflowExtent: number;
36
+ }): number;
26
37
  //# sourceMappingURL=toolbar-logic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toolbar-logic.d.ts","sourceRoot":"","sources":["../../src/layout/toolbar-logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AAEvF,wBAAgB,kCAAkC,CAChD,oBAAoB,EAAE,6BAA6B,GAClD;IAAE,WAAW,EAAE,oBAAoB,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAA;CAAE,CAYpF;AAED,wBAAgB,sCAAsC,CACpD,oBAAoB,EAAE,6BAA6B,EACnD,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GACjD,6BAA6B,CAQ/B;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,oBAAoB,EAAE,6BAA6B,CAAC;IACpD,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACnD,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,aAAa,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAC5D,WAAW,EAAE,OAAO,CAAC;CACtB,GAAG;IACF,kBAAkB,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACjE,4BAA4B,EAAE,6BAA6B,CAAC;IAC5D,WAAW,EAAE,oBAAoB,CAAC;IAClC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;IAC5C,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,yBAAyB,EAAE,OAAO,CAAC;IACnC,qBAAqB,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;CACzD,CAoBA"}
1
+ {"version":3,"file":"toolbar-logic.d.ts","sourceRoot":"","sources":["../../src/layout/toolbar-logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AAEvF,wBAAgB,kCAAkC,CAChD,oBAAoB,EAAE,6BAA6B,GAClD;IAAE,WAAW,EAAE,oBAAoB,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAA;CAAE,CAYpF;AAED,wBAAgB,sCAAsC,CACpD,oBAAoB,EAAE,6BAA6B,EACnD,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GACjD,6BAA6B,CAQ/B;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,oBAAoB,EAAE,6BAA6B,CAAC;IACpD,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACnD,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,aAAa,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAC5D,WAAW,EAAE,OAAO,CAAC;CACtB,GAAG;IACF,kBAAkB,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACjE,4BAA4B,EAAE,6BAA6B,CAAC;IAC5D,WAAW,EAAE,oBAAoB,CAAC;IAClC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;IAC5C,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,yBAAyB,EAAE,OAAO,CAAC;IACnC,qBAAqB,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;CACzD,CAoBA;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG,MAAM,CAeT"}
@@ -36,3 +36,28 @@ export function buildToolbarLayoutState(args) {
36
36
  collapseIconDirection: resolvedOrientationDirection === "horizontal-right" ? "right" : direction
37
37
  };
38
38
  }
39
+ /**
40
+ * Overflow da SgToolBar (mesma regra do Flutter): quantos itens cabem no espaco principal.
41
+ * `extents` = tamanho de cada item no eixo principal; `overflowExtent` = tamanho do botao "..."
42
+ * (so conta se algum item ficar de fora). Devolve `extents.length` quando tudo cabe.
43
+ */
44
+ export function resolveToolbarOverflow(args) {
45
+ const { extents, available, gap, overflowExtent } = args;
46
+ if (!Number.isFinite(available))
47
+ return extents.length;
48
+ let total = 0;
49
+ for (let i = 0; i < extents.length; i++)
50
+ total += (extents[i] ?? 0) + (i > 0 ? gap : 0);
51
+ if (total <= available + 0.01)
52
+ return extents.length;
53
+ let used = 0;
54
+ let count = 0;
55
+ for (let i = 0; i < extents.length; i++) {
56
+ const next = used + (extents[i] ?? 0) + (count > 0 ? gap : 0);
57
+ if (next + gap + overflowExtent > available + 0.01)
58
+ break;
59
+ used = next;
60
+ count++;
61
+ }
62
+ return count;
63
+ }