@prismiq/react 0.1.0 → 0.1.1

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 (57) hide show
  1. package/dist/{CustomSQLEditor-BXB4rf1q.d.cts → CustomSQLEditor-CYlOtecq.d.ts} +10 -3
  2. package/dist/{CustomSQLEditor-DYeId0Gp.d.ts → CustomSQLEditor-d84v_Cgp.d.cts} +10 -3
  3. package/dist/{DashboardDialog-LHmrtNQU.d.cts → DashboardDialog-CZD8I-6z.d.cts} +4 -4
  4. package/dist/{DashboardDialog-B3vYC5Gs.d.ts → DashboardDialog-DBNTVVSp.d.ts} +4 -4
  5. package/dist/{accessibility-2yy5yqRR.d.cts → accessibility-Bu2mNtaB.d.cts} +1 -1
  6. package/dist/{accessibility-2yy5yqRR.d.ts → accessibility-Bu2mNtaB.d.ts} +1 -1
  7. package/dist/charts/index.cjs +27 -27
  8. package/dist/charts/index.d.cts +2 -2
  9. package/dist/charts/index.d.ts +2 -2
  10. package/dist/charts/index.js +2 -2
  11. package/dist/{chunk-MOAEEF5P.js → chunk-3LDRRDJ6.js} +185 -91
  12. package/dist/chunk-3LDRRDJ6.js.map +1 -0
  13. package/dist/{chunk-NK7HKX2J.cjs → chunk-73TPDGXB.cjs} +7 -7
  14. package/dist/{chunk-NK7HKX2J.cjs.map → chunk-73TPDGXB.cjs.map} +1 -1
  15. package/dist/{chunk-UPYINBZU.js → chunk-ET7GCREP.js} +502 -46
  16. package/dist/chunk-ET7GCREP.js.map +1 -0
  17. package/dist/{chunk-2H5WTH4K.js → chunk-FQ23KG6G.js} +3 -3
  18. package/dist/{chunk-2H5WTH4K.js.map → chunk-FQ23KG6G.js.map} +1 -1
  19. package/dist/{chunk-4AVL6GQK.cjs → chunk-KXB2IZI2.cjs} +36 -9
  20. package/dist/chunk-KXB2IZI2.cjs.map +1 -0
  21. package/dist/{chunk-EX74SI67.js → chunk-LBE6GIBC.js} +36 -9
  22. package/dist/chunk-LBE6GIBC.js.map +1 -0
  23. package/dist/{chunk-NY6TZLST.cjs → chunk-URJH4H6G.cjs} +505 -49
  24. package/dist/chunk-URJH4H6G.cjs.map +1 -0
  25. package/dist/{chunk-FEABEF3J.cjs → chunk-VQDFS6VS.cjs} +374 -280
  26. package/dist/chunk-VQDFS6VS.cjs.map +1 -0
  27. package/dist/components/index.cjs +55 -55
  28. package/dist/components/index.d.cts +2 -2
  29. package/dist/components/index.d.ts +2 -2
  30. package/dist/components/index.js +2 -2
  31. package/dist/dashboard/index.cjs +36 -36
  32. package/dist/dashboard/index.d.cts +3 -3
  33. package/dist/dashboard/index.d.ts +3 -3
  34. package/dist/dashboard/index.js +4 -4
  35. package/dist/export/index.d.cts +1 -1
  36. package/dist/export/index.d.ts +1 -1
  37. package/dist/{index-C-Qcuu4Y.d.cts → index-CvKj3SWO.d.cts} +2 -2
  38. package/dist/{index-rPc7ijt8.d.ts → index-DXGLs1yY.d.ts} +2 -2
  39. package/dist/index.cjs +127 -127
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +30 -9
  42. package/dist/index.d.ts +30 -9
  43. package/dist/index.js +6 -6
  44. package/dist/index.js.map +1 -1
  45. package/dist/{types-WrCbOeAV.d.cts → types-j0kPJ9Hz.d.cts} +16 -1
  46. package/dist/{types-WrCbOeAV.d.ts → types-j0kPJ9Hz.d.ts} +16 -1
  47. package/dist/utils/index.cjs +15 -15
  48. package/dist/utils/index.d.cts +5 -21
  49. package/dist/utils/index.d.ts +5 -21
  50. package/dist/utils/index.js +1 -1
  51. package/package.json +2 -2
  52. package/dist/chunk-4AVL6GQK.cjs.map +0 -1
  53. package/dist/chunk-EX74SI67.js.map +0 -1
  54. package/dist/chunk-FEABEF3J.cjs.map +0 -1
  55. package/dist/chunk-MOAEEF5P.js.map +0 -1
  56. package/dist/chunk-NY6TZLST.cjs.map +0 -1
  57. package/dist/chunk-UPYINBZU.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkLMTG3LRC_cjs = require('./chunk-LMTG3LRC.cjs');
4
- var chunk4AVL6GQK_cjs = require('./chunk-4AVL6GQK.cjs');
4
+ var chunkKXB2IZI2_cjs = require('./chunk-KXB2IZI2.cjs');
5
5
  var react = require('react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var reactDom = require('react-dom');
@@ -1179,7 +1179,7 @@ var Dialog = react.forwardRef(function Dialog2({
1179
1179
  style,
1180
1180
  ...props
1181
1181
  }, _ref) {
1182
- const { containerRef } = chunk4AVL6GQK_cjs.useFocusTrap({
1182
+ const { containerRef } = chunkKXB2IZI2_cjs.useFocusTrap({
1183
1183
  active: open,
1184
1184
  onEscape: closeOnEscape ? onClose : void 0
1185
1185
  });
@@ -2327,6 +2327,9 @@ var PrismiqClient = class {
2327
2327
  }
2328
2328
  /**
2329
2329
  * Make an authenticated request to the API.
2330
+ *
2331
+ * @param path - API path (starting with /)
2332
+ * @param options - Fetch options including signal for cancellation
2330
2333
  */
2331
2334
  async request(path, options = {}) {
2332
2335
  const url = `${this.endpoint}${path}`;
@@ -2472,6 +2475,17 @@ var PrismiqClient = class {
2472
2475
  const result = await this.request(path);
2473
2476
  return result.values;
2474
2477
  }
2478
+ /**
2479
+ * Get data source metadata including display names and descriptions.
2480
+ *
2481
+ * Returns metadata for all exposed tables/views that can be used
2482
+ * to show user-friendly names in the UI instead of raw table names.
2483
+ *
2484
+ * @returns Array of data source metadata.
2485
+ */
2486
+ async getDataSources() {
2487
+ return this.request("/data-sources");
2488
+ }
2475
2489
  // ============================================================================
2476
2490
  // Query Methods
2477
2491
  // ============================================================================
@@ -2505,12 +2519,14 @@ var PrismiqClient = class {
2505
2519
  *
2506
2520
  * @param query - The query definition to execute.
2507
2521
  * @param bypassCache - If true, bypass cache and re-execute query.
2522
+ * @param signal - Optional AbortSignal for cancellation.
2508
2523
  * @returns The query result with all rows and cache metadata.
2509
2524
  */
2510
- async executeQuery(query, bypassCache = false) {
2525
+ async executeQuery(query, bypassCache = false, signal) {
2511
2526
  return this.request("/query/execute", {
2512
2527
  method: "POST",
2513
- body: JSON.stringify({ query, bypass_cache: bypassCache })
2528
+ body: JSON.stringify({ query, bypass_cache: bypassCache }),
2529
+ signal
2514
2530
  });
2515
2531
  }
2516
2532
  /**
@@ -2863,6 +2879,7 @@ function AnalyticsProvider({
2863
2879
  }
2864
2880
  const client = clientRef.current;
2865
2881
  const [schema, setSchema] = react.useState(null);
2882
+ const [dataSources, setDataSources] = react.useState([]);
2866
2883
  const [isLoading, setIsLoading] = react.useState(true);
2867
2884
  const [error, setError] = react.useState(null);
2868
2885
  const hasFetchedSchemaRef = react.useRef(false);
@@ -2880,8 +2897,13 @@ function AnalyticsProvider({
2880
2897
  setIsLoading(true);
2881
2898
  setError(null);
2882
2899
  try {
2883
- const fetchedSchema = await client.getSchema();
2900
+ const [fetchedSchema, fetchedDataSources] = await Promise.all([
2901
+ client.getSchema(),
2902
+ client.getDataSources().catch(() => [])
2903
+ // Non-critical, fallback to empty
2904
+ ]);
2884
2905
  setSchema(fetchedSchema);
2906
+ setDataSources(fetchedDataSources);
2885
2907
  onSchemaLoadRef.current?.(fetchedSchema);
2886
2908
  } catch (err) {
2887
2909
  const schemaError = err instanceof Error ? err : new Error(String(err));
@@ -2903,6 +2925,7 @@ function AnalyticsProvider({
2903
2925
  () => ({
2904
2926
  client,
2905
2927
  schema,
2928
+ dataSources,
2906
2929
  isLoading,
2907
2930
  error,
2908
2931
  refetchSchema,
@@ -2910,7 +2933,7 @@ function AnalyticsProvider({
2910
2933
  userId,
2911
2934
  schemaName
2912
2935
  }),
2913
- [client, schema, isLoading, error, refetchSchema, tenantId, userId, schemaName]
2936
+ [client, schema, dataSources, isLoading, error, refetchSchema, tenantId, userId, schemaName]
2914
2937
  );
2915
2938
  const callbacks = react.useMemo(
2916
2939
  () => ({
@@ -2931,22 +2954,44 @@ function useAnalytics() {
2931
2954
  return context;
2932
2955
  }
2933
2956
  function useSchema() {
2934
- const { schema, isLoading, error } = useAnalytics();
2957
+ const { schema, dataSources, isLoading, error } = useAnalytics();
2935
2958
  const tables = react.useMemo(() => schema?.tables ?? [], [schema]);
2936
2959
  const relationships = react.useMemo(() => schema?.relationships ?? [], [schema]);
2960
+ const dataSourceMap = react.useMemo(() => {
2961
+ const map = /* @__PURE__ */ new Map();
2962
+ for (const ds of dataSources) {
2963
+ map.set(ds.table, ds);
2964
+ }
2965
+ return map;
2966
+ }, [dataSources]);
2937
2967
  const getTable = react.useCallback(
2938
2968
  (name) => {
2939
2969
  return tables.find((table) => table.name === name);
2940
2970
  },
2941
2971
  [tables]
2942
2972
  );
2973
+ const getDisplayName = react.useCallback(
2974
+ (tableName) => {
2975
+ return dataSourceMap.get(tableName)?.title ?? tableName;
2976
+ },
2977
+ [dataSourceMap]
2978
+ );
2979
+ const getDescription = react.useCallback(
2980
+ (tableName) => {
2981
+ return dataSourceMap.get(tableName)?.subtitle ?? "";
2982
+ },
2983
+ [dataSourceMap]
2984
+ );
2943
2985
  return {
2944
2986
  schema,
2945
2987
  tables,
2946
2988
  relationships,
2989
+ dataSources,
2947
2990
  isLoading,
2948
2991
  error,
2949
- getTable
2992
+ getTable,
2993
+ getDisplayName,
2994
+ getDescription
2950
2995
  };
2951
2996
  }
2952
2997
  function queryEquals(a, b) {
@@ -4835,6 +4880,29 @@ var containerStyles7 = {
4835
4880
  gap: "var(--prismiq-spacing-xs)",
4836
4881
  flex: 1
4837
4882
  };
4883
+ var comboboxContainerStyles = {
4884
+ position: "relative",
4885
+ flex: 1
4886
+ };
4887
+ var dropdownStyles2 = {
4888
+ position: "fixed",
4889
+ backgroundColor: "var(--prismiq-color-background)",
4890
+ border: "1px solid var(--prismiq-color-border)",
4891
+ borderRadius: "var(--prismiq-radius-md)",
4892
+ boxShadow: "var(--prismiq-shadow-md)",
4893
+ zIndex: 1e3,
4894
+ maxHeight: "200px",
4895
+ overflow: "auto"
4896
+ };
4897
+ var optionStyles2 = {
4898
+ padding: "var(--prismiq-spacing-sm) var(--prismiq-spacing-md)",
4899
+ cursor: "pointer",
4900
+ fontSize: "var(--prismiq-font-size-sm)",
4901
+ transition: "background-color 0.1s"
4902
+ };
4903
+ var optionHoverStyles2 = {
4904
+ backgroundColor: "var(--prismiq-color-surface-hover)"
4905
+ };
4838
4906
  function parseValue(value, dataType) {
4839
4907
  if (!value) return void 0;
4840
4908
  const type = dataType?.toLowerCase() ?? "";
@@ -4865,15 +4933,270 @@ function getInputType(dataType) {
4865
4933
  }
4866
4934
  return "text";
4867
4935
  }
4936
+ function isMultiValueOperator(op) {
4937
+ return op === "in_" || op === "not_in" || op === "in_or_null";
4938
+ }
4939
+ var tagContainerStyles = {
4940
+ display: "flex",
4941
+ flexWrap: "wrap",
4942
+ alignItems: "center",
4943
+ gap: "4px",
4944
+ padding: "4px 8px",
4945
+ border: "1px solid var(--prismiq-color-border)",
4946
+ borderRadius: "var(--prismiq-radius-sm)",
4947
+ backgroundColor: "var(--prismiq-color-background)",
4948
+ minHeight: "32px",
4949
+ cursor: "text",
4950
+ flex: 1
4951
+ };
4952
+ var tagStyles = {
4953
+ display: "inline-flex",
4954
+ alignItems: "center",
4955
+ gap: "4px",
4956
+ padding: "1px 6px",
4957
+ backgroundColor: "var(--prismiq-color-surface)",
4958
+ border: "1px solid var(--prismiq-color-border)",
4959
+ borderRadius: "var(--prismiq-radius-sm)",
4960
+ fontSize: "var(--prismiq-font-size-sm)",
4961
+ lineHeight: "20px",
4962
+ whiteSpace: "nowrap"
4963
+ };
4964
+ var tagRemoveStyles = {
4965
+ display: "inline-flex",
4966
+ alignItems: "center",
4967
+ justifyContent: "center",
4968
+ width: "14px",
4969
+ height: "14px",
4970
+ border: "none",
4971
+ background: "none",
4972
+ cursor: "pointer",
4973
+ padding: 0,
4974
+ fontSize: "12px",
4975
+ lineHeight: 1,
4976
+ color: "var(--prismiq-color-text-muted)",
4977
+ borderRadius: "50%"
4978
+ };
4979
+ var tagInputStyles = {
4980
+ border: "none",
4981
+ outline: "none",
4982
+ background: "none",
4983
+ flex: 1,
4984
+ minWidth: "80px",
4985
+ fontSize: "var(--prismiq-font-size-sm)",
4986
+ padding: "2px 0",
4987
+ color: "var(--prismiq-color-text)"
4988
+ };
4989
+ var multiOptionCheckStyles = {
4990
+ marginRight: "6px",
4991
+ color: "var(--prismiq-color-primary)",
4992
+ fontWeight: 700,
4993
+ fontSize: "12px"
4994
+ };
4868
4995
  function FilterValueInput({
4869
4996
  operator,
4870
4997
  value,
4871
4998
  onChange,
4872
4999
  dataType,
4873
5000
  disabled = false,
4874
- className
5001
+ className,
5002
+ tableName,
5003
+ columnName
4875
5004
  }) {
5005
+ const { client } = useAnalytics();
4876
5006
  const inputType = getInputType(dataType);
5007
+ const isMulti = isMultiValueOperator(operator);
5008
+ const [sampleValues, setSampleValues] = react.useState([]);
5009
+ const [isLoadingValues, setIsLoadingValues] = react.useState(false);
5010
+ const fetchedRef = react.useRef(null);
5011
+ const fetchSeqRef = react.useRef(0);
5012
+ const [isDropdownOpen, setIsDropdownOpen] = react.useState(false);
5013
+ const [highlightedIndex, setHighlightedIndex] = react.useState(-1);
5014
+ const [dropdownPosition, setDropdownPosition] = react.useState({ top: 0, left: 0, width: 0 });
5015
+ const inputRef = react.useRef(null);
5016
+ const dropdownRef = react.useRef(null);
5017
+ const containerRef = react.useRef(null);
5018
+ const [multiInputText, setMultiInputText] = react.useState("");
5019
+ react.useEffect(() => {
5020
+ setMultiInputText("");
5021
+ }, [operator]);
5022
+ react.useEffect(() => {
5023
+ if (!tableName || !columnName || !client) {
5024
+ setSampleValues([]);
5025
+ setIsLoadingValues(false);
5026
+ fetchedRef.current = null;
5027
+ return;
5028
+ }
5029
+ const fetchKey = `${tableName}.${columnName}`;
5030
+ if (fetchedRef.current === fetchKey) return;
5031
+ const fetchSeq = ++fetchSeqRef.current;
5032
+ const fetchSamples = async () => {
5033
+ setIsLoadingValues(true);
5034
+ try {
5035
+ const values = await client.getColumnSample(tableName, columnName, 100);
5036
+ const stringValues = values.filter((v) => v !== null && v !== void 0).map((v) => String(v));
5037
+ if (fetchSeqRef.current !== fetchSeq) return;
5038
+ setSampleValues(stringValues);
5039
+ fetchedRef.current = fetchKey;
5040
+ } catch (err) {
5041
+ if (fetchSeqRef.current !== fetchSeq) return;
5042
+ console.error("Failed to fetch sample values:", err);
5043
+ setSampleValues([]);
5044
+ fetchedRef.current = null;
5045
+ } finally {
5046
+ if (fetchSeqRef.current === fetchSeq) {
5047
+ setIsLoadingValues(false);
5048
+ }
5049
+ }
5050
+ };
5051
+ fetchSamples();
5052
+ }, [client, tableName, columnName]);
5053
+ const selectedValues = isMulti && Array.isArray(value) ? value.filter((v) => v !== null && v !== void 0).map((v) => String(v)) : [];
5054
+ const currentValueStr = isMulti ? multiInputText : formatValue(value);
5055
+ const filteredOptions = isMulti ? sampleValues.filter(
5056
+ (v) => v.toLowerCase().includes(multiInputText.toLowerCase()) && !selectedValues.includes(v)
5057
+ ) : sampleValues.filter(
5058
+ (v) => v.toLowerCase().includes(currentValueStr.toLowerCase())
5059
+ );
5060
+ const updateDropdownPosition = react.useCallback(() => {
5061
+ const el = isMulti ? containerRef.current : inputRef.current;
5062
+ if (el) {
5063
+ const rect = el.getBoundingClientRect();
5064
+ setDropdownPosition({
5065
+ top: rect.bottom + 4,
5066
+ left: rect.left,
5067
+ width: rect.width
5068
+ });
5069
+ }
5070
+ }, [isMulti]);
5071
+ react.useEffect(() => {
5072
+ const handleClickOutside = (event) => {
5073
+ const target = event.target;
5074
+ const isInsideContainer = containerRef.current?.contains(target);
5075
+ const isInsideDropdown = dropdownRef.current?.contains(target);
5076
+ if (!isInsideContainer && !isInsideDropdown) {
5077
+ setIsDropdownOpen(false);
5078
+ }
5079
+ };
5080
+ document.addEventListener("mousedown", handleClickOutside);
5081
+ return () => document.removeEventListener("mousedown", handleClickOutside);
5082
+ }, []);
5083
+ const handleInputFocus = react.useCallback(() => {
5084
+ if (sampleValues.length > 0) {
5085
+ updateDropdownPosition();
5086
+ setIsDropdownOpen(true);
5087
+ setHighlightedIndex(-1);
5088
+ }
5089
+ }, [sampleValues.length, updateDropdownPosition]);
5090
+ const handleOptionSelect = react.useCallback(
5091
+ (optionValue) => {
5092
+ onChange(parseValue(optionValue, dataType));
5093
+ setIsDropdownOpen(false);
5094
+ inputRef.current?.blur();
5095
+ },
5096
+ [onChange, dataType]
5097
+ );
5098
+ const addMultiValue = react.useCallback(
5099
+ (val) => {
5100
+ const trimmed = val.trim();
5101
+ if (!trimmed) return;
5102
+ if (selectedValues.includes(trimmed)) return;
5103
+ const newValues = [...selectedValues, trimmed].map((v) => parseValue(v, dataType));
5104
+ onChange(newValues);
5105
+ setMultiInputText("");
5106
+ },
5107
+ [selectedValues, onChange, dataType]
5108
+ );
5109
+ const removeMultiValue = react.useCallback(
5110
+ (val) => {
5111
+ const newValues = selectedValues.filter((v) => v !== val).map((v) => parseValue(v, dataType));
5112
+ onChange(newValues.length > 0 ? newValues : []);
5113
+ },
5114
+ [selectedValues, onChange, dataType]
5115
+ );
5116
+ const handleMultiOptionSelect = react.useCallback(
5117
+ (optionValue) => {
5118
+ if (selectedValues.includes(optionValue)) {
5119
+ removeMultiValue(optionValue);
5120
+ } else {
5121
+ addMultiValue(optionValue);
5122
+ }
5123
+ setMultiInputText("");
5124
+ updateDropdownPosition();
5125
+ inputRef.current?.focus();
5126
+ },
5127
+ [selectedValues, addMultiValue, removeMultiValue, updateDropdownPosition]
5128
+ );
5129
+ const handleMultiInputKeyDown = react.useCallback(
5130
+ (e) => {
5131
+ if (e.key === "Backspace" && multiInputText === "" && selectedValues.length > 0) {
5132
+ const lastVal = selectedValues[selectedValues.length - 1];
5133
+ if (lastVal !== void 0) removeMultiValue(lastVal);
5134
+ return;
5135
+ }
5136
+ if (isDropdownOpen && filteredOptions.length > 0) {
5137
+ switch (e.key) {
5138
+ case "ArrowDown":
5139
+ e.preventDefault();
5140
+ setHighlightedIndex((prev) => Math.min(prev + 1, filteredOptions.length - 1));
5141
+ return;
5142
+ case "ArrowUp":
5143
+ e.preventDefault();
5144
+ setHighlightedIndex((prev) => Math.max(prev - 1, 0));
5145
+ return;
5146
+ case "Enter":
5147
+ e.preventDefault();
5148
+ if (highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {
5149
+ handleMultiOptionSelect(filteredOptions[highlightedIndex]);
5150
+ } else if (multiInputText.trim()) {
5151
+ addMultiValue(multiInputText);
5152
+ }
5153
+ return;
5154
+ case "Escape":
5155
+ setIsDropdownOpen(false);
5156
+ return;
5157
+ }
5158
+ }
5159
+ if (e.key === "," || e.key === "Enter") {
5160
+ e.preventDefault();
5161
+ addMultiValue(multiInputText);
5162
+ }
5163
+ },
5164
+ [
5165
+ multiInputText,
5166
+ selectedValues,
5167
+ isDropdownOpen,
5168
+ filteredOptions,
5169
+ highlightedIndex,
5170
+ addMultiValue,
5171
+ removeMultiValue,
5172
+ handleMultiOptionSelect
5173
+ ]
5174
+ );
5175
+ const handleSingleKeyDown = react.useCallback(
5176
+ (e) => {
5177
+ if (!isDropdownOpen || filteredOptions.length === 0) return;
5178
+ switch (e.key) {
5179
+ case "ArrowDown":
5180
+ e.preventDefault();
5181
+ setHighlightedIndex((prev) => Math.min(prev + 1, filteredOptions.length - 1));
5182
+ break;
5183
+ case "ArrowUp":
5184
+ e.preventDefault();
5185
+ setHighlightedIndex((prev) => Math.max(prev - 1, 0));
5186
+ break;
5187
+ case "Enter":
5188
+ e.preventDefault();
5189
+ if (highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {
5190
+ handleOptionSelect(filteredOptions[highlightedIndex]);
5191
+ }
5192
+ break;
5193
+ case "Escape":
5194
+ setIsDropdownOpen(false);
5195
+ break;
5196
+ }
5197
+ },
5198
+ [isDropdownOpen, filteredOptions, highlightedIndex, handleOptionSelect]
5199
+ );
4877
5200
  if (operator === "is_null" || operator === "is_not_null") {
4878
5201
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {});
4879
5202
  }
@@ -4913,36 +5236,163 @@ function FilterValueInput({
4913
5236
  )
4914
5237
  ] });
4915
5238
  }
4916
- if (operator === "in_" || operator === "not_in" || operator === "in_or_null") {
4917
- const handleMultiChange = (e) => {
4918
- const values = e.target.value.split(",").map((v) => v.trim()).filter(Boolean).map((v) => parseValue(v, dataType));
4919
- onChange(values);
4920
- };
4921
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: containerStyles7, children: /* @__PURE__ */ jsxRuntime.jsx(
5239
+ if (isMulti) {
5240
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: containerStyles7, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: comboboxContainerStyles, children: [
5241
+ /* @__PURE__ */ jsxRuntime.jsxs(
5242
+ "div",
5243
+ {
5244
+ "data-testid": "filter-tag-container",
5245
+ style: tagContainerStyles,
5246
+ onClick: () => inputRef.current?.focus(),
5247
+ children: [
5248
+ selectedValues.map((val) => /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-testid": `filter-tag-${val}`, style: tagStyles, children: [
5249
+ val,
5250
+ /* @__PURE__ */ jsxRuntime.jsx(
5251
+ "button",
5252
+ {
5253
+ type: "button",
5254
+ "data-testid": `filter-tag-remove-${val}`,
5255
+ style: tagRemoveStyles,
5256
+ onClick: (e) => {
5257
+ e.stopPropagation();
5258
+ removeMultiValue(val);
5259
+ },
5260
+ tabIndex: -1,
5261
+ children: "\xD7"
5262
+ }
5263
+ )
5264
+ ] }, val)),
5265
+ /* @__PURE__ */ jsxRuntime.jsx(
5266
+ "input",
5267
+ {
5268
+ ref: inputRef,
5269
+ "data-testid": "filter-multi-input",
5270
+ type: "text",
5271
+ placeholder: selectedValues.length === 0 ? isLoadingValues ? "Loading..." : "Type or select values" : "",
5272
+ value: multiInputText,
5273
+ disabled: disabled || isLoadingValues,
5274
+ onChange: (e) => {
5275
+ setMultiInputText(e.target.value);
5276
+ if (sampleValues.length > 0) {
5277
+ updateDropdownPosition();
5278
+ setIsDropdownOpen(true);
5279
+ setHighlightedIndex(-1);
5280
+ }
5281
+ },
5282
+ onFocus: handleInputFocus,
5283
+ onKeyDown: handleMultiInputKeyDown,
5284
+ style: tagInputStyles
5285
+ }
5286
+ )
5287
+ ]
5288
+ }
5289
+ ),
5290
+ selectedValues.length === 0 && !multiInputText && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "filter-multi-hint", style: {
5291
+ fontSize: "11px",
5292
+ color: "var(--prismiq-color-text-muted)",
5293
+ marginTop: "2px",
5294
+ paddingLeft: "2px"
5295
+ }, children: [
5296
+ "Press ",
5297
+ /* @__PURE__ */ jsxRuntime.jsx("kbd", { style: { padding: "0 3px", border: "1px solid var(--prismiq-color-border)", borderRadius: "3px", fontSize: "10px" }, children: "," }),
5298
+ " or ",
5299
+ /* @__PURE__ */ jsxRuntime.jsx("kbd", { style: { padding: "0 3px", border: "1px solid var(--prismiq-color-border)", borderRadius: "3px", fontSize: "10px" }, children: "Enter" }),
5300
+ " to add values"
5301
+ ] }),
5302
+ isDropdownOpen && filteredOptions.length > 0 && typeof document !== "undefined" && reactDom.createPortal(
5303
+ /* @__PURE__ */ jsxRuntime.jsx(
5304
+ "div",
5305
+ {
5306
+ ref: dropdownRef,
5307
+ "data-testid": "filter-dropdown",
5308
+ style: {
5309
+ ...dropdownStyles2,
5310
+ top: dropdownPosition.top,
5311
+ left: dropdownPosition.left,
5312
+ width: dropdownPosition.width
5313
+ },
5314
+ children: filteredOptions.map((optionValue, index) => {
5315
+ const isSelected = selectedValues.includes(optionValue);
5316
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5317
+ "div",
5318
+ {
5319
+ "data-testid": `filter-option-${index}`,
5320
+ onClick: () => handleMultiOptionSelect(optionValue),
5321
+ onMouseEnter: () => setHighlightedIndex(index),
5322
+ style: {
5323
+ ...optionStyles2,
5324
+ ...index === highlightedIndex ? optionHoverStyles2 : {},
5325
+ ...isSelected ? { backgroundColor: "var(--prismiq-color-surface)", fontWeight: 500 } : {}
5326
+ },
5327
+ children: [
5328
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: multiOptionCheckStyles, children: isSelected ? "\u2713" : "\u2003" }),
5329
+ optionValue
5330
+ ]
5331
+ },
5332
+ `${optionValue}-${index}`
5333
+ );
5334
+ })
5335
+ }
5336
+ ),
5337
+ document.body
5338
+ )
5339
+ ] }) });
5340
+ }
5341
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: containerStyles7, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: comboboxContainerStyles, children: [
5342
+ /* @__PURE__ */ jsxRuntime.jsx(
4922
5343
  Input,
4923
5344
  {
5345
+ ref: inputRef,
5346
+ "data-testid": "filter-single-input",
4924
5347
  inputSize: "sm",
4925
- type: "text",
4926
- placeholder: "value1, value2, ...",
4927
- value: formatValue(value),
4928
- disabled,
4929
- onChange: handleMultiChange,
4930
- style: { flex: 1 }
5348
+ type: inputType,
5349
+ placeholder: isLoadingValues ? "Loading..." : "Type or select value",
5350
+ value: currentValueStr,
5351
+ disabled: disabled || isLoadingValues,
5352
+ onChange: (e) => {
5353
+ onChange(parseValue(e.target.value, dataType));
5354
+ if (sampleValues.length > 0) {
5355
+ updateDropdownPosition();
5356
+ setIsDropdownOpen(true);
5357
+ }
5358
+ },
5359
+ onFocus: handleInputFocus,
5360
+ onKeyDown: handleSingleKeyDown,
5361
+ style: { width: "100%" }
4931
5362
  }
4932
- ) });
4933
- }
4934
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: containerStyles7, children: /* @__PURE__ */ jsxRuntime.jsx(
4935
- Input,
4936
- {
4937
- inputSize: "sm",
4938
- type: inputType,
4939
- placeholder: "Value",
4940
- value: formatValue(value),
4941
- disabled,
4942
- onChange: (e) => onChange(parseValue(e.target.value, dataType)),
4943
- style: { flex: 1 }
4944
- }
4945
- ) });
5363
+ ),
5364
+ isDropdownOpen && filteredOptions.length > 0 && typeof document !== "undefined" && reactDom.createPortal(
5365
+ /* @__PURE__ */ jsxRuntime.jsx(
5366
+ "div",
5367
+ {
5368
+ ref: dropdownRef,
5369
+ "data-testid": "filter-dropdown",
5370
+ style: {
5371
+ ...dropdownStyles2,
5372
+ top: dropdownPosition.top,
5373
+ left: dropdownPosition.left,
5374
+ width: dropdownPosition.width
5375
+ },
5376
+ children: filteredOptions.map((optionValue, index) => /* @__PURE__ */ jsxRuntime.jsx(
5377
+ "div",
5378
+ {
5379
+ "data-testid": `filter-option-${index}`,
5380
+ onClick: () => handleOptionSelect(optionValue),
5381
+ onMouseEnter: () => setHighlightedIndex(index),
5382
+ style: {
5383
+ ...optionStyles2,
5384
+ ...index === highlightedIndex ? optionHoverStyles2 : {},
5385
+ ...optionValue === currentValueStr ? { backgroundColor: "var(--prismiq-color-surface)", fontWeight: 500 } : {}
5386
+ },
5387
+ children: optionValue
5388
+ },
5389
+ `${optionValue}-${index}`
5390
+ ))
5391
+ }
5392
+ ),
5393
+ document.body
5394
+ )
5395
+ ] }) });
4946
5396
  }
4947
5397
  var rowStyles = {
4948
5398
  display: "flex",
@@ -5035,13 +5485,16 @@ function FilterRow({
5035
5485
  });
5036
5486
  return options;
5037
5487
  }, [tables, schema]);
5488
+ const currentTable = react.useMemo(
5489
+ () => tables.find((t) => t.id === filter.table_id),
5490
+ [tables, filter.table_id]
5491
+ );
5038
5492
  const currentColumnSchema = react.useMemo(() => {
5039
- const table = tables.find((t) => t.id === filter.table_id);
5040
- if (!table) return void 0;
5041
- const tableSchema = schema.tables.find((t) => t.name === table.name);
5493
+ if (!currentTable) return void 0;
5494
+ const tableSchema = schema.tables.find((t) => t.name === currentTable.name);
5042
5495
  if (!tableSchema) return void 0;
5043
5496
  return tableSchema.columns.find((c) => c.name === filter.column);
5044
- }, [tables, schema, filter.table_id, filter.column]);
5497
+ }, [currentTable, schema, filter.column]);
5045
5498
  const operatorOptions = react.useMemo(
5046
5499
  () => getOperatorsForType(currentColumnSchema?.data_type),
5047
5500
  [currentColumnSchema]
@@ -5102,7 +5555,9 @@ function FilterRow({
5102
5555
  operator: filter.operator,
5103
5556
  value: filter.value,
5104
5557
  onChange: handleValueChange,
5105
- dataType: currentColumnSchema?.data_type
5558
+ dataType: currentColumnSchema?.data_type,
5559
+ tableName: currentTable?.name,
5560
+ columnName: filter.column
5106
5561
  }
5107
5562
  ) }),
5108
5563
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -7480,7 +7935,7 @@ function TimeSeriesConfig({
7480
7935
  setError("No date column available. Add a date column to the query first.");
7481
7936
  return;
7482
7937
  }
7483
- const parsed = chunk4AVL6GQK_cjs.parseColumnRef(currentDateColumn, "t1");
7938
+ const parsed = chunkKXB2IZI2_cjs.parseColumnRef(currentDateColumn, "t1");
7484
7939
  if (!parsed) {
7485
7940
  setError("Invalid date column reference. Please select a valid date column.");
7486
7941
  return;
@@ -7494,7 +7949,7 @@ function TimeSeriesConfig({
7494
7949
  };
7495
7950
  const handleDateColumnChange = (value) => {
7496
7951
  if (!config || !value) return;
7497
- const parsed = chunk4AVL6GQK_cjs.parseColumnRef(value, config.table_id);
7952
+ const parsed = chunkKXB2IZI2_cjs.parseColumnRef(value, config.table_id);
7498
7953
  if (!parsed) {
7499
7954
  setError("Invalid column reference. Please select a valid date column.");
7500
7955
  return;
@@ -8543,13 +8998,14 @@ function TableSelector({
8543
8998
  className
8544
8999
  }) {
8545
9000
  const { theme } = chunkLMTG3LRC_cjs.useTheme();
9001
+ const { getDisplayName } = useSchema();
8546
9002
  const availableTableOptions = react.useMemo(() => {
8547
9003
  const selectedNames = new Set(tables.map((t) => t.name));
8548
9004
  return schema.tables.filter((t) => !selectedNames.has(t.name)).map((t) => ({
8549
9005
  value: t.name,
8550
- label: `${t.name} (${t.columns.length} cols)`
9006
+ label: getDisplayName(t.name)
8551
9007
  }));
8552
- }, [schema.tables, tables]);
9008
+ }, [schema.tables, tables, getDisplayName]);
8553
9009
  const suggestedTables = react.useMemo(() => {
8554
9010
  if (!showRelationships || tables.length === 0) return [];
8555
9011
  const selectedNames = new Set(tables.map((t) => t.name));
@@ -8651,7 +9107,7 @@ function TableSelector({
8651
9107
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: containerStyle, children: [
8652
9108
  tables.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: selectedTablesStyle, children: tables.map((table, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: tableChipStyle, children: [
8653
9109
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "table", size: 14 }),
8654
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: table.name }),
9110
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: getDisplayName(table.name) }),
8655
9111
  index === 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", variant: "default", children: "primary" }),
8656
9112
  tables.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(
8657
9113
  "button",
@@ -8659,7 +9115,7 @@ function TableSelector({
8659
9115
  type: "button",
8660
9116
  style: removeButtonStyle,
8661
9117
  onClick: () => handleRemoveTable(table.id),
8662
- "aria-label": `Remove ${table.name}`,
9118
+ "aria-label": `Remove ${getDisplayName(table.name)}`,
8663
9119
  children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "x", size: 12 })
8664
9120
  }
8665
9121
  )
@@ -8693,7 +9149,7 @@ function TableSelector({
8693
9149
  title: suggestion.relationship,
8694
9150
  children: [
8695
9151
  /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "plus", size: 10 }),
8696
- suggestion.table
9152
+ getDisplayName(suggestion.table)
8697
9153
  ]
8698
9154
  },
8699
9155
  suggestion.table
@@ -8777,5 +9233,5 @@ exports.usePinnedDashboards = usePinnedDashboards;
8777
9233
  exports.useQuery = useQuery;
8778
9234
  exports.useSavedQueries = useSavedQueries;
8779
9235
  exports.useSchema = useSchema;
8780
- //# sourceMappingURL=chunk-NY6TZLST.cjs.map
8781
- //# sourceMappingURL=chunk-NY6TZLST.cjs.map
9236
+ //# sourceMappingURL=chunk-URJH4H6G.cjs.map
9237
+ //# sourceMappingURL=chunk-URJH4H6G.cjs.map