@duke333/builder-ui 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -420,11 +420,13 @@ function createBuilderClient({ transport, apiBasePath }) {
420
420
  return createBuilderApiClient(transport, { apiBasePath });
421
421
  }
422
422
  var BuilderContext = createContext(null);
423
+ var defaultComponents = {};
423
424
  function BuilderProvider({
424
425
  children,
425
426
  client: configuredClient,
426
427
  transport,
427
428
  apiBasePath,
429
+ components = defaultComponents,
428
430
  queryClient,
429
431
  onNotify
430
432
  }) {
@@ -446,8 +448,8 @@ function BuilderProvider({
446
448
  const internalQueryClient = useRef(null);
447
449
  if (!internalQueryClient.current) internalQueryClient.current = queryClient ?? new QueryClient();
448
450
  const value = useMemo(
449
- () => ({ client: internalClient, hooks, notify }),
450
- [hooks, internalClient, notify]
451
+ () => ({ client: internalClient, components, hooks, notify }),
452
+ [components, hooks, internalClient, notify]
451
453
  );
452
454
  return /* @__PURE__ */ jsx(QueryClientProvider, { client: internalQueryClient.current, children: /* @__PURE__ */ jsxs(BuilderContext.Provider, { value, children: [
453
455
  children,
@@ -468,7 +470,7 @@ function builderErrorMessage(error, fallback) {
468
470
  }
469
471
 
470
472
  // src/ready/BuilderRuntimeComponents.tsx
471
- import { useMemo as useMemo4, useState as useState4 } from "react";
473
+ import { useMemo as useMemo4, useState as useState5 } from "react";
472
474
  import { FormProvider as FormProvider2, useForm as useForm2 } from "react-hook-form";
473
475
 
474
476
  // src/runtime/runtimeUtils.ts
@@ -593,7 +595,8 @@ function ReadyFields({ formId, viewId, schema, extraItems = [] }) {
593
595
  }
594
596
 
595
597
  // src/ready/ReadyOverlay.tsx
596
- import { useEffect, useId, useRef as useRef3 } from "react";
598
+ import { useEffect, useId, useRef as useRef3, useState as useState2 } from "react";
599
+ import { createPortal } from "react-dom";
597
600
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
598
601
  function ReadyOverlay({
599
602
  open,
@@ -605,10 +608,14 @@ function ReadyOverlay({
605
608
  }) {
606
609
  const panelRef = useRef3(null);
607
610
  const onCloseRef = useRef3(onClose);
611
+ const [portalRoot, setPortalRoot] = useState2(null);
608
612
  onCloseRef.current = onClose;
609
613
  const titleId = useId();
610
614
  useEffect(() => {
611
- if (!open) return;
615
+ setPortalRoot(document.body);
616
+ }, []);
617
+ useEffect(() => {
618
+ if (!open || !portalRoot) return;
612
619
  const previous = document.activeElement;
613
620
  panelRef.current?.focus();
614
621
  const onKeyDown = (event) => {
@@ -638,29 +645,32 @@ function ReadyOverlay({
638
645
  document.body.style.overflow = overflow;
639
646
  previous?.focus();
640
647
  };
641
- }, [open]);
642
- if (!open) return null;
643
- return /* @__PURE__ */ jsx3("div", { className: "builder-overlay", role: "presentation", onMouseDown: (event) => event.target === event.currentTarget && onClose(), children: /* @__PURE__ */ jsxs3(
644
- "div",
645
- {
646
- ref: panelRef,
647
- className: `builder-overlay__panel builder-overlay__panel--${presentation}`,
648
- role: "dialog",
649
- "aria-modal": "true",
650
- "aria-labelledby": titleId,
651
- tabIndex: -1,
652
- children: [
653
- /* @__PURE__ */ jsxs3("header", { className: "builder-overlay__header", children: [
654
- /* @__PURE__ */ jsxs3("div", { children: [
655
- /* @__PURE__ */ jsx3("h2", { id: titleId, className: "builder-title", children: title }),
656
- description ? /* @__PURE__ */ jsx3("p", { className: "builder-description", children: description }) : null
648
+ }, [open, portalRoot]);
649
+ if (!open || !portalRoot) return null;
650
+ return createPortal(
651
+ /* @__PURE__ */ jsx3("div", { className: "builder-overlay", role: "presentation", onMouseDown: (event) => event.target === event.currentTarget && onClose(), children: /* @__PURE__ */ jsxs3(
652
+ "div",
653
+ {
654
+ ref: panelRef,
655
+ className: `builder-overlay__panel builder-overlay__panel--${presentation}`,
656
+ role: "dialog",
657
+ "aria-modal": "true",
658
+ "aria-labelledby": titleId,
659
+ tabIndex: -1,
660
+ children: [
661
+ /* @__PURE__ */ jsxs3("header", { className: "builder-overlay__header", children: [
662
+ /* @__PURE__ */ jsxs3("div", { children: [
663
+ /* @__PURE__ */ jsx3("h2", { id: titleId, className: "builder-title", children: title }),
664
+ description ? /* @__PURE__ */ jsx3("p", { className: "builder-description", children: description }) : null
665
+ ] }),
666
+ /* @__PURE__ */ jsx3("button", { type: "button", className: "builder-icon-button", "aria-label": "\u0425\u0430\u0430\u0445", onClick: onClose, children: "\xD7" })
657
667
  ] }),
658
- /* @__PURE__ */ jsx3("button", { type: "button", className: "builder-icon-button", "aria-label": "\u0425\u0430\u0430\u0445", onClick: onClose, children: "\xD7" })
659
- ] }),
660
- /* @__PURE__ */ jsx3("div", { className: "builder-overlay__body", children })
661
- ]
662
- }
663
- ) });
668
+ /* @__PURE__ */ jsx3("div", { className: "builder-overlay__body", children })
669
+ ]
670
+ }
671
+ ) }),
672
+ portalRoot
673
+ );
664
674
  }
665
675
  function ReadyConfirmDialog({
666
676
  open,
@@ -684,16 +694,16 @@ function ReadyErrorState({ title, description }) {
684
694
  }
685
695
 
686
696
  // src/ready/ReadySubform.tsx
687
- import { useEffect as useEffect2, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
697
+ import { useEffect as useEffect2, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
688
698
  import { FormProvider, useForm } from "react-hook-form";
689
699
 
690
700
  // src/ready/ReadyTable.tsx
691
- import { useMemo as useMemo2, useState as useState2 } from "react";
701
+ import { useMemo as useMemo2, useState as useState3 } from "react";
692
702
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
693
703
  function useReadyTableState({ pageSize = 20 } = {}) {
694
- const [page, setPage] = useState2(1);
695
- const [search, setSearchValue] = useState2("");
696
- const [sort, setSort] = useState2();
704
+ const [page, setPage] = useState3(1);
705
+ const [search, setSearchValue] = useState3("");
706
+ const [sort, setSort] = useState3();
697
707
  return {
698
708
  query: { page, pageSize, search: search || void 0, sort },
699
709
  page,
@@ -712,7 +722,7 @@ function useReadyTableState({ pageSize = 20 } = {}) {
712
722
  };
713
723
  }
714
724
  function useReadyTable(args) {
715
- const [selectedIds, setSelectedIds] = useState2([]);
725
+ const [selectedIds, setSelectedIds] = useState3([]);
716
726
  return useMemo2(
717
727
  () => ({
718
728
  ...args,
@@ -819,7 +829,7 @@ function ReadyDataGrid({
819
829
  ] });
820
830
  }
821
831
  function readyColumns(columns) {
822
- return columns.map((column) => ({ ...column }));
832
+ return columns.filter((column) => !column.hidden).map((column) => ({ ...column }));
823
833
  }
824
834
 
825
835
  // src/ready/ReadySubform.tsx
@@ -845,8 +855,8 @@ function makeDraftEntry(viewId, values, editing) {
845
855
  }
846
856
  function ReadySubform({ subform, parentEntry, rows, onRowsChange }) {
847
857
  const { hooks, notify } = useBuilderRuntime();
848
- const [open, setOpen] = useState3(false);
849
- const [editing, setEditing] = useState3(null);
858
+ const [open, setOpen] = useState4(false);
859
+ const [editing, setEditing] = useState4(null);
850
860
  const initialized = useRef4(false);
851
861
  const parentValue = linkedValue(parentEntry, subform.parentField);
852
862
  const viewQuery = hooks.useRuntimeBuilderView(subform.viewId, Boolean(subform.viewId));
@@ -876,6 +886,7 @@ function ReadySubform({ subform, parentEntry, rows, onRowsChange }) {
876
886
  key: "_actions",
877
887
  label: "\u04AE\u0439\u043B\u0434\u044D\u043B",
878
888
  sortable: false,
889
+ hidden: false,
879
890
  align: "right",
880
891
  render: (row) => /* @__PURE__ */ jsx5("button", { type: "button", className: "builder-link builder-link--danger", onClick: (event) => {
881
892
  event.stopPropagation();
@@ -940,7 +951,7 @@ function ReadySubform({ subform, parentEntry, rows, onRowsChange }) {
940
951
  /* @__PURE__ */ jsx5(ReadyFields, { formId: view.form.id, viewId: view.id, schema: view.form.schema }),
941
952
  /* @__PURE__ */ jsxs5("div", { className: "builder-actions", children: [
942
953
  /* @__PURE__ */ jsx5("button", { type: "button", className: "builder-button builder-button--ghost", onClick: close, children: "\u0411\u043E\u043B\u0438\u0445" }),
943
- /* @__PURE__ */ jsx5("button", { type: "submit", className: "builder-button builder-button--primary", children: "\u0425\u0430\u0434\u0433\u0430\u043B\u0430\u0445" })
954
+ /* @__PURE__ */ jsx5("button", { type: "submit", className: "builder-button builder-button--primary", children: editing ? "\u0428\u0438\u043D\u044D\u0447\u043B\u044D\u0445" : "\u041D\u044D\u043C\u044D\u0445" })
944
955
  ] })
945
956
  ] }) }) }) : null
946
957
  ] });
@@ -948,7 +959,7 @@ function ReadySubform({ subform, parentEntry, rows, onRowsChange }) {
948
959
 
949
960
  // src/ready/BuilderRuntimeComponents.tsx
950
961
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
951
- function BuilderForm({ formId }) {
962
+ function DefaultBuilderForm({ formId }) {
952
963
  const { hooks, notify } = useBuilderRuntime();
953
964
  const query = hooks.useRuntimeBuilderForm(formId);
954
965
  const save = hooks.useSaveRuntimeBuilderFormEntry(formId);
@@ -974,7 +985,7 @@ function BuilderForm({ formId }) {
974
985
  ] }) })
975
986
  ] });
976
987
  }
977
- function BuilderGrid({ listId }) {
988
+ function DefaultBuilderGrid({ listId }) {
978
989
  const { hooks } = useBuilderRuntime();
979
990
  const listQuery = hooks.useRuntimeBuilderList(listId);
980
991
  const state = useReadyTableState();
@@ -1000,7 +1011,7 @@ function childPayload(row, includeIdentity) {
1000
1011
  }
1001
1012
  function BuilderEntryEditor({ view, editing, open, onClose }) {
1002
1013
  const { hooks, notify } = useBuilderRuntime();
1003
- const [subforms, setSubforms] = useState4({});
1014
+ const [subforms, setSubforms] = useState5({});
1004
1015
  const save = hooks.useSaveBuilderViewEntry(view.id);
1005
1016
  const createComposite = hooks.useCreateCompositeBuilderViewEntry(view.id);
1006
1017
  const updateComposite = hooks.useUpdateCompositeBuilderViewEntry(view.id);
@@ -1041,11 +1052,11 @@ function BuilderEntryEditor({ view, editing, open, onClose }) {
1041
1052
  ] })
1042
1053
  ] }) }) : /* @__PURE__ */ jsx6(ReadyErrorState, { title: "Form \u0445\u043E\u043B\u0431\u043E\u043E\u0433\u04AF\u0439", description: "\u042D\u043D\u044D view-\u0434 form \u0442\u043E\u0445\u0438\u0440\u0443\u0443\u043B\u0430\u0430\u0433\u04AF\u0439 \u0431\u0430\u0439\u043D\u0430." }) });
1043
1054
  }
1044
- function BuilderView({ viewId }) {
1055
+ function DefaultBuilderView({ viewId }) {
1045
1056
  const { hooks, notify } = useBuilderRuntime();
1046
- const [editing, setEditing] = useState4(null);
1047
- const [editorOpen, setEditorOpen] = useState4(false);
1048
- const [pendingDelete, setPendingDelete] = useState4(null);
1057
+ const [editing, setEditing] = useState5(null);
1058
+ const [editorOpen, setEditorOpen] = useState5(false);
1059
+ const [pendingDelete, setPendingDelete] = useState5(null);
1049
1060
  const viewQuery = hooks.useRuntimeBuilderView(viewId);
1050
1061
  const state = useReadyTableState();
1051
1062
  const entriesQuery = hooks.useBuilderViewEntries(viewId, state.query);
@@ -1058,6 +1069,7 @@ function BuilderView({ viewId }) {
1058
1069
  key: "_actions",
1059
1070
  label: "\u04AE\u0439\u043B\u0434\u044D\u043B",
1060
1071
  sortable: false,
1072
+ hidden: false,
1061
1073
  align: "right",
1062
1074
  render: (entry) => /* @__PURE__ */ jsxs6("div", { className: "builder-row-actions", "data-builder-row-action": true, children: [
1063
1075
  /* @__PURE__ */ jsx6("button", { type: "button", className: "builder-button builder-button--ghost", onClick: (event) => {
@@ -1116,6 +1128,21 @@ function BuilderView({ viewId }) {
1116
1128
  )
1117
1129
  ] });
1118
1130
  }
1131
+ function BuilderForm(props) {
1132
+ const { components } = useBuilderRuntime();
1133
+ const Component = components.Form;
1134
+ return Component ? /* @__PURE__ */ jsx6(Component, { ...props }) : /* @__PURE__ */ jsx6(DefaultBuilderForm, { ...props });
1135
+ }
1136
+ function BuilderGrid(props) {
1137
+ const { components } = useBuilderRuntime();
1138
+ const Component = components.Grid;
1139
+ return Component ? /* @__PURE__ */ jsx6(Component, { ...props }) : /* @__PURE__ */ jsx6(DefaultBuilderGrid, { ...props });
1140
+ }
1141
+ function BuilderView(props) {
1142
+ const { components } = useBuilderRuntime();
1143
+ const Component = components.View;
1144
+ return Component ? /* @__PURE__ */ jsx6(Component, { ...props }) : /* @__PURE__ */ jsx6(DefaultBuilderView, { ...props });
1145
+ }
1119
1146
 
1120
1147
  // src/admin/editorState.ts
1121
1148
  var BUILDER_EDITOR_NONE_VALUE = "__none";
@@ -1271,16 +1298,19 @@ function builderFormEditorValuesFromEntity(form, emptyValues) {
1271
1298
  function builderListEditorValuesFromEntity(list, emptyValues) {
1272
1299
  if (!list) return emptyValues;
1273
1300
  const source = list.source;
1301
+ const orderBy = list.schema.orderBy ? Array.isArray(list.schema.orderBy) ? list.schema.orderBy : [list.schema.orderBy] : [];
1274
1302
  return {
1275
1303
  title: list.title,
1276
1304
  description: list.description ?? "",
1277
1305
  sourceType: source?.type ?? "none",
1278
1306
  sourceTable: source?.type === "dbTable" ? source.tableName : BUILDER_EDITOR_NONE_VALUE,
1279
1307
  apiBasePath: source?.type === "apiEndpoint" ? source.basePath : "",
1308
+ orderBy,
1280
1309
  columns: list.schema.columns.map((column) => ({
1281
1310
  key: column.key,
1282
1311
  label: column.label,
1283
1312
  sortable: column.sortable,
1313
+ hidden: column.hidden ?? false,
1284
1314
  align: column.align
1285
1315
  })),
1286
1316
  triggers: valuesFromTriggers(list.triggers),
@@ -1325,6 +1355,7 @@ function builderListEditorColumnsFromTable(table) {
1325
1355
  key: column.name,
1326
1356
  label: column.label,
1327
1357
  sortable: column.sortable,
1358
+ hidden: false,
1328
1359
  align: column.fieldType === "number" || column.fieldType === "money" ? "right" : "left"
1329
1360
  }));
1330
1361
  }
@@ -1396,11 +1427,19 @@ function builderFormEditorInputFromValues(values) {
1396
1427
  }
1397
1428
  function builderListEditorInputFromValues(values) {
1398
1429
  const source = values.sourceType === "dbTable" && values.sourceTable !== BUILDER_EDITOR_NONE_VALUE ? { type: "dbTable", tableName: values.sourceTable } : values.sourceType === "apiEndpoint" && values.apiBasePath ? { type: "apiEndpoint", basePath: values.apiBasePath } : null;
1430
+ const orderBy = Array.from(
1431
+ new Map(
1432
+ values.orderBy.filter((rule) => rule.field && rule.field !== BUILDER_EDITOR_NONE_VALUE).map((rule) => [rule.field, rule])
1433
+ ).values()
1434
+ );
1399
1435
  return {
1400
1436
  title: values.title,
1401
1437
  description: values.description || null,
1402
1438
  source,
1403
- schema: { columns: values.columns },
1439
+ schema: {
1440
+ columns: values.columns,
1441
+ ...source && orderBy.length > 0 ? { orderBy } : {}
1442
+ },
1404
1443
  triggers: values.triggers.map((trigger) => ({
1405
1444
  event: trigger.event,
1406
1445
  action: "callApi",
@@ -1603,10 +1642,18 @@ function createBuilderDefaultFormEditorBody(options) {
1603
1642
  ] }),
1604
1643
  sourceType === "dbTable" ? /* @__PURE__ */ jsxs7("label", { style: styles.field, children: [
1605
1644
  /* @__PURE__ */ jsx7("span", { style: styles.label, children: "Table" }),
1606
- /* @__PURE__ */ jsxs7("select", { style: styles.input, ...form.register("sourceTable"), children: [
1607
- /* @__PURE__ */ jsx7("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select table" }),
1608
- tables.map((table) => /* @__PURE__ */ jsx7("option", { value: table.name, children: table.label ?? table.name }, table.name))
1609
- ] })
1645
+ /* @__PURE__ */ jsxs7(
1646
+ "select",
1647
+ {
1648
+ style: styles.input,
1649
+ value: sourceTable,
1650
+ ...form.register("sourceTable"),
1651
+ children: [
1652
+ /* @__PURE__ */ jsx7("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select table" }),
1653
+ tables.map((table) => /* @__PURE__ */ jsx7("option", { value: table.name, children: table.label ?? table.name }, table.name))
1654
+ ]
1655
+ }
1656
+ )
1610
1657
  ] }) : null,
1611
1658
  sourceType === "apiEndpoint" ? /* @__PURE__ */ jsxs7("label", { style: styles.field, children: [
1612
1659
  /* @__PURE__ */ jsx7("span", { style: styles.label, children: "API base path" }),
@@ -1864,6 +1911,7 @@ function createBuilderDefaultListEditorBody(options) {
1864
1911
  listId,
1865
1912
  form,
1866
1913
  columnArray,
1914
+ orderByArray,
1867
1915
  triggerArray,
1868
1916
  sourceType,
1869
1917
  sourceTable,
@@ -1873,6 +1921,8 @@ function createBuilderDefaultListEditorBody(options) {
1873
1921
  isSaving,
1874
1922
  importColumnsFromTable
1875
1923
  }) {
1924
+ const selectedTable = tables.find((table) => table.name === sourceTable);
1925
+ const orderByColumns = selectedTable?.columns.filter((column) => column.sortable) ?? [];
1876
1926
  return /* @__PURE__ */ jsxs7("div", { style: styles.page, children: [
1877
1927
  /* @__PURE__ */ jsxs7("div", { style: styles.header, children: [
1878
1928
  /* @__PURE__ */ jsxs7("div", { children: [
@@ -1905,10 +1955,18 @@ function createBuilderDefaultListEditorBody(options) {
1905
1955
  ] }),
1906
1956
  sourceType === "dbTable" ? /* @__PURE__ */ jsxs7("label", { style: styles.field, children: [
1907
1957
  /* @__PURE__ */ jsx7("span", { style: styles.label, children: "Table" }),
1908
- /* @__PURE__ */ jsxs7("select", { style: styles.input, ...form.register("sourceTable"), children: [
1909
- /* @__PURE__ */ jsx7("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select table" }),
1910
- tables.map((table) => /* @__PURE__ */ jsx7("option", { value: table.name, children: table.label ?? table.name }, table.name))
1911
- ] })
1958
+ /* @__PURE__ */ jsxs7(
1959
+ "select",
1960
+ {
1961
+ style: styles.input,
1962
+ value: sourceTable,
1963
+ ...form.register("sourceTable"),
1964
+ children: [
1965
+ /* @__PURE__ */ jsx7("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select table" }),
1966
+ tables.map((table) => /* @__PURE__ */ jsx7("option", { value: table.name, children: table.label ?? table.name }, table.name))
1967
+ ]
1968
+ }
1969
+ )
1912
1970
  ] }) : null,
1913
1971
  sourceType === "apiEndpoint" ? /* @__PURE__ */ jsxs7("label", { style: styles.field, children: [
1914
1972
  /* @__PURE__ */ jsx7("span", { style: styles.label, children: "API base path" }),
@@ -1926,6 +1984,85 @@ function createBuilderDefaultListEditorBody(options) {
1926
1984
  ) }) : null
1927
1985
  ] })
1928
1986
  ] }),
1987
+ /* @__PURE__ */ jsxs7("section", { style: styles.section, children: [
1988
+ /* @__PURE__ */ jsxs7("div", { style: styles.sectionHeader, children: [
1989
+ /* @__PURE__ */ jsxs7("div", { children: [
1990
+ /* @__PURE__ */ jsx7("h2", { style: styles.sectionTitle, children: "Default ordering" }),
1991
+ /* @__PURE__ */ jsx7("p", { style: styles.description, children: "Rules are applied from top to bottom." })
1992
+ ] }),
1993
+ /* @__PURE__ */ jsx7(
1994
+ "button",
1995
+ {
1996
+ type: "button",
1997
+ style: styles.button,
1998
+ disabled: sourceType === "none" || orderByArray.fields.length >= 8,
1999
+ onClick: () => orderByArray.append({
2000
+ field: sourceType === "dbTable" ? orderByColumns[0]?.name ?? BUILDER_EDITOR_NONE_VALUE : "",
2001
+ direction: "desc"
2002
+ }),
2003
+ children: "Add ordering"
2004
+ }
2005
+ )
2006
+ ] }),
2007
+ orderByArray.fields.length === 0 ? /* @__PURE__ */ jsx7("div", { style: styles.grid, children: /* @__PURE__ */ jsx7("p", { style: styles.description, children: "Automatic ordering is used." }) }) : null,
2008
+ orderByArray.fields.map((rule, index) => /* @__PURE__ */ jsxs7("div", { style: styles.row, children: [
2009
+ sourceType === "dbTable" ? /* @__PURE__ */ jsxs7("label", { style: styles.field, children: [
2010
+ /* @__PURE__ */ jsx7("span", { style: styles.label, children: "Order by field" }),
2011
+ /* @__PURE__ */ jsxs7("select", { style: styles.input, ...form.register(`orderBy.${index}.field`), children: [
2012
+ /* @__PURE__ */ jsx7("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select field" }),
2013
+ orderByColumns.map((column) => /* @__PURE__ */ jsx7("option", { value: column.name, children: column.label }, column.name))
2014
+ ] })
2015
+ ] }) : /* @__PURE__ */ jsxs7("label", { style: styles.field, children: [
2016
+ /* @__PURE__ */ jsx7("span", { style: styles.label, children: "Order by field" }),
2017
+ /* @__PURE__ */ jsx7(
2018
+ "input",
2019
+ {
2020
+ style: styles.input,
2021
+ placeholder: "createdAt",
2022
+ ...form.register(`orderBy.${index}.field`)
2023
+ }
2024
+ )
2025
+ ] }),
2026
+ /* @__PURE__ */ jsxs7("label", { style: styles.field, children: [
2027
+ /* @__PURE__ */ jsx7("span", { style: styles.label, children: "Direction" }),
2028
+ /* @__PURE__ */ jsxs7("select", { style: styles.input, ...form.register(`orderBy.${index}.direction`), children: [
2029
+ /* @__PURE__ */ jsx7("option", { value: "asc", children: "Ascending" }),
2030
+ /* @__PURE__ */ jsx7("option", { value: "desc", children: "Descending" })
2031
+ ] })
2032
+ ] }),
2033
+ /* @__PURE__ */ jsxs7("div", { style: styles.actions, children: [
2034
+ /* @__PURE__ */ jsx7(
2035
+ "button",
2036
+ {
2037
+ type: "button",
2038
+ style: styles.button,
2039
+ disabled: index === 0,
2040
+ onClick: () => orderByArray.move(index, index - 1),
2041
+ children: "Move up"
2042
+ }
2043
+ ),
2044
+ /* @__PURE__ */ jsx7(
2045
+ "button",
2046
+ {
2047
+ type: "button",
2048
+ style: styles.button,
2049
+ disabled: index === orderByArray.fields.length - 1,
2050
+ onClick: () => orderByArray.move(index, index + 1),
2051
+ children: "Move down"
2052
+ }
2053
+ ),
2054
+ /* @__PURE__ */ jsx7(
2055
+ "button",
2056
+ {
2057
+ type: "button",
2058
+ style: styles.button,
2059
+ onClick: () => orderByArray.remove(index),
2060
+ children: "Remove"
2061
+ }
2062
+ )
2063
+ ] })
2064
+ ] }, rule.id))
2065
+ ] }),
1929
2066
  /* @__PURE__ */ jsxs7("section", { style: styles.section, children: [
1930
2067
  /* @__PURE__ */ jsxs7("div", { style: styles.sectionHeader, children: [
1931
2068
  /* @__PURE__ */ jsxs7("div", { children: [
@@ -1966,16 +2103,10 @@ function createBuilderDefaultListEditorBody(options) {
1966
2103
  /* @__PURE__ */ jsx7("input", { type: "checkbox", ...form.register(`columns.${index}.sortable`) }),
1967
2104
  "Sortable"
1968
2105
  ] }),
1969
- /* @__PURE__ */ jsx7(
1970
- "button",
1971
- {
1972
- type: "button",
1973
- style: styles.button,
1974
- onClick: () => columnArray.remove(index),
1975
- disabled: columnArray.fields.length <= 1,
1976
- children: "Remove"
1977
- }
1978
- )
2106
+ /* @__PURE__ */ jsxs7("label", { style: styles.checkboxField, children: [
2107
+ /* @__PURE__ */ jsx7("input", { type: "checkbox", ...form.register(`columns.${index}.hidden`) }),
2108
+ "Hidden"
2109
+ ] })
1979
2110
  ] }, column.id))
1980
2111
  ] }),
1981
2112
  /* @__PURE__ */ jsxs7("section", { style: styles.section, children: [
@@ -2020,7 +2151,7 @@ function createBuilderDefaultListEditorBody(options) {
2020
2151
  }
2021
2152
 
2022
2153
  // src/admin/BuilderDefaultEntityManager.tsx
2023
- import { useMemo as useMemo5, useState as useState5 } from "react";
2154
+ import { useMemo as useMemo5, useState as useState6 } from "react";
2024
2155
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2025
2156
  var styles2 = {
2026
2157
  page: {
@@ -2117,7 +2248,7 @@ var styles2 = {
2117
2248
  };
2118
2249
  function createBuilderDefaultEntityManagerComponent(deps) {
2119
2250
  return function BuilderDefaultEntityManager() {
2120
- const [state, setState] = useState5({
2251
+ const [state, setState] = useState6({
2121
2252
  page: 1,
2122
2253
  pageSize: 20,
2123
2254
  search: ""
@@ -2504,6 +2635,7 @@ function createBuilderListEditorPageComponent(deps) {
2504
2635
  defaultValues: deps.emptyValues
2505
2636
  });
2506
2637
  const columnArray = useFieldArray({ control: form.control, name: "columns" });
2638
+ const orderByArray = useFieldArray({ control: form.control, name: "orderBy" });
2507
2639
  const triggerArray = useFieldArray({ control: form.control, name: "triggers" });
2508
2640
  const sourceType = useWatch2({ control: form.control, name: "sourceType" });
2509
2641
  const sourceTable = useWatch2({ control: form.control, name: "sourceTable" });
@@ -2537,6 +2669,7 @@ function createBuilderListEditorPageComponent(deps) {
2537
2669
  listId,
2538
2670
  form,
2539
2671
  columnArray,
2672
+ orderByArray,
2540
2673
  triggerArray,
2541
2674
  sourceType,
2542
2675
  sourceTable,
@@ -2699,6 +2832,7 @@ function createBuilderAdminPages({
2699
2832
  key: "name",
2700
2833
  label: "Name",
2701
2834
  sortable: true,
2835
+ hidden: false,
2702
2836
  align: "left"
2703
2837
  };
2704
2838
  const emptyListValues = {
@@ -2707,6 +2841,7 @@ function createBuilderAdminPages({
2707
2841
  sourceType: "none",
2708
2842
  sourceTable: BUILDER_EDITOR_NONE_VALUE,
2709
2843
  apiBasePath: "",
2844
+ orderBy: [],
2710
2845
  columns: [emptyColumn],
2711
2846
  triggers: [],
2712
2847
  version: 0
@@ -2940,11 +3075,11 @@ function createBuilderAdminPages({
2940
3075
  }
2941
3076
 
2942
3077
  // src/admin/BuilderEntityManager.tsx
2943
- import { useCallback as useCallback2, useMemo as useMemo6, useState as useState6 } from "react";
3078
+ import { useCallback as useCallback2, useMemo as useMemo6, useState as useState7 } from "react";
2944
3079
  import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2945
3080
  function createBuilderEntityManagerComponent(deps) {
2946
3081
  return function BuilderEntityManager() {
2947
- const [pendingDelete, setPendingDelete] = useState6(null);
3082
+ const [pendingDelete, setPendingDelete] = useState7(null);
2948
3083
  const state = deps.useDataTableState({ pageSize: 20 });
2949
3084
  const query = deps.hooks.useEntities(state.query);
2950
3085
  const remove = deps.hooks.useDelete();
@@ -3004,9 +3139,9 @@ function createBuilderEntityManagerComponent(deps) {
3004
3139
  }
3005
3140
  function createBuilderEntityDialogManagerComponent(deps) {
3006
3141
  return function BuilderEntityDialogManager() {
3007
- const [editing, setEditing] = useState6(null);
3008
- const [isEditorOpen, setEditorOpen] = useState6(false);
3009
- const [pendingDelete, setPendingDelete] = useState6(null);
3142
+ const [editing, setEditing] = useState7(null);
3143
+ const [isEditorOpen, setEditorOpen] = useState7(false);
3144
+ const [pendingDelete, setPendingDelete] = useState7(null);
3010
3145
  const state = deps.useDataTableState({ pageSize: 20 });
3011
3146
  const query = deps.hooks.useEntities(state.query);
3012
3147
  const remove = deps.hooks.useDelete();
@@ -3376,7 +3511,7 @@ function createDynamicListComponent(deps) {
3376
3511
  }
3377
3512
 
3378
3513
  // src/components/DynamicView.tsx
3379
- import { useMemo as useMemo10, useState as useState7 } from "react";
3514
+ import { useMemo as useMemo10, useState as useState8 } from "react";
3380
3515
  import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
3381
3516
  var interactiveRowTargetSelector = "button,a,input,select,textarea,[role='button'],[role='menuitem'],[data-builder-row-action]";
3382
3517
  function hasClosest(target) {
@@ -3389,9 +3524,9 @@ function shouldHandleDynamicViewRowClick(event) {
3389
3524
  }
3390
3525
  function createDynamicViewComponent(deps) {
3391
3526
  return function DynamicView({ viewId }) {
3392
- const [editing, setEditing] = useState7(null);
3393
- const [isDialogOpen, setDialogOpen] = useState7(false);
3394
- const [pendingDelete, setPendingDelete] = useState7(null);
3527
+ const [editing, setEditing] = useState8(null);
3528
+ const [isDialogOpen, setDialogOpen] = useState8(false);
3529
+ const [pendingDelete, setPendingDelete] = useState8(null);
3395
3530
  const viewQuery = deps.hooks.useRuntimeBuilderView(viewId);
3396
3531
  const state = deps.useDataTableState({ pageSize: 20 });
3397
3532
  const entriesQuery = deps.hooks.useBuilderViewEntries(viewId, state.query);
@@ -3485,7 +3620,7 @@ function createDynamicViewComponent(deps) {
3485
3620
  }
3486
3621
 
3487
3622
  // src/components/RuntimeEntryDialog.tsx
3488
- import { useEffect as useEffect6, useMemo as useMemo11, useState as useState8 } from "react";
3623
+ import { useEffect as useEffect6, useMemo as useMemo11, useState as useState9 } from "react";
3489
3624
  import { FormProvider as FormProvider6, useForm as useForm7 } from "react-hook-form";
3490
3625
  import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
3491
3626
  function isDbTableSource(source) {
@@ -3520,7 +3655,7 @@ function createRuntimeEntryDialogComponent(deps) {
3520
3655
  onSave,
3521
3656
  isSaving
3522
3657
  }) {
3523
- const [draftSubforms, setDraftSubforms] = useState8({});
3658
+ const [draftSubforms, setDraftSubforms] = useState9({});
3524
3659
  const compositeCreate = deps.useCreateCompositeEntry?.(view.id);
3525
3660
  const compositeUpdate = deps.useUpdateCompositeEntry?.(view.id);
3526
3661
  const form = useForm7({
@@ -3706,7 +3841,7 @@ function validateBuilderFrontendSetup(config) {
3706
3841
  // src/index.ts
3707
3842
  var builderUiPackage = {
3708
3843
  name: "@duke333/builder-ui",
3709
- version: "0.2.1"
3844
+ version: "0.2.2"
3710
3845
  };
3711
3846
  export {
3712
3847
  BUILDER_EDITOR_NONE_VALUE,