@openg2p/registry-widgets 1.1.6-dev.3 → 1.1.6-dev.4

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.esm.js CHANGED
@@ -1686,12 +1686,13 @@ function useWidgetTheme() {
1686
1686
  const WidgetContext = createContext({
1687
1687
  dataSourceRequestHandler: undefined,
1688
1688
  schemaData: undefined,
1689
+ hostContext: undefined,
1689
1690
  t: undefined,
1690
1691
  });
1691
1692
  const useWidgetContext = () => {
1692
1693
  return useContext(WidgetContext);
1693
1694
  };
1694
- const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, t, theme, children, }) => {
1695
+ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, hostContext, t, theme, children, }) => {
1695
1696
  const widgetStore = useMemo(() => store || createWidgetStore(), [store]);
1696
1697
  const eventBus = useMemo(() => new WidgetEventBus(), []);
1697
1698
  const resolvedTheme = useMemo(() => resolveTheme(theme), [theme]);
@@ -1699,8 +1700,9 @@ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, t, theme,
1699
1700
  const contextValue = useMemo(() => ({
1700
1701
  dataSourceRequestHandler,
1701
1702
  schemaData,
1703
+ hostContext,
1702
1704
  t,
1703
- }), [dataSourceRequestHandler, schemaData, t]);
1705
+ }), [dataSourceRequestHandler, schemaData, hostContext, t]);
1704
1706
  useEffect(() => {
1705
1707
  if (!dataSourceRequestHandler) {
1706
1708
  console.warn('[WidgetProvider] dataSourceRequestHandler is not provided. ' +
@@ -1848,7 +1850,7 @@ const useBaseWidget = (options) => {
1848
1850
  // This prevents data disappearance when switching to Edit mode and components
1849
1851
  // incorrectly clear values before options load or if handler is temporarily missing.
1850
1852
  if (newValue === '' || newValue === null || newValue === undefined) {
1851
- const allowEmptyClear = config.widget === 'register-lookup';
1853
+ const allowEmptyClear = config.widget === 'register-lookup' || config.widget === 'parent-lookup';
1852
1854
  if (!allowEmptyClear) {
1853
1855
  if (loadingRef.current) {
1854
1856
  console.warn(`[useBaseWidget] Ignoring empty value for ${widgetId} because data source is loading`);
@@ -4387,6 +4389,8 @@ const WIDGET_TYPES = [
4387
4389
  'display',
4388
4390
  'profile',
4389
4391
  'geo-hierarchy',
4392
+ 'register-lookup',
4393
+ 'parent-lookup',
4390
4394
  ];
4391
4395
  const ORIENTATIONS = ['horizontal', 'vertical'];
4392
4396
  const CONDITION_OPERATORS = [
@@ -7010,7 +7014,18 @@ const SelectWidget = ({ config }) => {
7010
7014
  : (value != null && value !== '' ? tSchema(t, String(value)) : '-');
7011
7015
  return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] SelectDisplayWidget flex flex-col sm:flex-row sm:items-start", children: [label && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1", children: jsxRuntimeExports.jsx("div", { className: "text-base text-gray-900 font-medium", title: String(displayValue ?? ''), children: displayValue }) })] }));
7012
7016
  }
7013
- return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: tSchema(t, widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", { value: value || '', onChange: (e) => onChange(e.target.value === '' ? undefined : e.target.value), onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
7017
+ return (jsxRuntimeExports.jsx("div", { className: "mb-[10px]", children: jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: tSchema(t, widgetConfig['widget-label']), required: isRequired }), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntimeExports.jsxs("select", {
7018
+ // value={value || ''}
7019
+ // onChange={(e) => onChange(e.target.value === '' ? undefined : e.target.value)}
7020
+ value: value === undefined || value === null ? '' : String(value), onChange: (e) => {
7021
+ const rawValue = e.target.value;
7022
+ if (rawValue === '') {
7023
+ onChange(undefined);
7024
+ return;
7025
+ }
7026
+ const selectedOption = dataSourceOptions.find((option) => String(option.value) === rawValue);
7027
+ onChange(selectedOption ? selectedOption.value : rawValue);
7028
+ }, onBlur: onBlur, disabled: !isEnabled || loading || widgetConfig['widget-readonly'], className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${(touched && error.length > 0) || (widgetConfig['widget-required'] && (!value || value === ''))
7014
7029
  ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
7015
7030
  : 'border-gray-300'} ${!isEnabled || loading || widgetConfig['widget-readonly'] ? 'bg-gray-100 cursor-not-allowed' : 'bg-white'}`, style: { borderRadius: '10px' }, title: tSchema(t, widgetConfig['widget-data-tooltip']), children: [jsxRuntimeExports.jsx("option", { value: "", children: t?.('common.select') }), dataSourceOptions.map((option) => (jsxRuntimeExports.jsx("option", { value: option.value, children: tSchema(t, option.label) }, option.value)))] }), loading && (jsxRuntimeExports.jsx("p", { className: "text-sm text-gray-500 mt-1", children: t?.('common.loadingOptions') })), touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] }) }));
7016
7031
  };
@@ -7397,18 +7412,46 @@ const TableWidget = ({ config }) => {
7397
7412
  const [originalRows, setOriginalRows] = useState(null);
7398
7413
  const isSectionEditMode = !isReadonly && operations.edit;
7399
7414
  const isAnyRowEditing = editingState !== null || isAdding;
7415
+ const resolveEditingRowData = useCallback(() => {
7416
+ if (!editingState)
7417
+ return null;
7418
+ const row = { ...editingState.currentValue };
7419
+ columns.forEach((col) => {
7420
+ const key = col['column-key'];
7421
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${editingState.rowIndex}-col-${key}`;
7422
+ if (storeValues[cellWidgetId] !== undefined) {
7423
+ row[key] = storeValues[cellWidgetId];
7424
+ }
7425
+ });
7426
+ return row;
7427
+ }, [editingState, columns, widgetConfig, storeValues]);
7428
+ const resolveNewRowData = useCallback(() => {
7429
+ if (!isAdding || !newRowData)
7430
+ return null;
7431
+ const row = { ...newRowData };
7432
+ columns.forEach((col) => {
7433
+ const key = col['column-key'];
7434
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rows.length}-col-${key}`;
7435
+ if (storeValues[cellWidgetId] !== undefined) {
7436
+ row[key] = storeValues[cellWidgetId];
7437
+ }
7438
+ });
7439
+ return row;
7440
+ }, [isAdding, newRowData, columns, widgetConfig, rows.length, storeValues]);
7400
7441
  const canSaveEditingRow = useMemo(() => {
7401
- if (!editingState) {
7442
+ const rowData = resolveEditingRowData();
7443
+ if (!rowData) {
7402
7444
  return false;
7403
7445
  }
7404
- return isTableRowDataValid(editingState.currentValue, columns, isReadonly, resolveSchemaLabel);
7405
- }, [editingState, columns, isReadonly, resolveSchemaLabel]);
7446
+ return isTableRowDataValid(rowData, columns, isReadonly, resolveSchemaLabel);
7447
+ }, [resolveEditingRowData, columns, isReadonly, resolveSchemaLabel]);
7406
7448
  const canSaveNewRow = useMemo(() => {
7407
- if (!isAdding || !newRowData) {
7449
+ const rowData = resolveNewRowData();
7450
+ if (!rowData) {
7408
7451
  return false;
7409
7452
  }
7410
- return isTableRowDataValid(newRowData, columns, isReadonly, resolveSchemaLabel);
7411
- }, [isAdding, newRowData, columns, isReadonly, resolveSchemaLabel]);
7453
+ return isTableRowDataValid(rowData, columns, isReadonly, resolveSchemaLabel);
7454
+ }, [resolveNewRowData, columns, isReadonly, resolveSchemaLabel]);
7412
7455
  const showConfirmation = useCallback((message, onConfirm, onCancel) => {
7413
7456
  setConfirmationState({
7414
7457
  show: true,
@@ -7467,29 +7510,41 @@ const TableWidget = ({ config }) => {
7467
7510
  }, [isAnyRowEditing, rows, showConfirmation, cancelEdit, t]);
7468
7511
  const updateCellValue = useCallback((columnKey, newValue, rowIndex) => {
7469
7512
  if (editingState && rowIndex !== undefined) {
7470
- setEditingState({
7471
- ...editingState,
7472
- currentValue: {
7473
- ...editingState.currentValue,
7474
- [columnKey]: newValue,
7475
- },
7513
+ setEditingState((prev) => {
7514
+ if (!prev || prev.rowIndex !== rowIndex)
7515
+ return prev;
7516
+ return {
7517
+ ...prev,
7518
+ currentValue: {
7519
+ ...prev.currentValue,
7520
+ [columnKey]: newValue,
7521
+ },
7522
+ };
7476
7523
  });
7477
7524
  const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7478
7525
  dispatch(setValue({ widgetId: cellWidgetId, value: newValue }));
7479
7526
  }
7480
- else if (isAdding && newRowData) {
7481
- setNewRowData({
7482
- ...newRowData,
7483
- [columnKey]: newValue,
7527
+ else if (isAdding) {
7528
+ setNewRowData((prev) => {
7529
+ if (!prev)
7530
+ return prev;
7531
+ return {
7532
+ ...prev,
7533
+ [columnKey]: newValue,
7534
+ };
7484
7535
  });
7536
+ if (rowIndex !== undefined) {
7537
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7538
+ dispatch(setValue({ widgetId: cellWidgetId, value: newValue }));
7539
+ }
7485
7540
  }
7486
- }, [editingState, isAdding, newRowData, widgetConfig, dispatch]);
7541
+ }, [editingState, isAdding, widgetConfig, dispatch]);
7487
7542
  const saveEdit = useCallback(async () => {
7488
- if (!editingState)
7543
+ const rowData = resolveEditingRowData();
7544
+ if (!editingState || !rowData)
7489
7545
  return;
7490
7546
  if (!canSaveEditingRow)
7491
7547
  return;
7492
- const rowData = editingState.currentValue;
7493
7548
  const rowIndex = editingState.rowIndex;
7494
7549
  setLoadingRowIndex(rowIndex);
7495
7550
  try {
@@ -7548,7 +7603,7 @@ const TableWidget = ({ config }) => {
7548
7603
  finally {
7549
7604
  setLoadingRowIndex(null);
7550
7605
  }
7551
- }, [editingState, canSaveEditingRow, rows, onChange, dataSourceRequestHandler, apiConfig, t, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
7606
+ }, [editingState, canSaveEditingRow, resolveEditingRowData, rows, onChange, dataSourceRequestHandler, apiConfig, t, isSectionEditMode, originalRows, columns, widgetConfig, dispatch]);
7552
7607
  const startAdd = useCallback(() => {
7553
7608
  if (isAnyRowEditing) {
7554
7609
  cancelEdit();
@@ -7561,23 +7616,26 @@ const TableWidget = ({ config }) => {
7561
7616
  setNewRowData(emptyRow);
7562
7617
  }, [isAnyRowEditing, columns, cancelEdit]);
7563
7618
  const saveAdd = useCallback(async () => {
7564
- if (!isAdding || !newRowData)
7619
+ const rowData = resolveNewRowData();
7620
+ if (!isAdding || !rowData)
7565
7621
  return;
7566
7622
  if (!canSaveNewRow)
7567
7623
  return;
7568
- setLoadingRowIndex(-1); // Use -1 to indicate new row
7624
+ setLoadingRowIndex(-1);
7569
7625
  try {
7570
- let savedRow = { ...newRowData };
7571
- // TODO: Update to use dataSourceRequestHandler pattern
7626
+ let savedRow = { ...rowData };
7572
7627
  if (dataSourceRequestHandler && apiConfig.add) {
7573
7628
  console.warn('[TableWidget] API add operations require migration to dataSourceRequestHandler pattern');
7574
- const response = newRowData;
7575
- if (response && typeof response === 'object') {
7576
- savedRow = { ...savedRow, ...response };
7629
+ if (rowData && typeof rowData === 'object') {
7630
+ savedRow = { ...savedRow, ...rowData };
7577
7631
  }
7578
7632
  }
7579
7633
  savedRow = { ...savedRow, edit_action: 'ADD' };
7580
7634
  onChange([...rows, savedRow]);
7635
+ columns.forEach((col) => {
7636
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rows.length}-col-${col['column-key']}`;
7637
+ dispatch(resetWidget(cellWidgetId));
7638
+ });
7581
7639
  setIsAdding(false);
7582
7640
  setNewRowData(null);
7583
7641
  }
@@ -7588,7 +7646,7 @@ const TableWidget = ({ config }) => {
7588
7646
  finally {
7589
7647
  setLoadingRowIndex(null);
7590
7648
  }
7591
- }, [isAdding, newRowData, canSaveNewRow, rows, onChange, dataSourceRequestHandler, apiConfig, t, isSectionEditMode]);
7649
+ }, [isAdding, resolveNewRowData, canSaveNewRow, rows, onChange, dataSourceRequestHandler, apiConfig, t, columns, widgetConfig, dispatch]);
7592
7650
  const deleteRow = useCallback(async (rowIndex) => {
7593
7651
  if (isAnyRowEditing) {
7594
7652
  showConfirmation(t?.('table.unsavedChanges') || 'You have unsaved changes. Do you want to discard them?', () => {
@@ -7639,6 +7697,11 @@ const TableWidget = ({ config }) => {
7639
7697
  return editingState.currentValue[columnKey];
7640
7698
  }
7641
7699
  if (isAdding && rowIndex === rows.length) {
7700
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7701
+ const storeValue = storeValues[cellWidgetId];
7702
+ if (storeValue !== undefined) {
7703
+ return storeValue;
7704
+ }
7642
7705
  return newRowData?.[columnKey];
7643
7706
  }
7644
7707
  return rows[rowIndex]?.[columnKey];
@@ -7653,6 +7716,9 @@ const TableWidget = ({ config }) => {
7653
7716
  if (widgetType === 'select') {
7654
7717
  return null; // Will be handled by SelectDisplayValue component
7655
7718
  }
7719
+ if (widgetType === 'parent-lookup') {
7720
+ return null;
7721
+ }
7656
7722
  if (column['widget-data-format']) {
7657
7723
  return formatValue(cellValue, column['widget-data-format'], column.widget);
7658
7724
  }
@@ -7669,7 +7735,14 @@ const TableWidget = ({ config }) => {
7669
7735
  setOriginalRows(null);
7670
7736
  }
7671
7737
  }, [isSectionEditMode, rows, originalRows]);
7738
+ const editSessionKey = editingState
7739
+ ? `edit-${editingState.rowIndex}`
7740
+ : isAdding
7741
+ ? `add-${rows.length}`
7742
+ : null;
7672
7743
  useEffect(() => {
7744
+ if (!editSessionKey)
7745
+ return;
7673
7746
  if (editingState) {
7674
7747
  columns.forEach((col) => {
7675
7748
  const columnKey = col['column-key'];
@@ -7678,9 +7751,8 @@ const TableWidget = ({ config }) => {
7678
7751
  const defaultValue = cellValue !== undefined ? cellValue : (col['widget-data-default'] ?? '');
7679
7752
  dispatch(setValue({ widgetId: cellWidgetId, value: defaultValue }));
7680
7753
  });
7754
+ return;
7681
7755
  }
7682
- }, [editingState, columns, widgetConfig, dispatch]);
7683
- useEffect(() => {
7684
7756
  if (isAdding && newRowData) {
7685
7757
  columns.forEach((col) => {
7686
7758
  const columnKey = col['column-key'];
@@ -7690,7 +7762,9 @@ const TableWidget = ({ config }) => {
7690
7762
  dispatch(setValue({ widgetId: cellWidgetId, value: defaultValue }));
7691
7763
  });
7692
7764
  }
7693
- }, [isAdding, newRowData, columns, widgetConfig, rows.length, dispatch]);
7765
+ // Seed only when add/edit session starts — not on every cell change.
7766
+ // eslint-disable-next-line react-hooks/exhaustive-deps
7767
+ }, [editSessionKey]);
7694
7768
  const getRowValuesForEdit = useCallback((rowIndex) => {
7695
7769
  if (editingState && editingState.rowIndex === rowIndex) {
7696
7770
  return editingState.currentValue ?? {};
@@ -7770,6 +7844,20 @@ const TableWidget = ({ config }) => {
7770
7844
  };
7771
7845
  return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: jsxRuntimeExports.jsx(SelectDisplayValue$1, { config: cellConfig, value: cellValue }) }));
7772
7846
  }
7847
+ if (widgetType === 'parent-lookup' && displayValue === null) {
7848
+ const cellWidgetId = `${widgetConfig['widget-id']}-row-${rowIndex}-col-${columnKey}`;
7849
+ const cellConfig = {
7850
+ ...column,
7851
+ 'widget-id': cellWidgetId,
7852
+ 'widget-label': '',
7853
+ 'widget-readonly': true,
7854
+ 'widget-data-path': undefined,
7855
+ 'widget-data-default': cellValue !== undefined ? cellValue : '',
7856
+ };
7857
+ return (jsxRuntimeExports.jsx("div", { className: "text-sm table-cell-widget", style: getCellStyle(), children: jsxRuntimeExports.jsx(WidgetRenderer, { config: cellConfig, schemaData: {
7858
+ [cellWidgetId]: cellValue !== undefined ? cellValue : '',
7859
+ } }) }));
7860
+ }
7773
7861
  return (jsxRuntimeExports.jsx("div", { className: "text-sm", style: getCellStyle(), children: displayValue }));
7774
7862
  }
7775
7863
  }, [isRowEditing, getCellValue, getDisplayValue, renderTableCell]);
@@ -7865,7 +7953,7 @@ const TableWidget = ({ config }) => {
7865
7953
  border: '1px solid var(--owt-btn-primary-border, #F07B1A)',
7866
7954
  backgroundColor: 'var(--owt-color-primary, #F5BB1A)',
7867
7955
  color: 'var(--owt-color-bg, #FFFFFF)',
7868
- }, children: t?.('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: { borderRadius: 'var(--owt-widget-table-border-radius, 15px)', borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)' }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [columns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: tSchema(t, col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) || isAnyRowEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: t?.('common.actions') || 'Actions' })) : null] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && !isAdding && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: columns.length + (((operations.edit || operations.remove) && !isReadonly) || isSectionEditMode ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [t?.('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${t?.('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
7956
+ }, children: t?.('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: { borderRadius: 'var(--owt-widget-table-border-radius, 15px)', borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)' }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [columns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: tSchema(t, col['column-label'] || col['widget-label'] || col['column-key']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) || isAnyRowEditing || isSectionEditMode ? (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: t?.('common.actions') || 'Actions' })) : null] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && !isAdding && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: columns.length + (((operations.edit || operations.remove) && !isReadonly) || isSectionEditMode ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [t?.('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${t?.('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
7869
7957
  const isEditing = isRowEditing(rowIndex);
7870
7958
  const isLoading = loadingRowIndex === rowIndex;
7871
7959
  return (jsxRuntimeExports.jsxs("tr", { className: `${isLoading ? 'opacity-50' : ''}${isEditing ? ' table-row-editing' : ''}`, style: {
@@ -7987,7 +8075,7 @@ const DialogTableField = memo(function DialogTableField({ col, cellWidgetId, dia
7987
8075
  widget: widgetType,
7988
8076
  'widget-type': col['widget-type'] || 'input',
7989
8077
  'widget-id': cellWidgetId,
7990
- 'widget-label': col['widget-label'],
8078
+ 'widget-label': col['widget-label'] || col['column-label'] || col['column-key'] || '',
7991
8079
  'widget-readonly': isReadonly || col['widget-readonly'] === true,
7992
8080
  'widget-data-path': undefined,
7993
8081
  'widget-data-default': col['widget-data-default'],
@@ -8232,6 +8320,8 @@ const DialogTableWidget = ({ config }) => {
8232
8320
  return '-';
8233
8321
  if (widgetType === 'select')
8234
8322
  return null;
8323
+ if (widgetType === 'parent-lookup')
8324
+ return null;
8235
8325
  if (column['widget-data-format'])
8236
8326
  return formatValue(cellValue, column['widget-data-format'], column.widget);
8237
8327
  return String(cellValue);
@@ -8285,7 +8375,7 @@ const DialogTableWidget = ({ config }) => {
8285
8375
  }, children: t?.('table.addRecord') || 'Add New Record' }) })), jsxRuntimeExports.jsx("div", { className: "overflow-x-auto border", style: {
8286
8376
  borderRadius: 'var(--owt-widget-table-border-radius, 15px)',
8287
8377
  borderColor: 'var(--owt-widget-table-border-color, #C4C4C4)',
8288
- }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [visibleColumns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: tSchema(t, col['widget-label']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: t?.('common.actions') || 'Actions' }))] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: visibleColumns.length + (((operations.edit || operations.remove) && !isReadonly) ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [t?.('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${t?.('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
8378
+ }, children: jsxRuntimeExports.jsxs("table", { className: "min-w-full", style: { borderCollapse: 'separate', borderSpacing: 0 }, children: [jsxRuntimeExports.jsx("thead", { style: { backgroundColor: 'var(--owt-widget-table-header-bg, #F6F6F6)' }, children: jsxRuntimeExports.jsxs("tr", { style: { borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)' }, children: [visibleColumns.map((col) => (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: tSchema(t, col['column-label'] || col['widget-label'] || col['column-key']) }, col['column-key']))), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wider", style: { color: 'var(--owt-widget-table-header-color, #727474)' }, children: t?.('common.actions') || 'Actions' }))] }) }), jsxRuntimeExports.jsxs("tbody", { style: { backgroundColor: 'var(--owt-widget-table-body-bg, #FFFFFF)' }, children: [rows.length === 0 && (jsxRuntimeExports.jsx("tr", { children: jsxRuntimeExports.jsxs("td", { colSpan: visibleColumns.length + (((operations.edit || operations.remove) && !isReadonly) ? 1 : 0), className: "px-4 py-6 text-center text-sm", style: { color: 'var(--owt-widget-table-empty-color, #727474)' }, children: [t?.('table.noData') || 'No records available.', operations.add && !isReadonly && ` ${t?.('table.clickToAdd') || 'Click "Add New Record" to add one.'}`] }) })), rows.map((row, rowIndex) => {
8289
8379
  const cellStyle = getRowCellStyle(row?.edit_action);
8290
8380
  return (jsxRuntimeExports.jsxs("tr", { style: {
8291
8381
  borderBottom: '1px solid var(--owt-widget-table-row-divider, #E4E4E4)',
@@ -8306,6 +8396,18 @@ const DialogTableWidget = ({ config }) => {
8306
8396
  };
8307
8397
  return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", style: cellStyle, children: jsxRuntimeExports.jsx(SelectDisplayValue, { config: displayConfig, value: row?.[key] }) }) }, key));
8308
8398
  }
8399
+ if (widgetType === 'parent-lookup' && displayValue === null) {
8400
+ const cellWidgetId = `${widgetConfig['widget-id']}-view-row-${rowIndex}-col-${key}`;
8401
+ const displayConfig = {
8402
+ ...col,
8403
+ 'widget-id': cellWidgetId,
8404
+ 'widget-label': '',
8405
+ 'widget-readonly': true,
8406
+ 'widget-data-path': undefined,
8407
+ 'widget-data-default': row?.[key] ?? '',
8408
+ };
8409
+ return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm table-cell-widget", style: cellStyle, children: jsxRuntimeExports.jsx(WidgetRenderer, { config: displayConfig, schemaData: { [cellWidgetId]: row?.[key] ?? '' } }) }) }, key));
8410
+ }
8309
8411
  return (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: jsxRuntimeExports.jsx("div", { className: "text-sm", style: cellStyle, children: displayValue }) }, key));
8310
8412
  }), ((operations.edit || operations.remove) && !isReadonly) && (jsxRuntimeExports.jsx("td", { className: "px-4 py-3 whitespace-nowrap", style: { minWidth: '120px' }, children: jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [operations.edit && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => openEditDialog(rowIndex), disabled: !isEnabled, className: "px-3 py-1 text-xs disabled:opacity-50 disabled:cursor-not-allowed", style: {
8311
8413
  borderRadius: 'var(--owt-btn-border-radius, 10px)',
@@ -9880,7 +9982,7 @@ const normalizeDisplayFields = (row) => {
9880
9982
  value: f.value !== null && f.value !== undefined ? String(f.value) : '-',
9881
9983
  }));
9882
9984
  };
9883
- const parsePagination = (pagination, rowCount, size, fallbackPage = 1) => {
9985
+ const parsePagination$1 = (pagination, rowCount, size, fallbackPage = 1) => {
9884
9986
  const totalItems = typeof pagination.number_of_items === 'number' ? pagination.number_of_items : rowCount;
9885
9987
  const totalPages = typeof pagination.number_of_pages === 'number'
9886
9988
  ? Math.max(1, pagination.number_of_pages)
@@ -10031,7 +10133,7 @@ const RegisterLookupWidget = ({ config }) => {
10031
10133
  const match = rows.find((row) => String(row.internal_record_id ?? '').trim() === target);
10032
10134
  if (match)
10033
10135
  return match;
10034
- totalPages = parsePagination(pagination, rows.length, hydratePageSize).totalPages;
10136
+ totalPages = parsePagination$1(pagination, rows.length, hydratePageSize).totalPages;
10035
10137
  if (page >= totalPages)
10036
10138
  break;
10037
10139
  page += 1;
@@ -10041,7 +10143,7 @@ const RegisterLookupWidget = ({ config }) => {
10041
10143
  const runSearch = useCallback(async (text, page = 1) => {
10042
10144
  try {
10043
10145
  const { rows, pagination } = await fetchRecords(text, page, pageSize);
10044
- const parsed = parsePagination(pagination, rows.length, pageSize, page);
10146
+ const parsed = parsePagination$1(pagination, rows.length, pageSize, page);
10045
10147
  setSearchResults(rows);
10046
10148
  setTotalCount(parsed.totalItems);
10047
10149
  setTotalPages(parsed.totalPages);
@@ -10174,6 +10276,361 @@ const RegisterLookupWidget = ({ config }) => {
10174
10276
  : t?.('common.searchHint', { defaultValue: 'Type and press Enter or click search' }) })) : (jsxRuntimeExports.jsx(ResultsTable, { rows: searchResults, selectedRowKey: pendingRow?.internal_record_id ?? null, onRowClick: setPendingRow, onRowDoubleClick: applySelection })) }), jsxRuntimeExports.jsxs("div", { className: `flex-shrink-0 flex flex-wrap items-center gap-3 px-5 py-3 border-t border-gray-200 ${totalCount !== null ? 'justify-between' : 'justify-end'}`, children: [totalCount !== null && (jsxRuntimeExports.jsx(PaginationFooter, { embedded: true, currentPage: currentPage, totalPages: totalPages, totalCount: totalCount, pageSize: pageSize, onPageChange: (page) => runSearch(searchText, page), onPrev: () => currentPage > 1 && runSearch(searchText, currentPage - 1), onNext: () => currentPage < totalPages && runSearch(searchText, currentPage + 1) })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => pendingRow && applySelection(pendingRow), disabled: !pendingRow, className: "px-4 h-9 text-sm font-medium rounded-[10px] text-white disabled:opacity-40 disabled:cursor-not-allowed flex-shrink-0", style: { backgroundColor: 'var(--owt-color-info, #2563eb)' }, children: selectRecordLabel })] })] })] }))] }));
10175
10277
  };
10176
10278
 
10279
+ const parentLookupPageCache = new Map();
10280
+ const parentLookupPageInflight = new Map();
10281
+ const parentLookupRecordCache = new Map();
10282
+ const parsePagination = (pagination, rowCount, size, fallbackPage = 1) => {
10283
+ const totalItems = typeof pagination.number_of_items === 'number' ? pagination.number_of_items : rowCount;
10284
+ const totalPages = typeof pagination.number_of_pages === 'number'
10285
+ ? Math.max(1, pagination.number_of_pages)
10286
+ : totalItems > 0
10287
+ ? Math.max(1, Math.ceil(totalItems / size))
10288
+ : 1;
10289
+ return { totalItems, totalPages, currentPage: pagination.current_page ?? fallbackPage };
10290
+ };
10291
+ const parentLabel = (row) => {
10292
+ if (!row)
10293
+ return '';
10294
+ if (row.record_name != null && String(row.record_name).trim() !== '') {
10295
+ return String(row.record_name);
10296
+ }
10297
+ return String(row.internal_record_id ?? '');
10298
+ };
10299
+ const indexParentRecords = (rows) => {
10300
+ for (const row of rows) {
10301
+ const id = String(row.internal_record_id ?? '').trim();
10302
+ if (id) {
10303
+ parentLookupRecordCache.set(id, row);
10304
+ }
10305
+ }
10306
+ };
10307
+ const buildParentLookupCacheKey = (service, endpoint, method, params) => `${service}|${endpoint}|${method}|${JSON.stringify(params)}`;
10308
+ const fetchParentLookupPage = async (handler, service, endpoint, method, params, headers) => {
10309
+ const resolvedMethod = method || 'POST';
10310
+ const cacheKey = buildParentLookupCacheKey(service, endpoint, resolvedMethod, params);
10311
+ const cached = parentLookupPageCache.get(cacheKey);
10312
+ if (cached) {
10313
+ return cached;
10314
+ }
10315
+ const inflight = parentLookupPageInflight.get(cacheKey);
10316
+ if (inflight) {
10317
+ return inflight;
10318
+ }
10319
+ const fetchPromise = (async () => {
10320
+ const result = await handler(service, endpoint, resolvedMethod, params, { headers });
10321
+ const parsed = {
10322
+ rows: (result?.records ?? []),
10323
+ pagination: (result?.pagination ?? {}),
10324
+ };
10325
+ indexParentRecords(parsed.rows);
10326
+ parentLookupPageCache.set(cacheKey, parsed);
10327
+ return parsed;
10328
+ })();
10329
+ parentLookupPageInflight.set(cacheKey, fetchPromise);
10330
+ try {
10331
+ return await fetchPromise;
10332
+ }
10333
+ finally {
10334
+ parentLookupPageInflight.delete(cacheKey);
10335
+ }
10336
+ };
10337
+ const ParentLookupWidget = ({ config, value: valueProp, error: errorProp, touched: touchedProp, isEnabled: isEnabledProp, isRequired: isRequiredProp, onChange: onChangeProp, onBlur: onBlurProp, }) => {
10338
+ const hook = useBaseWidget({ config });
10339
+ // WidgetRenderer spreads its hook result as props (includes table onValueChange).
10340
+ // Prefer those so parent selection updates the table row / form state.
10341
+ const useInjected = typeof onChangeProp === 'function';
10342
+ const value = useInjected ? valueProp : hook.value;
10343
+ const error = useInjected ? (errorProp ?? hook.error) : hook.error;
10344
+ const touched = useInjected ? !!touchedProp : hook.touched;
10345
+ const isEnabled = useInjected ? (isEnabledProp ?? hook.isEnabled) : hook.isEnabled;
10346
+ const isRequired = useInjected ? !!isRequiredProp : hook.isRequired;
10347
+ const onChange = useInjected ? onChangeProp : hook.onChange;
10348
+ const onBlur = useInjected ? (onBlurProp ?? hook.onBlur) : hook.onBlur;
10349
+ const widgetConfig = config;
10350
+ const { t, dataSourceRequestHandler, hostContext } = useWidgetContext();
10351
+ const dataSource = widgetConfig['widget-data-source'];
10352
+ const lookupConfig = widgetConfig['widget-lookup-config'];
10353
+ const pageSize = lookupConfig?.page_size ?? 10;
10354
+ const isCompact = !widgetConfig['widget-label'];
10355
+ const requestParams = useMemo(() => {
10356
+ const merged = { ...(hostContext || {}) };
10357
+ const schemaParams = dataSource?.params || {};
10358
+ for (const [key, paramValue] of Object.entries(schemaParams)) {
10359
+ if (paramValue !== null && paramValue !== undefined && paramValue !== '') {
10360
+ merged[key] = paramValue;
10361
+ }
10362
+ }
10363
+ return merged;
10364
+ }, [hostContext, dataSource?.params]);
10365
+ const [isOpen, setIsOpen] = useState(false);
10366
+ const [searchText, setSearchText] = useState('');
10367
+ const [searchResults, setSearchResults] = useState([]);
10368
+ const [currentPage, setCurrentPage] = useState(1);
10369
+ const [totalPages, setTotalPages] = useState(1);
10370
+ const [totalCount, setTotalCount] = useState(null);
10371
+ const [pendingRow, setPendingRow] = useState(null);
10372
+ const [appliedRecord, setAppliedRecord] = useState(null);
10373
+ const [isHydrating, setIsHydrating] = useState(false);
10374
+ const [modalPos, setModalPos] = useState({ x: 80, y: 80 });
10375
+ const [modalSize, setModalSize] = useState({ w: 860, h: 520 });
10376
+ const isDragging = useRef(false);
10377
+ const dragOrigin = useRef({ mouseX: 0, mouseY: 0, posX: 0, posY: 0 });
10378
+ const searchInputRef = useRef(null);
10379
+ const hydratedValueRef = useRef(null);
10380
+ const fetchRecords = useCallback(async (text, page, size) => {
10381
+ if (!dataSource?.service || !dataSource?.endpoint || !dataSourceRequestHandler) {
10382
+ return { rows: [], pagination: {} };
10383
+ }
10384
+ return fetchParentLookupPage(dataSourceRequestHandler, dataSource.service, dataSource.endpoint, dataSource.method, {
10385
+ ...requestParams,
10386
+ search_text: text,
10387
+ current_page: page,
10388
+ page_size: size,
10389
+ }, dataSource.headers);
10390
+ }, [dataSource, dataSourceRequestHandler, requestParams]);
10391
+ const findRecordByValue = useCallback(async (recordValue) => {
10392
+ const target = String(recordValue).trim();
10393
+ if (!target)
10394
+ return null;
10395
+ const cachedRecord = parentLookupRecordCache.get(target);
10396
+ if (cachedRecord) {
10397
+ return cachedRecord;
10398
+ }
10399
+ const hydratePageSize = lookupConfig?.hydrate_page_size ?? 50;
10400
+ let page = 1;
10401
+ let pages = 1;
10402
+ while (page <= pages) {
10403
+ const { rows, pagination } = await fetchRecords('', page, hydratePageSize);
10404
+ const match = rows.find((row) => String(row.internal_record_id ?? '').trim() === target);
10405
+ if (match)
10406
+ return match;
10407
+ pages = parsePagination(pagination, rows.length, hydratePageSize).totalPages;
10408
+ if (page >= pages)
10409
+ break;
10410
+ page += 1;
10411
+ }
10412
+ return null;
10413
+ }, [fetchRecords, lookupConfig?.hydrate_page_size]);
10414
+ const runSearch = useCallback(async (text, page = 1) => {
10415
+ try {
10416
+ const { rows, pagination } = await fetchRecords(text, page, pageSize);
10417
+ const parsed = parsePagination(pagination, rows.length, pageSize, page);
10418
+ setSearchResults(rows);
10419
+ setTotalCount(parsed.totalItems);
10420
+ setTotalPages(parsed.totalPages);
10421
+ setCurrentPage(parsed.currentPage);
10422
+ }
10423
+ catch {
10424
+ setSearchResults([]);
10425
+ setTotalCount(null);
10426
+ setTotalPages(1);
10427
+ setCurrentPage(1);
10428
+ }
10429
+ }, [fetchRecords, pageSize]);
10430
+ useEffect(() => {
10431
+ const onMove = (e) => {
10432
+ if (!isDragging.current)
10433
+ return;
10434
+ setModalPos({
10435
+ x: dragOrigin.current.posX + (e.clientX - dragOrigin.current.mouseX),
10436
+ y: dragOrigin.current.posY + (e.clientY - dragOrigin.current.mouseY),
10437
+ });
10438
+ };
10439
+ const onUp = () => {
10440
+ isDragging.current = false;
10441
+ };
10442
+ document.addEventListener('mousemove', onMove);
10443
+ document.addEventListener('mouseup', onUp);
10444
+ return () => {
10445
+ document.removeEventListener('mousemove', onMove);
10446
+ document.removeEventListener('mouseup', onUp);
10447
+ };
10448
+ }, []);
10449
+ const hasValue = value !== null && value !== undefined && value !== '';
10450
+ const applySelection = (row) => {
10451
+ indexParentRecords([row]);
10452
+ hydratedValueRef.current = row.internal_record_id;
10453
+ onChange(row.internal_record_id);
10454
+ setAppliedRecord(row);
10455
+ setIsOpen(false);
10456
+ };
10457
+ const clearSelection = () => {
10458
+ hydratedValueRef.current = null;
10459
+ onChange(null);
10460
+ setAppliedRecord(null);
10461
+ setPendingRow(null);
10462
+ };
10463
+ const openLookup = () => {
10464
+ const w = Math.min(Math.round(window.innerWidth * 0.82), 940);
10465
+ const h = Math.min(Math.round(window.innerHeight * 0.72), 560);
10466
+ setModalPos({
10467
+ x: Math.round((window.innerWidth - w) / 2),
10468
+ y: Math.round((window.innerHeight - h) / 2),
10469
+ });
10470
+ setModalSize({ w, h });
10471
+ setIsOpen(true);
10472
+ setSearchText('');
10473
+ setSearchResults([]);
10474
+ setTotalCount(null);
10475
+ setCurrentPage(1);
10476
+ setTotalPages(1);
10477
+ setPendingRow(appliedRecord);
10478
+ setTimeout(() => searchInputRef.current?.focus(), 50);
10479
+ runSearch('', 1);
10480
+ };
10481
+ useEffect(() => {
10482
+ if (!hasValue) {
10483
+ hydratedValueRef.current = null;
10484
+ setAppliedRecord(null);
10485
+ setIsHydrating(false);
10486
+ return;
10487
+ }
10488
+ if (!dataSource?.service || !dataSource?.endpoint || !dataSourceRequestHandler)
10489
+ return;
10490
+ if (hydratedValueRef.current === value)
10491
+ return;
10492
+ let cancelled = false;
10493
+ setIsHydrating(true);
10494
+ setAppliedRecord(null);
10495
+ (async () => {
10496
+ try {
10497
+ const match = await findRecordByValue(value);
10498
+ if (cancelled)
10499
+ return;
10500
+ hydratedValueRef.current = value;
10501
+ setAppliedRecord(match ?? { internal_record_id: String(value) });
10502
+ }
10503
+ catch {
10504
+ if (!cancelled) {
10505
+ hydratedValueRef.current = value;
10506
+ setAppliedRecord({ internal_record_id: String(value) });
10507
+ }
10508
+ }
10509
+ finally {
10510
+ if (!cancelled)
10511
+ setIsHydrating(false);
10512
+ }
10513
+ })();
10514
+ return () => {
10515
+ cancelled = true;
10516
+ setIsHydrating(false);
10517
+ };
10518
+ }, [hasValue, value, dataSource, dataSourceRequestHandler, findRecordByValue]);
10519
+ const isReadonly = !!widgetConfig['widget-readonly'];
10520
+ const rawLabel = widgetConfig['widget-label'];
10521
+ const label = tSchema(t, rawLabel || 'Parent');
10522
+ const hasError = (touched && error.length > 0) || (!!widgetConfig['widget-required'] && !hasValue);
10523
+ const placeholder = tSchema(t, String(lookupConfig?.action_label ?? t?.('common.select') ?? 'Select'));
10524
+ const searchPlaceholder = tSchema(t, String(lookupConfig?.search_placeholder ?? 'Search parent...'));
10525
+ const selectRecordLabel = tSchema(t, String(lookupConfig?.select_record_label ?? 'Select Parent'));
10526
+ const displayName = isHydrating
10527
+ ? t?.('common.loading', { defaultValue: 'Loading...' })
10528
+ : parentLabel(appliedRecord) || (hasValue ? String(value) : '');
10529
+ const selectTrigger = isCompact ? (jsxRuntimeExports.jsxs("select", { value: hasValue ? '__selected__' : '', disabled: !isEnabled || isReadonly, onMouseDown: (e) => {
10530
+ if (!isEnabled || isReadonly)
10531
+ return;
10532
+ e.preventDefault();
10533
+ openLookup();
10534
+ }, onKeyDown: (e) => {
10535
+ if (!isEnabled || isReadonly)
10536
+ return;
10537
+ if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
10538
+ e.preventDefault();
10539
+ openLookup();
10540
+ }
10541
+ }, onChange: () => undefined, onBlur: onBlur, className: `w-full h-[28px] px-2 text-sm border focus:outline-none table-cell-input ${!isEnabled || isReadonly ? 'cursor-not-allowed' : 'cursor-pointer'}`, style: {
10542
+ borderRadius: '10px',
10543
+ borderColor: hasError
10544
+ ? 'var(--owt-color-error, #B91C1C)'
10545
+ : 'var(--owt-widget-input-border, #C4C4C4)',
10546
+ backgroundColor: !isEnabled || isReadonly
10547
+ ? 'var(--owt-color-bg-alt, #F6F6F6)'
10548
+ : 'var(--owt-color-bg, #FFFFFF)',
10549
+ }, title: hasValue ? displayName : placeholder, children: [jsxRuntimeExports.jsx("option", { value: "", children: placeholder }), hasValue && jsxRuntimeExports.jsx("option", { value: "__selected__", children: displayName })] })) : (jsxRuntimeExports.jsxs("select", { value: hasValue ? '__selected__' : '', disabled: !isEnabled || isReadonly, onMouseDown: (e) => {
10550
+ if (!isEnabled || isReadonly)
10551
+ return;
10552
+ e.preventDefault();
10553
+ openLookup();
10554
+ }, onKeyDown: (e) => {
10555
+ if (!isEnabled || isReadonly)
10556
+ return;
10557
+ if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
10558
+ e.preventDefault();
10559
+ openLookup();
10560
+ }
10561
+ }, onChange: () => undefined, onBlur: onBlur, className: `w-full sm:w-[180px] max-w-full h-[30px] px-3 border shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 ${hasError
10562
+ ? 'border-red-500 focus:ring-red-500 focus:border-red-500'
10563
+ : 'border-gray-300'} ${!isEnabled || isReadonly
10564
+ ? 'bg-gray-100 cursor-not-allowed'
10565
+ : 'bg-white cursor-pointer'}`, style: { borderRadius: '10px' }, title: hasValue ? displayName : placeholder, children: [jsxRuntimeExports.jsx("option", { value: "", children: placeholder }), hasValue && jsxRuntimeExports.jsx("option", { value: "__selected__", children: displayName })] }));
10566
+ if (isReadonly && !isCompact) {
10567
+ return (jsxRuntimeExports.jsxs("div", { className: "mb-[10px] flex flex-col sm:flex-row sm:items-start", children: [rawLabel && (jsxRuntimeExports.jsxs("div", { className: "text-base text-gray-600 font-medium md:min-w-[120px] sm:pr-4 mb-1 sm:mb-0", style: { fontFamily: 'Roboto, sans-serif' }, title: label, children: [label, ":"] })), jsxRuntimeExports.jsx("div", { className: "flex-1 text-base text-gray-900 font-medium", children: hasValue ? displayName : '-' })] }));
10568
+ }
10569
+ if (isReadonly && isCompact) {
10570
+ return (jsxRuntimeExports.jsx("span", { className: "text-sm text-gray-900 truncate block", children: hasValue ? displayName : '-' }));
10571
+ }
10572
+ return (jsxRuntimeExports.jsxs("div", { className: isCompact ? 'table-cell-field w-full' : 'mb-[10px]', children: [isCompact ? (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [selectTrigger, jsxRuntimeExports.jsx("p", { className: "table-cell-field-error", "aria-hidden": "true", children: '\u00a0' })] })) : (jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-start", children: [rawLabel && (jsxRuntimeExports.jsx(WidgetFieldLabel, { className: "text-base font-medium text-gray-700 md:min-w-[120px] sm:pr-4 sm:pt-1 mb-1 sm:mb-0", label: rawLabel, required: isRequired })), jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [selectTrigger, touched && error.length > 0 && (jsxRuntimeExports.jsx("p", { className: "text-red-500 text-sm mt-1", children: error[0] }))] })] })), isOpen && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-[100]", style: { backgroundColor: 'rgba(0,0,0,0.5)' }, onClick: () => {
10573
+ setIsOpen(false);
10574
+ onBlur();
10575
+ } }), jsxRuntimeExports.jsxs("div", { className: "flex flex-col overflow-hidden", style: {
10576
+ position: 'fixed',
10577
+ top: modalPos.y,
10578
+ left: modalPos.x,
10579
+ width: modalSize.w,
10580
+ height: modalSize.h,
10581
+ zIndex: 101,
10582
+ resize: 'both',
10583
+ minWidth: 340,
10584
+ minHeight: 260,
10585
+ maxWidth: '96vw',
10586
+ maxHeight: '92vh',
10587
+ backgroundColor: 'var(--owt-color-bg, #FFFFFF)',
10588
+ borderRadius: 'var(--owt-widget-card-border-radius, 20px)',
10589
+ boxShadow: '0 24px 64px rgba(0,0,0,0.28)',
10590
+ }, onClick: (e) => e.stopPropagation(), children: [jsxRuntimeExports.jsxs("div", { onMouseDown: (e) => {
10591
+ e.preventDefault();
10592
+ isDragging.current = true;
10593
+ dragOrigin.current = {
10594
+ mouseX: e.clientX,
10595
+ mouseY: e.clientY,
10596
+ posX: modalPos.x,
10597
+ posY: modalPos.y,
10598
+ };
10599
+ }, className: "flex items-center justify-between px-5 py-4 flex-shrink-0 select-none border-b border-gray-200 cursor-grab", children: [jsxRuntimeExports.jsx("h3", { className: "text-lg font-semibold text-gray-900", children: t?.('common.selectTitle', { label, defaultValue: `Select ${label}` }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
10600
+ setIsOpen(false);
10601
+ onBlur();
10602
+ }, onMouseDown: (e) => e.stopPropagation(), className: "p-0 border-0 bg-transparent cursor-pointer", "aria-label": t?.('common.close', { defaultValue: 'Close' }), children: jsxRuntimeExports.jsx("img", { src: img$f, alt: "", className: "w-5 h-5 opacity-60" }) })] }), jsxRuntimeExports.jsx("div", { className: "px-5 py-3 flex-shrink-0 border-b border-gray-200", children: jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 px-3 h-[30px] border border-gray-300 rounded-[10px] bg-white", children: [jsxRuntimeExports.jsx("input", { ref: searchInputRef, type: "text", value: searchText, onChange: (e) => setSearchText(e.target.value), onKeyDown: (e) => {
10603
+ if (e.key === 'Enter') {
10604
+ e.preventDefault();
10605
+ runSearch(searchText, 1);
10606
+ }
10607
+ }, placeholder: searchPlaceholder, className: "flex-1 outline-none text-sm text-gray-900 bg-transparent" }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => runSearch(searchText, 1), "aria-label": t?.('common.search', { defaultValue: 'Search' }), className: "flex-shrink-0 p-0 border-0 bg-transparent cursor-pointer", children: jsxRuntimeExports.jsx("img", { src: img$g, alt: "", className: "w-4 h-4 opacity-40" }) })] }) }), jsxRuntimeExports.jsx("div", { className: "overflow-auto flex-1", children: searchResults.length === 0 ? (jsxRuntimeExports.jsx("p", { className: "text-center text-sm text-gray-500 py-10", children: searchText
10608
+ ? t?.('common.noResults', { defaultValue: 'No results found' })
10609
+ : t?.('common.searchHint', {
10610
+ defaultValue: 'Type and press Enter or click search',
10611
+ }) })) : (jsxRuntimeExports.jsx("div", { className: "overflow-auto h-full", children: jsxRuntimeExports.jsxs("table", { className: "w-full text-sm border-collapse", children: [jsxRuntimeExports.jsx("thead", { className: "sticky top-0 z-[1] bg-gray-50", children: jsxRuntimeExports.jsxs("tr", { children: [jsxRuntimeExports.jsx("th", { className: "text-left px-4 py-2 text-sm font-medium text-gray-600 whitespace-nowrap border-b border-gray-200 bg-gray-50", children: tSchema(t, 'record_name') }), jsxRuntimeExports.jsx("th", { className: "text-left px-4 py-2 text-sm font-medium text-gray-600 whitespace-nowrap border-b border-gray-200 bg-gray-50", children: tSchema(t, 'internal_record_id') })] }) }), jsxRuntimeExports.jsx("tbody", { children: searchResults.map((row, idx) => {
10612
+ const rowKey = row.internal_record_id ?? idx;
10613
+ const isSelected = pendingRow?.internal_record_id != null &&
10614
+ rowKey === pendingRow.internal_record_id;
10615
+ return (jsxRuntimeExports.jsxs("tr", { onClick: () => setPendingRow(row), onDoubleClick: () => applySelection(row), className: `cursor-pointer border-b border-gray-100 transition-colors ${isSelected ? 'bg-blue-100' : 'hover:bg-blue-50'}`, children: [jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-sm text-gray-900 whitespace-nowrap", children: parentLabel(row) || '-' }), jsxRuntimeExports.jsx("td", { className: "px-4 py-2 text-sm text-gray-900 whitespace-nowrap", children: row.internal_record_id != null
10616
+ ? String(row.internal_record_id)
10617
+ : '-' })] }, rowKey));
10618
+ }) })] }) })) }), jsxRuntimeExports.jsxs("div", { className: `flex-shrink-0 flex flex-wrap items-center gap-3 px-5 py-3 border-t border-gray-200 ${totalCount !== null ? 'justify-between' : 'justify-end'}`, children: [totalCount !== null && (jsxRuntimeExports.jsxs("span", { className: "text-sm text-gray-600", children: [totalCount === 1
10619
+ ? t?.('common.record', {
10620
+ count: totalCount,
10621
+ defaultValue: `${totalCount} record`,
10622
+ })
10623
+ : t?.('common.records', {
10624
+ count: totalCount,
10625
+ defaultValue: `${totalCount} records`,
10626
+ }), totalCount > 0 &&
10627
+ ` · ${(currentPage - 1) * pageSize + 1}-${Math.min(currentPage * pageSize, totalCount)}`] })), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [totalCount !== null && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("button", { type: "button", onClick: () => currentPage > 1 && runSearch(searchText, currentPage - 1), disabled: currentPage <= 1, className: "px-3 h-8 text-sm font-medium rounded-[10px] bg-gray-100 text-gray-700 disabled:opacity-40 disabled:cursor-not-allowed", children: t?.('common.previous', { defaultValue: 'Prev' }) }), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => currentPage < totalPages && runSearch(searchText, currentPage + 1), disabled: currentPage >= totalPages, className: "px-3 h-8 text-sm font-medium rounded-[10px] bg-gray-100 text-gray-700 disabled:opacity-40 disabled:cursor-not-allowed", children: t?.('common.next', { defaultValue: 'Next' }) })] })), hasValue && (jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
10628
+ clearSelection();
10629
+ setIsOpen(false);
10630
+ onBlur();
10631
+ }, className: "px-4 h-9 text-sm font-medium rounded-[10px] bg-gray-100 text-gray-700 flex-shrink-0", children: t?.('common.remove', { defaultValue: 'Clear' }) })), jsxRuntimeExports.jsx("button", { type: "button", onClick: () => pendingRow && applySelection(pendingRow), disabled: !pendingRow, className: "px-4 h-9 text-sm font-medium rounded-[10px] text-white disabled:opacity-40 disabled:cursor-not-allowed flex-shrink-0", style: { backgroundColor: 'var(--owt-color-info, #2563eb)' }, children: selectRecordLabel })] })] })] })] }))] }));
10632
+ };
10633
+
10177
10634
  const MultiSelectWidget = ({ config }) => {
10178
10635
  const { value, error, touched, isEnabled, isRequired, onChange, onBlur, dataSourceOptions, loading, config: widgetConfig, } = useBaseWidget({ config });
10179
10636
  const { t } = useWidgetContext();
@@ -11378,6 +11835,7 @@ const registerDefaultWidgets = () => {
11378
11835
  widgetRegistry.register({ widget: 'scores-display', component: ScoresDisplayWidget });
11379
11836
  widgetRegistry.register({ widget: 'id-authentication', component: IdAuthenticationWidget });
11380
11837
  widgetRegistry.register({ widget: 'register-lookup', component: RegisterLookupWidget });
11838
+ widgetRegistry.register({ widget: 'parent-lookup', component: ParentLookupWidget });
11381
11839
  widgetRegistry.register({ widget: 'multi-select', component: MultiSelectWidget });
11382
11840
  widgetRegistry.register({ widget: 'geo-hierarchy', component: GeoHierarchyWidget });
11383
11841
  widgetRegistry.register({ widget: 'docs', component: DocsWidget });
@@ -11487,5 +11945,5 @@ const translateUISchema = (schema, translate) => {
11487
11945
  };
11488
11946
  };
11489
11947
 
11490
- export { BooleanWidget, CheckboxWidget, DateInputWidget, DateTimeInputWidget, DialogTableWidget, DisplayWidget, DocsWidget, FileInputWidget, GeoHierarchyWidget, HeaderSectionWidget, IdAuthenticationWidget, JSONEditorPanel, MultiSelectWidget, NumberInputWidget, PanelRenderer, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, RegisterLookupWidget, ScoresDisplayWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, createWidgetStore, createZodSchema, defaultTheme, evaluateCondition, evaluateWidgetConditions, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, getApiDataSource, getCachedApiDataSource, getFormattedNumberLength, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, hasVisibilityRules, isAllowedKey, isGeoHierarchyDataSource, normalizeNumericDefault, normalizeOptionRules, parseDataPath, parseNumber, registerDefaultWidgets, removeMask, resetAll, resetWidget, resolveTheme, resolveWidgetIdValue, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldRequireWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, useBaseWidget, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTheme, validateNumericValue, validateWidget, widgetRegistry };
11948
+ export { BooleanWidget, CheckboxWidget, DateInputWidget, DateTimeInputWidget, DialogTableWidget, DisplayWidget, DocsWidget, FileInputWidget, GeoHierarchyWidget, HeaderSectionWidget, IdAuthenticationWidget, JSONEditorPanel, MultiSelectWidget, NumberInputWidget, PanelRenderer, ParentLookupWidget, PhoneInputWidget, ProfileWidget, PropertyEditor, RadioWidget, RegisterLookupWidget, ScoresDisplayWidget, SectionBuilder, SectionRenderer, SectionTree, SectionsContainer, SelectWidget, TableWidget, TextAreaWidget, TextInputWidget, VisualBuilderPanel, WidgetEventBus, WidgetProvider, WidgetRenderer, applyCaseControl, applyDecimalPrecision, applyMask, createWidgetStore, createZodSchema, defaultTheme, evaluateCondition, evaluateWidgetConditions, filterByCharacterType, formatCurrency, formatDate, formatNumber, formatPhone, formatValue, getApiDataSource, getCachedApiDataSource, getFormattedNumberLength, getSchemaDataSource, getStaticDataSource, getValueByPath, getWidgetValue, hasVisibilityRules, isAllowedKey, isGeoHierarchyDataSource, normalizeNumericDefault, normalizeOptionRules, parseDataPath, parseNumber, registerDefaultWidgets, removeMask, resetAll, resetWidget, resolveTheme, resolveWidgetIdValue, setDataSource, setError, setLoading, setTouched, setValue, setValueByPath, setValues, setWidgetValue, shouldEnableWidget, shouldRequireWidget, shouldShowWidget, transformDataSourceOptions, translatePanelConfig, translateUISchema, translateWidgetConfig, useBaseWidget, useWidgetCascade, useWidgetContext, useWidgetEventBus, useWidgetTheme, validateNumericValue, validateWidget, widgetRegistry };
11491
11949
  //# sourceMappingURL=index.esm.js.map