@dragonmastery/zinia-forms-core 0.4.8 → 0.5.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.
- package/dist/index.d.ts +218 -21
- package/dist/index.js +2208 -205
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { reactive, watch, provide, ref, computed, unref, inject, getCurrentInstance,
|
|
1
|
+
import { reactive, watch, provide, ref, computed, unref, nextTick, onMounted, inject, getCurrentInstance, Teleport } from 'vue';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { jsxs, jsx, Fragment } from 'vue/jsx-runtime';
|
|
4
4
|
|
|
@@ -6767,6 +6767,166 @@ function createDaisyUIDeleteModal() {
|
|
|
6767
6767
|
return DaisyUIDeleteModal;
|
|
6768
6768
|
}
|
|
6769
6769
|
|
|
6770
|
+
// src/filtering/operators.ts
|
|
6771
|
+
var OPERATORS = {
|
|
6772
|
+
// Comparison operators
|
|
6773
|
+
EQUALS: "eq",
|
|
6774
|
+
NOT_EQUALS: "ne",
|
|
6775
|
+
// String operators
|
|
6776
|
+
CONTAINS: "contains",
|
|
6777
|
+
// case-insensitive contains (default for strings)
|
|
6778
|
+
STARTS_WITH: "sw",
|
|
6779
|
+
ENDS_WITH: "ew",
|
|
6780
|
+
// Number/Date comparison operators
|
|
6781
|
+
GREATER_THAN: "gt",
|
|
6782
|
+
GREATER_THAN_OR_EQUAL: "gte",
|
|
6783
|
+
LESS_THAN: "lt",
|
|
6784
|
+
LESS_THAN_OR_EQUAL: "lte",
|
|
6785
|
+
BETWEEN: "between",
|
|
6786
|
+
// Array operators
|
|
6787
|
+
IS_ONE_OF: "in",
|
|
6788
|
+
IS_NOT_ONE_OF: "notIn",
|
|
6789
|
+
// Null check operators
|
|
6790
|
+
IS_EMPTY: "isEmpty",
|
|
6791
|
+
IS_NOT_EMPTY: "isNotEmpty"
|
|
6792
|
+
};
|
|
6793
|
+
var DATA_TYPES = {
|
|
6794
|
+
STRING: "string",
|
|
6795
|
+
NUMBER: "number",
|
|
6796
|
+
DATE: "date",
|
|
6797
|
+
BOOLEAN: "boolean",
|
|
6798
|
+
MONEY: "money",
|
|
6799
|
+
PERCENTAGE: "percentage"
|
|
6800
|
+
};
|
|
6801
|
+
var NUMERIC_TYPES = [
|
|
6802
|
+
DATA_TYPES.NUMBER,
|
|
6803
|
+
DATA_TYPES.MONEY,
|
|
6804
|
+
DATA_TYPES.PERCENTAGE
|
|
6805
|
+
];
|
|
6806
|
+
var OPERATORS_BY_TYPE = {
|
|
6807
|
+
string: [
|
|
6808
|
+
OPERATORS.EQUALS,
|
|
6809
|
+
OPERATORS.NOT_EQUALS,
|
|
6810
|
+
OPERATORS.CONTAINS,
|
|
6811
|
+
OPERATORS.STARTS_WITH,
|
|
6812
|
+
OPERATORS.ENDS_WITH,
|
|
6813
|
+
OPERATORS.IS_ONE_OF,
|
|
6814
|
+
OPERATORS.IS_NOT_ONE_OF
|
|
6815
|
+
],
|
|
6816
|
+
number: [
|
|
6817
|
+
OPERATORS.EQUALS,
|
|
6818
|
+
OPERATORS.NOT_EQUALS,
|
|
6819
|
+
OPERATORS.GREATER_THAN,
|
|
6820
|
+
OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
6821
|
+
OPERATORS.LESS_THAN,
|
|
6822
|
+
OPERATORS.LESS_THAN_OR_EQUAL,
|
|
6823
|
+
OPERATORS.BETWEEN
|
|
6824
|
+
],
|
|
6825
|
+
date: [
|
|
6826
|
+
OPERATORS.EQUALS,
|
|
6827
|
+
OPERATORS.NOT_EQUALS,
|
|
6828
|
+
OPERATORS.GREATER_THAN,
|
|
6829
|
+
OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
6830
|
+
OPERATORS.LESS_THAN,
|
|
6831
|
+
OPERATORS.LESS_THAN_OR_EQUAL,
|
|
6832
|
+
OPERATORS.BETWEEN,
|
|
6833
|
+
OPERATORS.IS_EMPTY,
|
|
6834
|
+
OPERATORS.IS_NOT_EMPTY
|
|
6835
|
+
],
|
|
6836
|
+
boolean: [OPERATORS.EQUALS, OPERATORS.NOT_EQUALS],
|
|
6837
|
+
enum: [
|
|
6838
|
+
OPERATORS.EQUALS,
|
|
6839
|
+
OPERATORS.NOT_EQUALS,
|
|
6840
|
+
OPERATORS.IS_ONE_OF,
|
|
6841
|
+
OPERATORS.IS_NOT_ONE_OF
|
|
6842
|
+
],
|
|
6843
|
+
money: [
|
|
6844
|
+
OPERATORS.EQUALS,
|
|
6845
|
+
OPERATORS.NOT_EQUALS,
|
|
6846
|
+
OPERATORS.GREATER_THAN,
|
|
6847
|
+
OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
6848
|
+
OPERATORS.LESS_THAN,
|
|
6849
|
+
OPERATORS.LESS_THAN_OR_EQUAL,
|
|
6850
|
+
OPERATORS.BETWEEN
|
|
6851
|
+
],
|
|
6852
|
+
percentage: [
|
|
6853
|
+
OPERATORS.EQUALS,
|
|
6854
|
+
OPERATORS.NOT_EQUALS,
|
|
6855
|
+
OPERATORS.GREATER_THAN,
|
|
6856
|
+
OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
6857
|
+
OPERATORS.LESS_THAN,
|
|
6858
|
+
OPERATORS.LESS_THAN_OR_EQUAL,
|
|
6859
|
+
OPERATORS.BETWEEN
|
|
6860
|
+
]
|
|
6861
|
+
};
|
|
6862
|
+
var SINGLE_VALUE_OPERATORS = [
|
|
6863
|
+
OPERATORS.EQUALS,
|
|
6864
|
+
OPERATORS.NOT_EQUALS,
|
|
6865
|
+
OPERATORS.CONTAINS,
|
|
6866
|
+
OPERATORS.STARTS_WITH,
|
|
6867
|
+
OPERATORS.ENDS_WITH,
|
|
6868
|
+
OPERATORS.GREATER_THAN,
|
|
6869
|
+
OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
6870
|
+
OPERATORS.LESS_THAN,
|
|
6871
|
+
OPERATORS.LESS_THAN_OR_EQUAL
|
|
6872
|
+
];
|
|
6873
|
+
var ARRAY_VALUE_OPERATORS = [
|
|
6874
|
+
OPERATORS.IS_ONE_OF,
|
|
6875
|
+
OPERATORS.IS_NOT_ONE_OF,
|
|
6876
|
+
OPERATORS.BETWEEN
|
|
6877
|
+
];
|
|
6878
|
+
var NULL_CHECK_OPERATORS = [OPERATORS.IS_EMPTY, OPERATORS.IS_NOT_EMPTY];
|
|
6879
|
+
function isValidOperatorForType(operator, type) {
|
|
6880
|
+
const validOperators = OPERATORS_BY_TYPE[type];
|
|
6881
|
+
return validOperators.includes(operator);
|
|
6882
|
+
}
|
|
6883
|
+
function requiresSingleValue(operator) {
|
|
6884
|
+
return SINGLE_VALUE_OPERATORS.includes(operator);
|
|
6885
|
+
}
|
|
6886
|
+
function requiresArrayValues(operator) {
|
|
6887
|
+
return ARRAY_VALUE_OPERATORS.includes(operator);
|
|
6888
|
+
}
|
|
6889
|
+
function isNullCheckOperator(operator) {
|
|
6890
|
+
return NULL_CHECK_OPERATORS.includes(operator);
|
|
6891
|
+
}
|
|
6892
|
+
|
|
6893
|
+
// src/filtering/operator-map.ts
|
|
6894
|
+
var SHORTHAND_TO_OPERATOR = {
|
|
6895
|
+
eq: OPERATORS.EQUALS,
|
|
6896
|
+
ne: OPERATORS.NOT_EQUALS,
|
|
6897
|
+
contains: OPERATORS.CONTAINS,
|
|
6898
|
+
sw: OPERATORS.STARTS_WITH,
|
|
6899
|
+
ew: OPERATORS.ENDS_WITH,
|
|
6900
|
+
gt: OPERATORS.GREATER_THAN,
|
|
6901
|
+
gte: OPERATORS.GREATER_THAN_OR_EQUAL,
|
|
6902
|
+
lt: OPERATORS.LESS_THAN,
|
|
6903
|
+
lte: OPERATORS.LESS_THAN_OR_EQUAL,
|
|
6904
|
+
between: OPERATORS.BETWEEN,
|
|
6905
|
+
in: OPERATORS.IS_ONE_OF,
|
|
6906
|
+
notIn: OPERATORS.IS_NOT_ONE_OF,
|
|
6907
|
+
isEmpty: OPERATORS.IS_EMPTY,
|
|
6908
|
+
isNotEmpty: OPERATORS.IS_NOT_EMPTY,
|
|
6909
|
+
// Legacy aliases for backward compatibility
|
|
6910
|
+
isNull: OPERATORS.IS_EMPTY,
|
|
6911
|
+
isNotNull: OPERATORS.IS_NOT_EMPTY
|
|
6912
|
+
};
|
|
6913
|
+
var DEFAULT_OPERATORS = {
|
|
6914
|
+
string: OPERATORS.CONTAINS,
|
|
6915
|
+
// 'contains' - always case-insensitive
|
|
6916
|
+
number: OPERATORS.EQUALS,
|
|
6917
|
+
// 'eq'
|
|
6918
|
+
date: OPERATORS.EQUALS,
|
|
6919
|
+
// 'eq'
|
|
6920
|
+
boolean: OPERATORS.EQUALS,
|
|
6921
|
+
// 'eq'
|
|
6922
|
+
enum: OPERATORS.IS_ONE_OF,
|
|
6923
|
+
// 'in' - Multi-select is the primary control for enums
|
|
6924
|
+
money: OPERATORS.EQUALS,
|
|
6925
|
+
// 'eq'
|
|
6926
|
+
percentage: OPERATORS.EQUALS
|
|
6927
|
+
// 'eq'
|
|
6928
|
+
};
|
|
6929
|
+
|
|
6770
6930
|
// src/components/createDataTableComponents.ts
|
|
6771
6931
|
function createDataTableComponents(schema, renderStyle) {
|
|
6772
6932
|
const styleToUse = renderStyle || getDefaultStyle();
|
|
@@ -6779,6 +6939,570 @@ function createDataTableComponents(schema, renderStyle) {
|
|
|
6779
6939
|
ZiniaDataTable
|
|
6780
6940
|
};
|
|
6781
6941
|
}
|
|
6942
|
+
|
|
6943
|
+
// src/filtering/validation.ts
|
|
6944
|
+
function validateFieldRegistry(registry) {
|
|
6945
|
+
const errors = [];
|
|
6946
|
+
if (!registry || typeof registry !== "object") {
|
|
6947
|
+
return {
|
|
6948
|
+
valid: false,
|
|
6949
|
+
errors: [{ message: "Field registry must be an object", code: "INVALID_REGISTRY" }]
|
|
6950
|
+
};
|
|
6951
|
+
}
|
|
6952
|
+
const fieldNames = Object.keys(registry);
|
|
6953
|
+
if (fieldNames.length === 0) {
|
|
6954
|
+
return {
|
|
6955
|
+
valid: false,
|
|
6956
|
+
errors: [{ message: "Field registry must contain at least one field", code: "EMPTY_REGISTRY" }]
|
|
6957
|
+
};
|
|
6958
|
+
}
|
|
6959
|
+
const validTypes = ["string", "number", "date", "boolean", "enum", "money", "percentage"];
|
|
6960
|
+
const seenFields = /* @__PURE__ */ new Set();
|
|
6961
|
+
for (const [fieldName, metadata] of Object.entries(registry)) {
|
|
6962
|
+
if (!fieldName || typeof fieldName !== "string") {
|
|
6963
|
+
errors.push({
|
|
6964
|
+
field: fieldName,
|
|
6965
|
+
message: "Field name must be a non-empty string",
|
|
6966
|
+
code: "INVALID_FIELD_NAME"
|
|
6967
|
+
});
|
|
6968
|
+
continue;
|
|
6969
|
+
}
|
|
6970
|
+
if (seenFields.has(fieldName)) {
|
|
6971
|
+
errors.push({
|
|
6972
|
+
field: fieldName,
|
|
6973
|
+
message: `Duplicate field name: ${fieldName}`,
|
|
6974
|
+
code: "DUPLICATE_FIELD"
|
|
6975
|
+
});
|
|
6976
|
+
continue;
|
|
6977
|
+
}
|
|
6978
|
+
seenFields.add(fieldName);
|
|
6979
|
+
if (!metadata || typeof metadata !== "object") {
|
|
6980
|
+
errors.push({
|
|
6981
|
+
field: fieldName,
|
|
6982
|
+
message: "Field metadata must be an object",
|
|
6983
|
+
code: "INVALID_METADATA"
|
|
6984
|
+
});
|
|
6985
|
+
continue;
|
|
6986
|
+
}
|
|
6987
|
+
if (!metadata.type || !validTypes.includes(metadata.type)) {
|
|
6988
|
+
errors.push({
|
|
6989
|
+
field: fieldName,
|
|
6990
|
+
message: `Invalid type: ${metadata.type}. Must be one of: ${validTypes.join(", ")}`,
|
|
6991
|
+
code: "INVALID_TYPE"
|
|
6992
|
+
});
|
|
6993
|
+
}
|
|
6994
|
+
if (metadata.filterable !== void 0 && typeof metadata.filterable !== "boolean") {
|
|
6995
|
+
errors.push({
|
|
6996
|
+
field: fieldName,
|
|
6997
|
+
message: "filterable must be a boolean",
|
|
6998
|
+
code: "INVALID_FILTERABLE"
|
|
6999
|
+
});
|
|
7000
|
+
}
|
|
7001
|
+
if (metadata.searchable !== void 0 && typeof metadata.searchable !== "boolean") {
|
|
7002
|
+
errors.push({
|
|
7003
|
+
field: fieldName,
|
|
7004
|
+
message: "searchable must be a boolean",
|
|
7005
|
+
code: "INVALID_SEARCHABLE"
|
|
7006
|
+
});
|
|
7007
|
+
}
|
|
7008
|
+
if (metadata.sortable !== void 0 && typeof metadata.sortable !== "boolean") {
|
|
7009
|
+
errors.push({
|
|
7010
|
+
field: fieldName,
|
|
7011
|
+
message: "sortable must be a boolean",
|
|
7012
|
+
code: "INVALID_SORTABLE"
|
|
7013
|
+
});
|
|
7014
|
+
}
|
|
7015
|
+
}
|
|
7016
|
+
return {
|
|
7017
|
+
valid: errors.length === 0,
|
|
7018
|
+
errors
|
|
7019
|
+
};
|
|
7020
|
+
}
|
|
7021
|
+
function validateFilterValueObject(fieldName, filter, registry) {
|
|
7022
|
+
const errors = [];
|
|
7023
|
+
if (!(fieldName in registry)) {
|
|
7024
|
+
return {
|
|
7025
|
+
valid: false,
|
|
7026
|
+
errors: [
|
|
7027
|
+
{
|
|
7028
|
+
field: fieldName,
|
|
7029
|
+
message: `Field "${fieldName}" does not exist in registry`,
|
|
7030
|
+
code: "FIELD_NOT_IN_REGISTRY"
|
|
7031
|
+
}
|
|
7032
|
+
]
|
|
7033
|
+
};
|
|
7034
|
+
}
|
|
7035
|
+
const fieldMetadata = registry[fieldName];
|
|
7036
|
+
const isFilterable = fieldMetadata.filterable !== false;
|
|
7037
|
+
if (!isFilterable) {
|
|
7038
|
+
return {
|
|
7039
|
+
valid: false,
|
|
7040
|
+
errors: [
|
|
7041
|
+
{
|
|
7042
|
+
field: fieldName,
|
|
7043
|
+
message: `Field "${fieldName}" is not filterable`,
|
|
7044
|
+
code: "FIELD_NOT_FILTERABLE"
|
|
7045
|
+
}
|
|
7046
|
+
]
|
|
7047
|
+
};
|
|
7048
|
+
}
|
|
7049
|
+
const fieldType = fieldMetadata.type;
|
|
7050
|
+
if (!filter.operator || typeof filter.operator !== "string") {
|
|
7051
|
+
errors.push({
|
|
7052
|
+
field: fieldName,
|
|
7053
|
+
message: "Operator is required and must be a string",
|
|
7054
|
+
code: "MISSING_OPERATOR"
|
|
7055
|
+
});
|
|
7056
|
+
} else if (!isValidOperatorForType(filter.operator, fieldType)) {
|
|
7057
|
+
errors.push({
|
|
7058
|
+
field: fieldName,
|
|
7059
|
+
message: `Operator "${filter.operator}" is not valid for type "${fieldType}"`,
|
|
7060
|
+
code: "INVALID_OPERATOR_FOR_TYPE"
|
|
7061
|
+
});
|
|
7062
|
+
}
|
|
7063
|
+
if (filter.operator) {
|
|
7064
|
+
if (isNullCheckOperator(filter.operator)) {
|
|
7065
|
+
if (filter.value !== void 0) {
|
|
7066
|
+
errors.push({
|
|
7067
|
+
field: fieldName,
|
|
7068
|
+
message: `Operator "${filter.operator}" must not have a value`,
|
|
7069
|
+
code: "NULL_CHECK_WITH_VALUE"
|
|
7070
|
+
});
|
|
7071
|
+
}
|
|
7072
|
+
if (filter.values !== void 0) {
|
|
7073
|
+
errors.push({
|
|
7074
|
+
field: fieldName,
|
|
7075
|
+
message: `Operator "${filter.operator}" must not have values`,
|
|
7076
|
+
code: "NULL_CHECK_WITH_VALUES"
|
|
7077
|
+
});
|
|
7078
|
+
}
|
|
7079
|
+
} else if (requiresSingleValue(filter.operator)) {
|
|
7080
|
+
if (filter.value === void 0) {
|
|
7081
|
+
errors.push({
|
|
7082
|
+
field: fieldName,
|
|
7083
|
+
message: `Operator "${filter.operator}" requires a value`,
|
|
7084
|
+
code: "MISSING_VALUE"
|
|
7085
|
+
});
|
|
7086
|
+
}
|
|
7087
|
+
if (filter.values !== void 0) {
|
|
7088
|
+
errors.push({
|
|
7089
|
+
field: fieldName,
|
|
7090
|
+
message: `Operator "${filter.operator}" must use "value", not "values"`,
|
|
7091
|
+
code: "SINGLE_VALUE_WITH_ARRAY"
|
|
7092
|
+
});
|
|
7093
|
+
}
|
|
7094
|
+
} else if (requiresArrayValues(filter.operator)) {
|
|
7095
|
+
if (!filter.values || !Array.isArray(filter.values)) {
|
|
7096
|
+
errors.push({
|
|
7097
|
+
field: fieldName,
|
|
7098
|
+
message: `Operator "${filter.operator}" requires a values array`,
|
|
7099
|
+
code: "MISSING_VALUES"
|
|
7100
|
+
});
|
|
7101
|
+
} else if (filter.values.length === 0) {
|
|
7102
|
+
errors.push({
|
|
7103
|
+
field: fieldName,
|
|
7104
|
+
message: `Operator "${filter.operator}" requires a non-empty values array`,
|
|
7105
|
+
code: "EMPTY_VALUES_ARRAY"
|
|
7106
|
+
});
|
|
7107
|
+
} else if (filter.operator === OPERATORS.BETWEEN && filter.values.length !== 2) {
|
|
7108
|
+
errors.push({
|
|
7109
|
+
field: fieldName,
|
|
7110
|
+
message: `Operator "${OPERATORS.BETWEEN}" requires exactly 2 values, got ${filter.values.length}`,
|
|
7111
|
+
code: "INVALID_BETWEEN_COUNT"
|
|
7112
|
+
});
|
|
7113
|
+
} else if (filter.operator === OPERATORS.BETWEEN) {
|
|
7114
|
+
const [min, max] = filter.values;
|
|
7115
|
+
if (NUMERIC_TYPES.includes(fieldType)) {
|
|
7116
|
+
if (typeof min !== "number" || typeof max !== "number") {
|
|
7117
|
+
errors.push({
|
|
7118
|
+
field: fieldName,
|
|
7119
|
+
message: "between operator values must be numbers",
|
|
7120
|
+
code: "INVALID_BETWEEN_TYPE"
|
|
7121
|
+
});
|
|
7122
|
+
} else if (min > max) {
|
|
7123
|
+
errors.push({
|
|
7124
|
+
field: fieldName,
|
|
7125
|
+
message: `between operator: min (${min}) must be <= max (${max})`,
|
|
7126
|
+
code: "INVALID_BETWEEN_RANGE"
|
|
7127
|
+
});
|
|
7128
|
+
}
|
|
7129
|
+
} else if (fieldType === DATA_TYPES.DATE) {
|
|
7130
|
+
if (typeof min !== "string" || typeof max !== "string") {
|
|
7131
|
+
errors.push({
|
|
7132
|
+
field: fieldName,
|
|
7133
|
+
message: "between operator values must be ISO 8601 date strings",
|
|
7134
|
+
code: "INVALID_BETWEEN_TYPE"
|
|
7135
|
+
});
|
|
7136
|
+
} else {
|
|
7137
|
+
try {
|
|
7138
|
+
const minDate = new Date(min);
|
|
7139
|
+
const maxDate = new Date(max);
|
|
7140
|
+
if (isNaN(minDate.getTime()) || isNaN(maxDate.getTime())) {
|
|
7141
|
+
errors.push({
|
|
7142
|
+
field: fieldName,
|
|
7143
|
+
message: "between operator values must be valid ISO 8601 dates",
|
|
7144
|
+
code: "INVALID_DATE_FORMAT"
|
|
7145
|
+
});
|
|
7146
|
+
} else if (minDate > maxDate) {
|
|
7147
|
+
errors.push({
|
|
7148
|
+
field: fieldName,
|
|
7149
|
+
message: `between operator: start date must be <= end date`,
|
|
7150
|
+
code: "INVALID_BETWEEN_RANGE"
|
|
7151
|
+
});
|
|
7152
|
+
}
|
|
7153
|
+
} catch (e) {
|
|
7154
|
+
errors.push({
|
|
7155
|
+
field: fieldName,
|
|
7156
|
+
message: "between operator values must be valid ISO 8601 dates",
|
|
7157
|
+
code: "INVALID_DATE_FORMAT"
|
|
7158
|
+
});
|
|
7159
|
+
}
|
|
7160
|
+
}
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
if (filter.value !== void 0) {
|
|
7164
|
+
errors.push({
|
|
7165
|
+
field: fieldName,
|
|
7166
|
+
message: `Operator "${filter.operator}" must use "values", not "value"`,
|
|
7167
|
+
code: "ARRAY_VALUE_WITH_SINGLE"
|
|
7168
|
+
});
|
|
7169
|
+
}
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
7172
|
+
if (filter.caseSensitive !== void 0 && fieldType !== "string" && fieldType !== "enum") {
|
|
7173
|
+
errors.push({
|
|
7174
|
+
field: fieldName,
|
|
7175
|
+
message: "caseSensitive is only applicable to string and enum types",
|
|
7176
|
+
code: "INVALID_CASE_SENSITIVE"
|
|
7177
|
+
});
|
|
7178
|
+
}
|
|
7179
|
+
return {
|
|
7180
|
+
valid: errors.length === 0,
|
|
7181
|
+
errors
|
|
7182
|
+
};
|
|
7183
|
+
}
|
|
7184
|
+
function validateFilterConfiguration(config, registry) {
|
|
7185
|
+
const errors = [];
|
|
7186
|
+
const registryValidation = validateFieldRegistry(registry);
|
|
7187
|
+
if (!registryValidation.valid) {
|
|
7188
|
+
return registryValidation;
|
|
7189
|
+
}
|
|
7190
|
+
if (!config || typeof config !== "object") {
|
|
7191
|
+
return {
|
|
7192
|
+
valid: false,
|
|
7193
|
+
errors: [{ message: "Filter configuration must be an object", code: "INVALID_CONFIG" }]
|
|
7194
|
+
};
|
|
7195
|
+
}
|
|
7196
|
+
for (const [fieldName, filter] of Object.entries(config)) {
|
|
7197
|
+
const filterValidation = validateFilterValueObject(fieldName, filter, registry);
|
|
7198
|
+
if (!filterValidation.valid) {
|
|
7199
|
+
errors.push(...filterValidation.errors);
|
|
7200
|
+
}
|
|
7201
|
+
}
|
|
7202
|
+
return {
|
|
7203
|
+
valid: errors.length === 0,
|
|
7204
|
+
errors
|
|
7205
|
+
};
|
|
7206
|
+
}
|
|
7207
|
+
function mapTypeToFilterDataType(type, inputType) {
|
|
7208
|
+
switch (type) {
|
|
7209
|
+
case "string":
|
|
7210
|
+
if (inputType === "select" || inputType === "combobox" || inputType === "radio") {
|
|
7211
|
+
return "enum";
|
|
7212
|
+
}
|
|
7213
|
+
return "string";
|
|
7214
|
+
case "number":
|
|
7215
|
+
if (inputType === "currency") {
|
|
7216
|
+
return "money";
|
|
7217
|
+
}
|
|
7218
|
+
return "number";
|
|
7219
|
+
case "boolean":
|
|
7220
|
+
return "boolean";
|
|
7221
|
+
case "date":
|
|
7222
|
+
return "date";
|
|
7223
|
+
case "enum":
|
|
7224
|
+
return "enum";
|
|
7225
|
+
default:
|
|
7226
|
+
return "string";
|
|
7227
|
+
}
|
|
7228
|
+
}
|
|
7229
|
+
function buildFieldRegistryFromSchema(schema, fieldsMetadata) {
|
|
7230
|
+
const registry = {};
|
|
7231
|
+
const shape = schema.shape;
|
|
7232
|
+
const fieldNames = Object.keys(shape);
|
|
7233
|
+
for (const fieldName of fieldNames) {
|
|
7234
|
+
const fieldMetadata = fieldsMetadata[fieldName];
|
|
7235
|
+
if (!fieldMetadata) {
|
|
7236
|
+
const fieldSchema = shape[fieldName];
|
|
7237
|
+
const inferredType = inferTypeFromZodSchema(fieldSchema);
|
|
7238
|
+
registry[fieldName] = {
|
|
7239
|
+
type: inferredType,
|
|
7240
|
+
filterable: true,
|
|
7241
|
+
searchable: false,
|
|
7242
|
+
sortable: true
|
|
7243
|
+
};
|
|
7244
|
+
continue;
|
|
7245
|
+
}
|
|
7246
|
+
const filterType = mapTypeToFilterDataType(fieldMetadata.type, fieldMetadata.inputType);
|
|
7247
|
+
const filterable = "filterable" in fieldMetadata ? fieldMetadata.filterable !== false : true;
|
|
7248
|
+
const searchable = "searchable" in fieldMetadata ? fieldMetadata.searchable === true : false;
|
|
7249
|
+
const sortable = "sortable" in fieldMetadata ? fieldMetadata.sortable !== false : true;
|
|
7250
|
+
registry[fieldName] = {
|
|
7251
|
+
type: filterType,
|
|
7252
|
+
filterable,
|
|
7253
|
+
searchable,
|
|
7254
|
+
sortable
|
|
7255
|
+
};
|
|
7256
|
+
}
|
|
7257
|
+
return registry;
|
|
7258
|
+
}
|
|
7259
|
+
function inferTypeFromZodSchema(schema) {
|
|
7260
|
+
let unwrapped = schema;
|
|
7261
|
+
while (unwrapped instanceof z.ZodOptional || unwrapped instanceof z.ZodNullable || unwrapped instanceof z.ZodDefault) {
|
|
7262
|
+
if (unwrapped instanceof z.ZodOptional) {
|
|
7263
|
+
unwrapped = unwrapped._def.innerType;
|
|
7264
|
+
} else if (unwrapped instanceof z.ZodNullable) {
|
|
7265
|
+
unwrapped = unwrapped._def.innerType;
|
|
7266
|
+
} else if (unwrapped instanceof z.ZodDefault) {
|
|
7267
|
+
unwrapped = unwrapped._def.innerType;
|
|
7268
|
+
}
|
|
7269
|
+
}
|
|
7270
|
+
if (unwrapped instanceof z.ZodString) {
|
|
7271
|
+
return "string";
|
|
7272
|
+
} else if (unwrapped instanceof z.ZodNumber) {
|
|
7273
|
+
return "number";
|
|
7274
|
+
} else if (unwrapped instanceof z.ZodBoolean) {
|
|
7275
|
+
return "boolean";
|
|
7276
|
+
} else if (unwrapped instanceof z.ZodDate) {
|
|
7277
|
+
return "date";
|
|
7278
|
+
} else if (unwrapped instanceof z.ZodEnum) {
|
|
7279
|
+
return "enum";
|
|
7280
|
+
} else {
|
|
7281
|
+
return "string";
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
|
|
7285
|
+
// src/filtering/serialization.ts
|
|
7286
|
+
function serializeFiltersToQueryString(config) {
|
|
7287
|
+
if (Object.keys(config).length === 0) {
|
|
7288
|
+
return "";
|
|
7289
|
+
}
|
|
7290
|
+
try {
|
|
7291
|
+
const params = new URLSearchParams();
|
|
7292
|
+
for (const [fieldName, filter] of Object.entries(config)) {
|
|
7293
|
+
const prefix = `filters[${fieldName}]`;
|
|
7294
|
+
params.append(`${prefix}[operator]`, filter.operator);
|
|
7295
|
+
if (filter.value !== void 0) {
|
|
7296
|
+
params.append(`${prefix}[value]`, String(filter.value));
|
|
7297
|
+
}
|
|
7298
|
+
if (filter.values !== void 0 && Array.isArray(filter.values)) {
|
|
7299
|
+
params.append(`${prefix}[values]`, JSON.stringify(filter.values));
|
|
7300
|
+
}
|
|
7301
|
+
if (filter.caseSensitive !== void 0) {
|
|
7302
|
+
params.append(`${prefix}[caseSensitive]`, String(filter.caseSensitive));
|
|
7303
|
+
}
|
|
7304
|
+
}
|
|
7305
|
+
return params.toString();
|
|
7306
|
+
} catch (error) {
|
|
7307
|
+
console.error("Failed to serialize filters to query string:", error);
|
|
7308
|
+
return "";
|
|
7309
|
+
}
|
|
7310
|
+
}
|
|
7311
|
+
function deserializeFiltersFromQueryString(queryString, registry) {
|
|
7312
|
+
if (!queryString || queryString.trim() === "") {
|
|
7313
|
+
return {};
|
|
7314
|
+
}
|
|
7315
|
+
try {
|
|
7316
|
+
try {
|
|
7317
|
+
const params = new URLSearchParams(queryString);
|
|
7318
|
+
const config = {};
|
|
7319
|
+
const fieldMap = /* @__PURE__ */ new Map();
|
|
7320
|
+
for (const [key, value] of params.entries()) {
|
|
7321
|
+
const match = key.match(/^filters\[([^\]]+)\]\[([^\]]+)\]$/);
|
|
7322
|
+
if (!match) {
|
|
7323
|
+
throw new Error("Not URLSearchParams format, trying base64");
|
|
7324
|
+
}
|
|
7325
|
+
const [, fieldName, property] = match;
|
|
7326
|
+
if (!fieldMap.has(fieldName)) {
|
|
7327
|
+
fieldMap.set(fieldName, {});
|
|
7328
|
+
}
|
|
7329
|
+
const filter = fieldMap.get(fieldName);
|
|
7330
|
+
switch (property) {
|
|
7331
|
+
case "operator":
|
|
7332
|
+
filter.operator = value;
|
|
7333
|
+
break;
|
|
7334
|
+
case "value":
|
|
7335
|
+
filter.value = value;
|
|
7336
|
+
break;
|
|
7337
|
+
case "values":
|
|
7338
|
+
try {
|
|
7339
|
+
filter.values = JSON.parse(value);
|
|
7340
|
+
} catch {
|
|
7341
|
+
}
|
|
7342
|
+
break;
|
|
7343
|
+
case "caseSensitive":
|
|
7344
|
+
filter.caseSensitive = value === "true";
|
|
7345
|
+
break;
|
|
7346
|
+
}
|
|
7347
|
+
}
|
|
7348
|
+
for (const [fieldName, filter] of fieldMap.entries()) {
|
|
7349
|
+
if (filter.operator) {
|
|
7350
|
+
config[fieldName] = filter;
|
|
7351
|
+
}
|
|
7352
|
+
}
|
|
7353
|
+
const validation = validateFilterConfiguration(config, registry);
|
|
7354
|
+
if (!validation.valid) {
|
|
7355
|
+
console.warn("Deserialized filters failed validation:", validation.errors);
|
|
7356
|
+
return {};
|
|
7357
|
+
}
|
|
7358
|
+
return config;
|
|
7359
|
+
} catch {
|
|
7360
|
+
let jsonString;
|
|
7361
|
+
if (typeof atob !== "undefined") {
|
|
7362
|
+
jsonString = atob(queryString);
|
|
7363
|
+
} else if (typeof Buffer !== "undefined") {
|
|
7364
|
+
jsonString = Buffer.from(queryString, "base64").toString("utf-8");
|
|
7365
|
+
} else {
|
|
7366
|
+
jsonString = decodeURIComponent(queryString);
|
|
7367
|
+
}
|
|
7368
|
+
const config = JSON.parse(jsonString);
|
|
7369
|
+
const validation = validateFilterConfiguration(config, registry);
|
|
7370
|
+
if (!validation.valid) {
|
|
7371
|
+
console.warn("Deserialized filters failed validation:", validation.errors);
|
|
7372
|
+
return {};
|
|
7373
|
+
}
|
|
7374
|
+
return config;
|
|
7375
|
+
}
|
|
7376
|
+
} catch (error) {
|
|
7377
|
+
console.error("Failed to deserialize filters from query string:", error);
|
|
7378
|
+
return {};
|
|
7379
|
+
}
|
|
7380
|
+
}
|
|
7381
|
+
function serializeFiltersToQueryParams(config, registry) {
|
|
7382
|
+
const params = {};
|
|
7383
|
+
for (const [fieldName, filter] of Object.entries(config)) {
|
|
7384
|
+
const fieldMeta = registry[fieldName];
|
|
7385
|
+
if (!fieldMeta) continue;
|
|
7386
|
+
const operator = filter.operator;
|
|
7387
|
+
if (!SHORTHAND_TO_OPERATOR[operator]) {
|
|
7388
|
+
console.warn(`Invalid operator: ${operator}`);
|
|
7389
|
+
continue;
|
|
7390
|
+
}
|
|
7391
|
+
if (isNullCheckOperator(operator)) {
|
|
7392
|
+
params[`flt[${fieldName}][${operator}]`] = "";
|
|
7393
|
+
} else if (requiresArrayValues(operator)) {
|
|
7394
|
+
if (filter.values && Array.isArray(filter.values)) {
|
|
7395
|
+
params[`flt[${fieldName}][${operator}]`] = JSON.stringify(filter.values);
|
|
7396
|
+
}
|
|
7397
|
+
} else {
|
|
7398
|
+
params[`flt[${fieldName}][${operator}]`] = String(filter.value);
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7401
|
+
return params;
|
|
7402
|
+
}
|
|
7403
|
+
function deserializeFiltersFromQueryParams(params, registry) {
|
|
7404
|
+
const config = {};
|
|
7405
|
+
const fieldMap = /* @__PURE__ */ new Map();
|
|
7406
|
+
for (const [key, value] of Object.entries(params)) {
|
|
7407
|
+
const matchDefault = key.match(/^flt\[([^\]]+)\]$/);
|
|
7408
|
+
const matchWithOp = key.match(/^flt\[([^\]]+)\]\[([^\]]+)\]$/);
|
|
7409
|
+
if (!matchDefault && !matchWithOp) continue;
|
|
7410
|
+
const fieldName = matchDefault ? matchDefault[1] : matchWithOp[1];
|
|
7411
|
+
const operatorShorthand = matchWithOp ? matchWithOp[2] : null;
|
|
7412
|
+
const stringValue = Array.isArray(value) ? value[0] : value;
|
|
7413
|
+
const fieldMeta = registry[fieldName];
|
|
7414
|
+
if (!fieldMeta) continue;
|
|
7415
|
+
const fieldType = fieldMeta.type;
|
|
7416
|
+
const defaultOp = DEFAULT_OPERATORS[fieldType] || "eq";
|
|
7417
|
+
let operator;
|
|
7418
|
+
if (operatorShorthand) {
|
|
7419
|
+
if (!SHORTHAND_TO_OPERATOR[operatorShorthand]) {
|
|
7420
|
+
continue;
|
|
7421
|
+
}
|
|
7422
|
+
operator = operatorShorthand;
|
|
7423
|
+
} else {
|
|
7424
|
+
operator = defaultOp;
|
|
7425
|
+
}
|
|
7426
|
+
if (!fieldMap.has(fieldName)) {
|
|
7427
|
+
fieldMap.set(fieldName, { operator });
|
|
7428
|
+
}
|
|
7429
|
+
const filter = fieldMap.get(fieldName);
|
|
7430
|
+
filter.operator = operator;
|
|
7431
|
+
if (isNullCheckOperator(operator)) ; else if (requiresArrayValues(operator)) {
|
|
7432
|
+
if (!stringValue || stringValue.trim() === "") {
|
|
7433
|
+
fieldMap.delete(fieldName);
|
|
7434
|
+
continue;
|
|
7435
|
+
}
|
|
7436
|
+
try {
|
|
7437
|
+
const parsed = JSON.parse(stringValue);
|
|
7438
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
7439
|
+
if (NUMERIC_TYPES.includes(fieldType)) {
|
|
7440
|
+
filter.values = parsed.map((v) => Number(v));
|
|
7441
|
+
} else {
|
|
7442
|
+
filter.values = parsed;
|
|
7443
|
+
}
|
|
7444
|
+
} else {
|
|
7445
|
+
fieldMap.delete(fieldName);
|
|
7446
|
+
continue;
|
|
7447
|
+
}
|
|
7448
|
+
} catch (e) {
|
|
7449
|
+
const values = stringValue.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
7450
|
+
if (values.length === 0) {
|
|
7451
|
+
fieldMap.delete(fieldName);
|
|
7452
|
+
continue;
|
|
7453
|
+
}
|
|
7454
|
+
if (operator === OPERATORS.BETWEEN && values.length === 2) {
|
|
7455
|
+
if (NUMERIC_TYPES.includes(fieldType)) {
|
|
7456
|
+
filter.values = [Number(values[0]), Number(values[1])];
|
|
7457
|
+
} else if (fieldType === DATA_TYPES.DATE) {
|
|
7458
|
+
filter.values = [values[0], values[1]];
|
|
7459
|
+
} else {
|
|
7460
|
+
filter.values = values;
|
|
7461
|
+
}
|
|
7462
|
+
} else if (operator === OPERATORS.BETWEEN) {
|
|
7463
|
+
fieldMap.delete(fieldName);
|
|
7464
|
+
continue;
|
|
7465
|
+
} else {
|
|
7466
|
+
if (NUMERIC_TYPES.includes(fieldType)) {
|
|
7467
|
+
filter.values = values.map((v) => Number(v));
|
|
7468
|
+
} else {
|
|
7469
|
+
filter.values = values;
|
|
7470
|
+
}
|
|
7471
|
+
}
|
|
7472
|
+
}
|
|
7473
|
+
} else {
|
|
7474
|
+
if ((operator === OPERATORS.IS_ONE_OF || operator === OPERATORS.IS_NOT_ONE_OF) && stringValue) {
|
|
7475
|
+
if (NUMERIC_TYPES.includes(fieldType)) {
|
|
7476
|
+
filter.values = [Number(stringValue)];
|
|
7477
|
+
} else {
|
|
7478
|
+
filter.values = [stringValue];
|
|
7479
|
+
}
|
|
7480
|
+
} else {
|
|
7481
|
+
if (NUMERIC_TYPES.includes(fieldType)) {
|
|
7482
|
+
filter.value = Number(stringValue);
|
|
7483
|
+
} else if (fieldType === DATA_TYPES.BOOLEAN) {
|
|
7484
|
+
filter.value = stringValue === "true" || stringValue === "1";
|
|
7485
|
+
} else {
|
|
7486
|
+
filter.value = stringValue;
|
|
7487
|
+
}
|
|
7488
|
+
if (fieldType === DATA_TYPES.STRING && operator === OPERATORS.CONTAINS) {
|
|
7489
|
+
filter.caseSensitive = false;
|
|
7490
|
+
}
|
|
7491
|
+
}
|
|
7492
|
+
}
|
|
7493
|
+
}
|
|
7494
|
+
for (const [fieldName, filter] of fieldMap.entries()) {
|
|
7495
|
+
if (filter.operator) {
|
|
7496
|
+
config[fieldName] = filter;
|
|
7497
|
+
}
|
|
7498
|
+
}
|
|
7499
|
+
const validation = validateFilterConfiguration(config, registry);
|
|
7500
|
+
if (!validation.valid) {
|
|
7501
|
+
console.warn("Deserialized filters failed validation:", validation.errors);
|
|
7502
|
+
return {};
|
|
7503
|
+
}
|
|
7504
|
+
return config;
|
|
7505
|
+
}
|
|
6782
7506
|
function useDataTableState(initialData = [], options = {}) {
|
|
6783
7507
|
const data = ref(initialData);
|
|
6784
7508
|
const isLoading = ref(false);
|
|
@@ -6796,6 +7520,7 @@ function useDataTableState(initialData = [], options = {}) {
|
|
|
6796
7520
|
field: null,
|
|
6797
7521
|
direction: "asc"
|
|
6798
7522
|
});
|
|
7523
|
+
const fieldRegistry = ref(null);
|
|
6799
7524
|
const filters = ref({});
|
|
6800
7525
|
const search = reactive({
|
|
6801
7526
|
query: "",
|
|
@@ -6857,6 +7582,9 @@ function useDataTableState(initialData = [], options = {}) {
|
|
|
6857
7582
|
const setFilters = (newFilters) => {
|
|
6858
7583
|
filters.value = newFilters;
|
|
6859
7584
|
};
|
|
7585
|
+
const setFieldRegistry = (registry) => {
|
|
7586
|
+
fieldRegistry.value = registry;
|
|
7587
|
+
};
|
|
6860
7588
|
const setSearch = (query, searchableFields) => {
|
|
6861
7589
|
search.query = query;
|
|
6862
7590
|
if (searchableFields) {
|
|
@@ -6914,6 +7642,7 @@ function useDataTableState(initialData = [], options = {}) {
|
|
|
6914
7642
|
pagination,
|
|
6915
7643
|
sorting,
|
|
6916
7644
|
filters,
|
|
7645
|
+
fieldRegistry,
|
|
6917
7646
|
search,
|
|
6918
7647
|
selection,
|
|
6919
7648
|
// UI state
|
|
@@ -6937,6 +7666,7 @@ function useDataTableState(initialData = [], options = {}) {
|
|
|
6937
7666
|
setPagination,
|
|
6938
7667
|
setSorting,
|
|
6939
7668
|
setFilters,
|
|
7669
|
+
setFieldRegistry,
|
|
6940
7670
|
setSearch,
|
|
6941
7671
|
// Selection methods
|
|
6942
7672
|
selectRow,
|
|
@@ -6954,6 +7684,7 @@ var ZINIA_DATA_TABLE_ACTIONS_KEY = "ziniaDataTableActions";
|
|
|
6954
7684
|
var ZINIA_DATA_TABLE_SEARCH_INPUT_KEY = "ziniaDataTableSearchInput";
|
|
6955
7685
|
var ZINIA_DATA_TABLE_FILTER_INPUTS_KEY = "ziniaDataTableFilterInputs";
|
|
6956
7686
|
var ZINIA_DATA_TABLE_FILTER_OPERATORS_KEY = "ziniaDataTableFilterOperators";
|
|
7687
|
+
var ZINIA_DATA_TABLE_FILTER_CASE_SENSITIVE_KEY = "ziniaDataTableFilterCaseSensitive";
|
|
6957
7688
|
var ZINIA_DATA_TABLE_FILTER_OPTIONS_STATE_KEY = "ziniaDataTableFilterOptionsState";
|
|
6958
7689
|
var ZINIA_DATA_TABLE_FILTER_OPTIONS_LOADING_KEY = "ziniaDataTableFilterOptionsLoading";
|
|
6959
7690
|
var ZINIA_DATA_TABLE_NAME_KEY = "ziniaDataTableName";
|
|
@@ -6962,12 +7693,15 @@ function useDataTable(schema, options) {
|
|
|
6962
7693
|
schemaId: options.schemaId,
|
|
6963
7694
|
storeName: "dataTable"
|
|
6964
7695
|
});
|
|
7696
|
+
const fieldRegistry = buildFieldRegistryFromSchema(schema, fieldsMetadata);
|
|
6965
7697
|
const tableState = useDataTableState([], {
|
|
6966
7698
|
debug: options.debug
|
|
6967
7699
|
});
|
|
7700
|
+
tableState.setFieldRegistry(fieldRegistry);
|
|
6968
7701
|
const searchInputValue = ref("");
|
|
6969
7702
|
const filterInputValues = ref({});
|
|
6970
7703
|
const filterOperators = ref({});
|
|
7704
|
+
const filterCaseSensitive = ref({});
|
|
6971
7705
|
const filterOptionsState = ref({});
|
|
6972
7706
|
const filterOptionsLoading = ref({});
|
|
6973
7707
|
if (options.pagination?.pageSize) {
|
|
@@ -6984,15 +7718,20 @@ function useDataTable(schema, options) {
|
|
|
6984
7718
|
Object.entries(options.initialFilters).forEach(([field, filter]) => {
|
|
6985
7719
|
if (filter && filter.value !== "" && filter.value !== null && filter.value !== void 0) {
|
|
6986
7720
|
filterInputValues.value[field] = String(filter.value);
|
|
6987
|
-
|
|
7721
|
+
const fieldMetadata = fieldRegistry[field];
|
|
7722
|
+
const defaultOperator = fieldMetadata ? DEFAULT_OPERATORS[fieldMetadata.type] || OPERATORS.EQUALS : OPERATORS.EQUALS;
|
|
7723
|
+
filterOperators.value[field] = filter.operator || defaultOperator;
|
|
6988
7724
|
}
|
|
6989
7725
|
});
|
|
6990
7726
|
}
|
|
6991
7727
|
const extractActiveFilters = (filterState) => {
|
|
6992
7728
|
const activeFilters = {};
|
|
6993
|
-
Object.entries(filterState).forEach(([
|
|
6994
|
-
|
|
6995
|
-
|
|
7729
|
+
Object.entries(filterState).forEach(([fieldName, filter]) => {
|
|
7730
|
+
const hasValue = filter.value !== void 0 && filter.value !== "" && filter.value !== null;
|
|
7731
|
+
const hasValues = filter.values !== void 0 && Array.isArray(filter.values) && filter.values.length > 0;
|
|
7732
|
+
const isNullCheck = filter.operator === OPERATORS.IS_EMPTY || filter.operator === OPERATORS.IS_NOT_EMPTY;
|
|
7733
|
+
if (hasValue || hasValues || isNullCheck) {
|
|
7734
|
+
activeFilters[fieldName] = filter;
|
|
6996
7735
|
}
|
|
6997
7736
|
});
|
|
6998
7737
|
return activeFilters;
|
|
@@ -7022,6 +7761,16 @@ function useDataTable(schema, options) {
|
|
|
7022
7761
|
try {
|
|
7023
7762
|
tableState.setLoading(true);
|
|
7024
7763
|
tableState.setError(null);
|
|
7764
|
+
const activeFilters = extractActiveFilters(tableState.state.filters.value);
|
|
7765
|
+
const validation = validateFilterConfiguration(activeFilters, fieldRegistry);
|
|
7766
|
+
if (!validation.valid) {
|
|
7767
|
+
const errorMessage = `Filter validation failed: ${validation.errors.map((e) => e.message).join(", ")}`;
|
|
7768
|
+
tableState.setError(errorMessage);
|
|
7769
|
+
if (options.debug) {
|
|
7770
|
+
console.error("\u274C Filter validation errors:", validation.errors);
|
|
7771
|
+
}
|
|
7772
|
+
throw new Error(errorMessage);
|
|
7773
|
+
}
|
|
7025
7774
|
if (options.debug) {
|
|
7026
7775
|
console.log("\u{1F504} Fetching table data with params:", {
|
|
7027
7776
|
page: tableState.state.pagination.currentPage,
|
|
@@ -7030,7 +7779,7 @@ function useDataTable(schema, options) {
|
|
|
7030
7779
|
field: tableState.state.sorting.field,
|
|
7031
7780
|
direction: tableState.state.sorting.direction
|
|
7032
7781
|
} : null,
|
|
7033
|
-
filters:
|
|
7782
|
+
filters: activeFilters,
|
|
7034
7783
|
search: {
|
|
7035
7784
|
query: tableState.state.search.query,
|
|
7036
7785
|
searchableFields: tableState.state.search.searchableFields
|
|
@@ -7044,7 +7793,7 @@ function useDataTable(schema, options) {
|
|
|
7044
7793
|
field: tableState.state.sorting.field,
|
|
7045
7794
|
direction: tableState.state.sorting.direction
|
|
7046
7795
|
} : null,
|
|
7047
|
-
filters:
|
|
7796
|
+
filters: activeFilters,
|
|
7048
7797
|
search: {
|
|
7049
7798
|
query: tableState.state.search.query,
|
|
7050
7799
|
searchableFields: tableState.state.search.searchableFields
|
|
@@ -7079,13 +7828,30 @@ function useDataTable(schema, options) {
|
|
|
7079
7828
|
tableState.setPagination(0);
|
|
7080
7829
|
await fetchData();
|
|
7081
7830
|
};
|
|
7082
|
-
const setFilter = async (field,
|
|
7831
|
+
const setFilter = async (field, filter) => {
|
|
7083
7832
|
const currentFilters = { ...tableState.state.filters.value };
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7833
|
+
const fieldName = field;
|
|
7834
|
+
const hasValue = filter.value !== void 0 && filter.value !== "" && filter.value !== null;
|
|
7835
|
+
const hasValues = filter.values !== void 0 && Array.isArray(filter.values) && filter.values.length > 0;
|
|
7836
|
+
const isNullCheck = filter.operator === OPERATORS.IS_EMPTY || filter.operator === OPERATORS.IS_NOT_EMPTY;
|
|
7837
|
+
const isEmpty = !hasValue && !hasValues && !isNullCheck;
|
|
7838
|
+
if (isEmpty) {
|
|
7839
|
+
delete currentFilters[fieldName];
|
|
7840
|
+
tableState.setFilters(currentFilters);
|
|
7841
|
+
tableState.setPagination(0);
|
|
7842
|
+
await fetchData();
|
|
7843
|
+
return;
|
|
7844
|
+
}
|
|
7845
|
+
const validation = validateFilterValueObject(fieldName, filter, fieldRegistry);
|
|
7846
|
+
if (!validation.valid) {
|
|
7847
|
+
const errorMessage = `Invalid filter for field "${fieldName}": ${validation.errors.map((e) => e.message).join(", ")}`;
|
|
7848
|
+
tableState.setError(errorMessage);
|
|
7849
|
+
if (options.debug) {
|
|
7850
|
+
console.error("\u274C Filter validation errors:", validation.errors);
|
|
7851
|
+
}
|
|
7852
|
+
throw new Error(errorMessage);
|
|
7088
7853
|
}
|
|
7854
|
+
currentFilters[fieldName] = filter;
|
|
7089
7855
|
tableState.setFilters(currentFilters);
|
|
7090
7856
|
tableState.setPagination(0);
|
|
7091
7857
|
await fetchData();
|
|
@@ -7095,13 +7861,39 @@ function useDataTable(schema, options) {
|
|
|
7095
7861
|
delete currentFilters[field];
|
|
7096
7862
|
tableState.setFilters(currentFilters);
|
|
7097
7863
|
tableState.setPagination(0);
|
|
7864
|
+
delete filterInputValues.value[field];
|
|
7865
|
+
delete filterInputValues.value[`${field}_start`];
|
|
7866
|
+
delete filterInputValues.value[`${field}_end`];
|
|
7867
|
+
delete filterOperators.value[field];
|
|
7868
|
+
delete filterCaseSensitive.value[field];
|
|
7098
7869
|
await fetchData();
|
|
7099
7870
|
};
|
|
7100
7871
|
const clearAllFilters = async () => {
|
|
7101
7872
|
tableState.setFilters({});
|
|
7102
7873
|
tableState.setPagination(0);
|
|
7874
|
+
filterInputValues.value = {};
|
|
7875
|
+
filterOperators.value = {};
|
|
7876
|
+
filterCaseSensitive.value = {};
|
|
7877
|
+
await fetchData();
|
|
7878
|
+
};
|
|
7879
|
+
const setFiltersFromQueryParams = async (params) => {
|
|
7880
|
+
const filters = deserializeFiltersFromQueryParams(params, fieldRegistry);
|
|
7881
|
+
tableState.setFilters(filters);
|
|
7882
|
+
tableState.setPagination(0);
|
|
7103
7883
|
await fetchData();
|
|
7104
7884
|
};
|
|
7885
|
+
const setFiltersFromQueryString = async (queryString) => {
|
|
7886
|
+
if (queryString && queryString.trim() !== "") {
|
|
7887
|
+
const params = new URLSearchParams(queryString);
|
|
7888
|
+
const paramsObj = {};
|
|
7889
|
+
params.forEach((value, key) => {
|
|
7890
|
+
paramsObj[key] = value;
|
|
7891
|
+
});
|
|
7892
|
+
await setFiltersFromQueryParams(paramsObj);
|
|
7893
|
+
} else {
|
|
7894
|
+
await setFiltersFromQueryParams({});
|
|
7895
|
+
}
|
|
7896
|
+
};
|
|
7105
7897
|
const setSearch = async (query) => {
|
|
7106
7898
|
tableState.setSearch(query);
|
|
7107
7899
|
tableState.setPagination(0);
|
|
@@ -7236,8 +8028,20 @@ function useDataTable(schema, options) {
|
|
|
7236
8028
|
},
|
|
7237
8029
|
get count() {
|
|
7238
8030
|
return Object.keys(extractActiveFilters(tableState.state.filters.value)).length;
|
|
8031
|
+
},
|
|
8032
|
+
get queryParams() {
|
|
8033
|
+
const activeFilters = extractActiveFilters(tableState.state.filters.value);
|
|
8034
|
+
return serializeFiltersToQueryParams(activeFilters, fieldRegistry);
|
|
8035
|
+
},
|
|
8036
|
+
// Legacy: queryString for backward compatibility (returns empty string, use queryParams instead)
|
|
8037
|
+
get queryString() {
|
|
8038
|
+
return "";
|
|
7239
8039
|
}
|
|
7240
8040
|
},
|
|
8041
|
+
// Field registry
|
|
8042
|
+
get fieldRegistry() {
|
|
8043
|
+
return fieldRegistry;
|
|
8044
|
+
},
|
|
7241
8045
|
// Search
|
|
7242
8046
|
search: {
|
|
7243
8047
|
get query() {
|
|
@@ -7270,6 +8074,8 @@ function useDataTable(schema, options) {
|
|
|
7270
8074
|
setFilter,
|
|
7271
8075
|
clearFilter,
|
|
7272
8076
|
clearAllFilters,
|
|
8077
|
+
setFiltersFromQueryParams,
|
|
8078
|
+
setFiltersFromQueryString,
|
|
7273
8079
|
setSearch,
|
|
7274
8080
|
clearSearch,
|
|
7275
8081
|
goToPage,
|
|
@@ -7303,6 +8109,7 @@ function useDataTable(schema, options) {
|
|
|
7303
8109
|
provide(ZINIA_DATA_TABLE_SEARCH_INPUT_KEY, searchInputValue);
|
|
7304
8110
|
provide(ZINIA_DATA_TABLE_FILTER_INPUTS_KEY, filterInputValues);
|
|
7305
8111
|
provide(ZINIA_DATA_TABLE_FILTER_OPERATORS_KEY, filterOperators);
|
|
8112
|
+
provide(ZINIA_DATA_TABLE_FILTER_CASE_SENSITIVE_KEY, filterCaseSensitive);
|
|
7306
8113
|
provide(ZINIA_DATA_TABLE_FILTER_OPTIONS_STATE_KEY, filterOptionsState);
|
|
7307
8114
|
provide(ZINIA_DATA_TABLE_FILTER_OPTIONS_LOADING_KEY, filterOptionsLoading);
|
|
7308
8115
|
provide(ZINIA_DATA_TABLE_NAME_KEY, options.name || "datatable");
|
|
@@ -7312,11 +8119,13 @@ function useDataTable(schema, options) {
|
|
|
7312
8119
|
console.error("Failed to load filter options:", err);
|
|
7313
8120
|
}
|
|
7314
8121
|
});
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
8122
|
+
if (options.autoLoad !== false) {
|
|
8123
|
+
fetchData().catch((err) => {
|
|
8124
|
+
if (options.debug) {
|
|
8125
|
+
console.error("Failed to auto-load table data:", err);
|
|
8126
|
+
}
|
|
8127
|
+
});
|
|
8128
|
+
}
|
|
7320
8129
|
return {
|
|
7321
8130
|
table,
|
|
7322
8131
|
// Components
|
|
@@ -7329,6 +8138,7 @@ function useDataTable(schema, options) {
|
|
|
7329
8138
|
setFilter,
|
|
7330
8139
|
clearFilter,
|
|
7331
8140
|
clearAllFilters,
|
|
8141
|
+
setFiltersFromQueryString,
|
|
7332
8142
|
setSearch,
|
|
7333
8143
|
clearSearch,
|
|
7334
8144
|
goToPage,
|
|
@@ -7358,6 +8168,10 @@ function getFilterType(field, column, fieldsMetadata) {
|
|
|
7358
8168
|
return "boolean";
|
|
7359
8169
|
case "combobox":
|
|
7360
8170
|
return "combobox";
|
|
8171
|
+
case "date":
|
|
8172
|
+
case "datetime-local":
|
|
8173
|
+
case "time":
|
|
8174
|
+
return "date";
|
|
7361
8175
|
case "select":
|
|
7362
8176
|
case "radio":
|
|
7363
8177
|
return column.filterOptions || fieldMetadata?.options ? "select" : "text";
|
|
@@ -7709,170 +8523,787 @@ var DesktopTable = (props) => {
|
|
|
7709
8523
|
] })
|
|
7710
8524
|
] }) });
|
|
7711
8525
|
};
|
|
7712
|
-
var
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
|
|
7724
|
-
children: [
|
|
7725
|
-
props.showAllOption !== false && /* @__PURE__ */ jsx("option", { value: "", children: props.isOptionsLoading ? "Loading options..." : props.allOptionText !== void 0 ? props.allOptionText : `All ${props.label}` }),
|
|
7726
|
-
props.options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
|
|
7727
|
-
]
|
|
7728
|
-
}
|
|
7729
|
-
);
|
|
7730
|
-
};
|
|
7731
|
-
|
|
7732
|
-
// src/fields/daisy_ui/datatable/helpers/debounce.ts
|
|
7733
|
-
var timeoutStore = /* @__PURE__ */ new Map();
|
|
7734
|
-
function debounce(key, callback, delay = 300) {
|
|
7735
|
-
const existing = timeoutStore.get(key);
|
|
7736
|
-
if (existing) {
|
|
7737
|
-
clearTimeout(existing);
|
|
8526
|
+
var dropdownState = reactive({});
|
|
8527
|
+
var DROPDOWN_MAX_HEIGHT = 240;
|
|
8528
|
+
var MIN_SPACE_BELOW = 100;
|
|
8529
|
+
var EnumMultiSelectFilter = (props) => {
|
|
8530
|
+
if (!dropdownState[props.field]) {
|
|
8531
|
+
dropdownState[props.field] = {
|
|
8532
|
+
isOpen: false,
|
|
8533
|
+
searchQuery: "",
|
|
8534
|
+
selectedIndex: -1,
|
|
8535
|
+
position: { top: 0, left: 0, width: 0, openUpward: false },
|
|
8536
|
+
cleanupFns: []
|
|
8537
|
+
};
|
|
7738
8538
|
}
|
|
7739
|
-
const
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
}
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
8539
|
+
const state = dropdownState[props.field];
|
|
8540
|
+
const selectedValues = state.isOpen && state.pendingValues !== void 0 ? state.pendingValues : Array.isArray(props.value) ? props.value : props.value ? [props.value] : [];
|
|
8541
|
+
const valuesEqual = (a, b) => {
|
|
8542
|
+
if (typeof a === typeof b) {
|
|
8543
|
+
return a === b;
|
|
8544
|
+
}
|
|
8545
|
+
return String(a) === String(b);
|
|
8546
|
+
};
|
|
8547
|
+
const selectedOptions = computed(() => {
|
|
8548
|
+
return selectedValues.map((val) => {
|
|
8549
|
+
const option = props.options.find((opt) => valuesEqual(opt.value, val));
|
|
8550
|
+
return option || { label: String(val), value: val };
|
|
8551
|
+
}).filter((opt) => Boolean(opt));
|
|
8552
|
+
});
|
|
8553
|
+
const filteredOptions = computed(() => {
|
|
8554
|
+
const query = state.searchQuery.trim().toLowerCase();
|
|
8555
|
+
if (!query) {
|
|
8556
|
+
return props.options;
|
|
8557
|
+
}
|
|
8558
|
+
return props.options.filter((opt) => opt.label.toLowerCase().includes(query));
|
|
8559
|
+
});
|
|
8560
|
+
const updateDropdownPosition = () => {
|
|
8561
|
+
const container = document.querySelector(`[data-enum-multiselect-container="${props.field}"]`);
|
|
8562
|
+
if (!container) return;
|
|
8563
|
+
const rect = container.getBoundingClientRect();
|
|
8564
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
8565
|
+
const spaceAbove = rect.top;
|
|
8566
|
+
const openUpward = spaceBelow < MIN_SPACE_BELOW && spaceAbove > spaceBelow;
|
|
8567
|
+
state.position = {
|
|
8568
|
+
top: openUpward ? rect.top - 4 : rect.bottom + 4,
|
|
8569
|
+
left: rect.left,
|
|
8570
|
+
width: rect.width,
|
|
8571
|
+
openUpward
|
|
8572
|
+
};
|
|
8573
|
+
};
|
|
8574
|
+
const applyPendingFilter = () => {
|
|
8575
|
+
if (state.pendingValues === void 0) return;
|
|
8576
|
+
if (state.pendingValues.length === 0) {
|
|
8577
|
+
if (props.onClearFilter) {
|
|
8578
|
+
props.onClearFilter(props.field);
|
|
8579
|
+
}
|
|
8580
|
+
state.pendingValues = void 0;
|
|
8581
|
+
return;
|
|
8582
|
+
}
|
|
8583
|
+
const filter = {
|
|
8584
|
+
operator: props.operator,
|
|
8585
|
+
values: state.pendingValues
|
|
8586
|
+
};
|
|
8587
|
+
props.onFilterChange(props.field, filter);
|
|
8588
|
+
state.pendingValues = void 0;
|
|
8589
|
+
};
|
|
8590
|
+
const toggleOption = (option) => {
|
|
8591
|
+
const currentValues = [...selectedValues];
|
|
8592
|
+
const index = currentValues.findIndex((v) => valuesEqual(v, option.value));
|
|
8593
|
+
if (index >= 0) {
|
|
8594
|
+
currentValues.splice(index, 1);
|
|
8595
|
+
} else {
|
|
8596
|
+
currentValues.push(option.value);
|
|
8597
|
+
}
|
|
8598
|
+
state.pendingValues = currentValues;
|
|
8599
|
+
nextTick(() => {
|
|
8600
|
+
updateDropdownPosition();
|
|
8601
|
+
});
|
|
8602
|
+
};
|
|
8603
|
+
const removeSelected = (value) => {
|
|
8604
|
+
const currentValues = (Array.isArray(props.value) ? props.value : props.value ? [props.value] : []).filter(
|
|
8605
|
+
(v) => !valuesEqual(v, value)
|
|
8606
|
+
);
|
|
8607
|
+
state.isOpen = false;
|
|
8608
|
+
state.searchQuery = "";
|
|
8609
|
+
state.selectedIndex = -1;
|
|
8610
|
+
state.pendingValues = void 0;
|
|
8611
|
+
cleanupEventListeners();
|
|
8612
|
+
if (currentValues.length === 0) {
|
|
8613
|
+
if (props.onClearFilter) {
|
|
8614
|
+
props.onClearFilter(props.field);
|
|
8615
|
+
}
|
|
8616
|
+
return;
|
|
8617
|
+
}
|
|
8618
|
+
const filter = {
|
|
8619
|
+
operator: props.operator,
|
|
8620
|
+
values: currentValues
|
|
8621
|
+
};
|
|
8622
|
+
props.onFilterChange(props.field, filter);
|
|
8623
|
+
};
|
|
8624
|
+
const handleScrollOrResize = () => {
|
|
8625
|
+
if (state.isOpen) {
|
|
8626
|
+
updateDropdownPosition();
|
|
8627
|
+
}
|
|
8628
|
+
};
|
|
8629
|
+
const handleClickOutside = (e) => {
|
|
8630
|
+
const container = document.querySelector(`[data-enum-multiselect-container="${props.field}"]`);
|
|
8631
|
+
const dropdown = document.querySelector(`[data-enum-multiselect-dropdown="${props.field}"]`);
|
|
8632
|
+
const target = e.target;
|
|
8633
|
+
if (state.isOpen && container && !container.contains(target) && dropdown && !dropdown.contains(target)) {
|
|
8634
|
+
applyPendingFilter();
|
|
8635
|
+
state.isOpen = false;
|
|
8636
|
+
state.searchQuery = "";
|
|
8637
|
+
state.selectedIndex = -1;
|
|
8638
|
+
cleanupEventListeners();
|
|
8639
|
+
}
|
|
8640
|
+
};
|
|
8641
|
+
if (!state.cleanupFns) {
|
|
8642
|
+
state.cleanupFns = [];
|
|
8643
|
+
}
|
|
8644
|
+
const setupEventListeners = () => {
|
|
8645
|
+
if (!state.cleanupFns) {
|
|
8646
|
+
state.cleanupFns = [];
|
|
8647
|
+
}
|
|
8648
|
+
if (state.cleanupFns.length > 0) return;
|
|
8649
|
+
window.addEventListener("scroll", handleScrollOrResize, true);
|
|
8650
|
+
window.addEventListener("resize", handleScrollOrResize);
|
|
8651
|
+
document.addEventListener("click", handleClickOutside, true);
|
|
8652
|
+
state.cleanupFns.push(() => {
|
|
8653
|
+
window.removeEventListener("scroll", handleScrollOrResize, true);
|
|
8654
|
+
window.removeEventListener("resize", handleScrollOrResize);
|
|
8655
|
+
document.removeEventListener("click", handleClickOutside, true);
|
|
8656
|
+
});
|
|
8657
|
+
};
|
|
8658
|
+
const cleanupEventListeners = () => {
|
|
8659
|
+
if (state.cleanupFns && state.cleanupFns.length > 0) {
|
|
8660
|
+
state.cleanupFns.forEach((fn) => fn());
|
|
8661
|
+
state.cleanupFns = [];
|
|
8662
|
+
}
|
|
8663
|
+
};
|
|
8664
|
+
const handleFocus = () => {
|
|
8665
|
+
if (state.pendingValues === void 0) {
|
|
8666
|
+
state.pendingValues = Array.isArray(props.value) ? [...props.value] : props.value ? [props.value] : [];
|
|
8667
|
+
}
|
|
8668
|
+
state.isOpen = true;
|
|
8669
|
+
updateDropdownPosition();
|
|
8670
|
+
setupEventListeners();
|
|
8671
|
+
};
|
|
8672
|
+
const handleBlur = (e) => {
|
|
8673
|
+
setTimeout(() => {
|
|
8674
|
+
const relatedTarget = e.relatedTarget || document.activeElement;
|
|
8675
|
+
const container = document.querySelector(
|
|
8676
|
+
`[data-enum-multiselect-container="${props.field}"]`
|
|
8677
|
+
);
|
|
8678
|
+
const dropdown = document.querySelector(`[data-enum-multiselect-dropdown="${props.field}"]`);
|
|
8679
|
+
if (container && container.contains(relatedTarget) || dropdown && dropdown.contains(relatedTarget)) {
|
|
8680
|
+
return;
|
|
8681
|
+
}
|
|
8682
|
+
if (state.isOpen) {
|
|
8683
|
+
applyPendingFilter();
|
|
8684
|
+
state.isOpen = false;
|
|
8685
|
+
state.searchQuery = "";
|
|
8686
|
+
state.selectedIndex = -1;
|
|
8687
|
+
cleanupEventListeners();
|
|
8688
|
+
}
|
|
8689
|
+
}, 100);
|
|
8690
|
+
};
|
|
8691
|
+
const handleKeyDown = (e) => {
|
|
8692
|
+
if (e.key === "Escape") {
|
|
8693
|
+
e.preventDefault();
|
|
8694
|
+
e.stopPropagation();
|
|
8695
|
+
state.pendingValues = void 0;
|
|
8696
|
+
state.isOpen = false;
|
|
8697
|
+
state.searchQuery = "";
|
|
8698
|
+
state.selectedIndex = -1;
|
|
8699
|
+
cleanupEventListeners();
|
|
8700
|
+
return;
|
|
8701
|
+
}
|
|
8702
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
8703
|
+
e.preventDefault();
|
|
8704
|
+
e.stopPropagation();
|
|
8705
|
+
if (state.selectedIndex >= 0 && state.selectedIndex < filteredOptions.value.length) {
|
|
8706
|
+
toggleOption(filteredOptions.value[state.selectedIndex]);
|
|
8707
|
+
} else if (e.key === "Enter" && filteredOptions.value.length === 1) {
|
|
8708
|
+
toggleOption(filteredOptions.value[0]);
|
|
8709
|
+
}
|
|
8710
|
+
return;
|
|
8711
|
+
}
|
|
8712
|
+
if (e.key === "ArrowDown") {
|
|
8713
|
+
e.preventDefault();
|
|
8714
|
+
e.stopPropagation();
|
|
8715
|
+
if (!state.isOpen) {
|
|
8716
|
+
state.isOpen = true;
|
|
8717
|
+
updateDropdownPosition();
|
|
8718
|
+
setupEventListeners();
|
|
8719
|
+
}
|
|
8720
|
+
const totalOptions = filteredOptions.value.length;
|
|
8721
|
+
if (totalOptions > 0) {
|
|
8722
|
+
state.selectedIndex = state.selectedIndex < 0 ? 0 : (state.selectedIndex + 1) % totalOptions;
|
|
8723
|
+
nextTick(() => scrollToSelected());
|
|
8724
|
+
}
|
|
8725
|
+
return;
|
|
8726
|
+
}
|
|
8727
|
+
if (e.key === "ArrowUp") {
|
|
8728
|
+
e.preventDefault();
|
|
8729
|
+
e.stopPropagation();
|
|
8730
|
+
if (!state.isOpen) {
|
|
8731
|
+
state.isOpen = true;
|
|
8732
|
+
updateDropdownPosition();
|
|
8733
|
+
setupEventListeners();
|
|
8734
|
+
}
|
|
8735
|
+
const totalOptions = filteredOptions.value.length;
|
|
8736
|
+
if (totalOptions > 0) {
|
|
8737
|
+
state.selectedIndex = state.selectedIndex < 0 ? totalOptions - 1 : (state.selectedIndex - 1 + totalOptions) % totalOptions;
|
|
8738
|
+
nextTick(() => scrollToSelected());
|
|
8739
|
+
}
|
|
8740
|
+
return;
|
|
8741
|
+
}
|
|
8742
|
+
if (e.key === "Tab") {
|
|
8743
|
+
applyPendingFilter();
|
|
8744
|
+
state.isOpen = false;
|
|
8745
|
+
state.searchQuery = "";
|
|
8746
|
+
state.selectedIndex = -1;
|
|
8747
|
+
cleanupEventListeners();
|
|
8748
|
+
}
|
|
8749
|
+
};
|
|
8750
|
+
const scrollToSelected = () => {
|
|
8751
|
+
const dropdown = document.querySelector(`[data-enum-multiselect-dropdown="${props.field}"]`);
|
|
8752
|
+
if (!dropdown || state.selectedIndex < 0) return;
|
|
8753
|
+
const option = dropdown.querySelector(`[data-option-index="${state.selectedIndex}"]`);
|
|
8754
|
+
if (!option) return;
|
|
8755
|
+
const elementTop = option.offsetTop;
|
|
8756
|
+
const elementHeight = option.offsetHeight;
|
|
8757
|
+
const containerHeight = dropdown.clientHeight;
|
|
8758
|
+
const containerScrollTop = dropdown.scrollTop;
|
|
8759
|
+
if (elementTop < containerScrollTop) {
|
|
8760
|
+
dropdown.scrollTop = elementTop;
|
|
8761
|
+
} else if (elementTop + elementHeight > containerScrollTop + containerHeight) {
|
|
8762
|
+
dropdown.scrollTop = elementTop + elementHeight - containerHeight;
|
|
8763
|
+
}
|
|
8764
|
+
};
|
|
8765
|
+
const dropdownStyle = computed(() => {
|
|
8766
|
+
const pos = state.position;
|
|
8767
|
+
return {
|
|
8768
|
+
position: "fixed",
|
|
8769
|
+
top: pos.openUpward ? "auto" : `${pos.top}px`,
|
|
8770
|
+
bottom: pos.openUpward ? `${window.innerHeight - pos.top}px` : "auto",
|
|
8771
|
+
left: `${pos.left}px`,
|
|
8772
|
+
width: `${pos.width}px`,
|
|
8773
|
+
maxHeight: `${DROPDOWN_MAX_HEIGHT}px`,
|
|
8774
|
+
zIndex: 9999
|
|
8775
|
+
};
|
|
8776
|
+
});
|
|
8777
|
+
return /* @__PURE__ */ jsxs("div", { class: "flex flex-col gap-2 w-full", children: [
|
|
8778
|
+
selectedOptions.value.length > 0 && /* @__PURE__ */ jsx("div", { class: "flex flex-wrap gap-1.5 max-h-24 overflow-y-auto", children: selectedOptions.value.map((option) => /* @__PURE__ */ jsxs(
|
|
8779
|
+
"div",
|
|
8780
|
+
{
|
|
8781
|
+
class: "badge badge-primary badge-sm gap-1.5 pr-1 max-w-full",
|
|
8782
|
+
"data-testid": `datatable-filter-${props.field}-chip-${option.value}`,
|
|
8783
|
+
children: [
|
|
8784
|
+
/* @__PURE__ */ jsx("span", { class: "truncate max-w-[120px] sm:max-w-[200px]", title: option.label, children: option.label }),
|
|
8785
|
+
/* @__PURE__ */ jsx(
|
|
8786
|
+
"button",
|
|
8787
|
+
{
|
|
8788
|
+
type: "button",
|
|
8789
|
+
onClick: (e) => {
|
|
8790
|
+
e.preventDefault();
|
|
8791
|
+
e.stopPropagation();
|
|
8792
|
+
removeSelected(option.value);
|
|
8793
|
+
},
|
|
8794
|
+
onMousedown: (e) => {
|
|
8795
|
+
e.preventDefault();
|
|
8796
|
+
},
|
|
8797
|
+
class: "btn btn-ghost btn-xs p-0 h-4 w-4 min-h-0 rounded-full hover:bg-primary-focus flex-shrink-0",
|
|
8798
|
+
"aria-label": `Remove ${option.label}`,
|
|
8799
|
+
tabindex: -1,
|
|
8800
|
+
children: /* @__PURE__ */ jsx(
|
|
8801
|
+
"svg",
|
|
8802
|
+
{
|
|
8803
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
8804
|
+
class: "h-3 w-3",
|
|
8805
|
+
fill: "none",
|
|
8806
|
+
viewBox: "0 0 24 24",
|
|
8807
|
+
stroke: "currentColor",
|
|
8808
|
+
"stroke-width": "2",
|
|
8809
|
+
children: /* @__PURE__ */ jsx("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M6 18L18 6M6 6l12 12" })
|
|
8810
|
+
}
|
|
8811
|
+
)
|
|
8812
|
+
}
|
|
8813
|
+
)
|
|
8814
|
+
]
|
|
8815
|
+
},
|
|
8816
|
+
String(option.value)
|
|
8817
|
+
)) }),
|
|
8818
|
+
/* @__PURE__ */ jsxs("div", { class: "relative", "data-enum-multiselect-container": props.field, children: [
|
|
8819
|
+
/* @__PURE__ */ jsx(
|
|
8820
|
+
"input",
|
|
8821
|
+
{
|
|
8822
|
+
type: "text",
|
|
8823
|
+
value: state.searchQuery,
|
|
8824
|
+
onInput: (e) => {
|
|
8825
|
+
const value = e.target.value;
|
|
8826
|
+
state.searchQuery = value;
|
|
8827
|
+
state.isOpen = true;
|
|
8828
|
+
state.selectedIndex = -1;
|
|
8829
|
+
updateDropdownPosition();
|
|
8830
|
+
if (!state.cleanupFns || state.cleanupFns.length === 0) {
|
|
8831
|
+
setupEventListeners();
|
|
8832
|
+
}
|
|
8833
|
+
},
|
|
8834
|
+
onFocus: handleFocus,
|
|
8835
|
+
onBlur: handleBlur,
|
|
8836
|
+
onKeydown: handleKeyDown,
|
|
8837
|
+
placeholder: props.isOptionsLoading ? "Loading options..." : selectedOptions.value.length > 0 ? `Search ${props.label}...` : `Select ${props.label}...`,
|
|
8838
|
+
class: "input input-bordered input-sm w-full pr-8",
|
|
8839
|
+
disabled: props.isLoading || props.isOptionsLoading,
|
|
8840
|
+
"data-testid": `datatable-filter-${props.field}-input`
|
|
8841
|
+
}
|
|
8842
|
+
),
|
|
8843
|
+
/* @__PURE__ */ jsx("div", { class: "absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none", children: /* @__PURE__ */ jsx(
|
|
8844
|
+
"svg",
|
|
8845
|
+
{
|
|
8846
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
8847
|
+
class: `h-4 w-4 text-base-content/50 transition-transform ${state.isOpen ? "rotate-180" : ""}`,
|
|
8848
|
+
fill: "none",
|
|
8849
|
+
viewBox: "0 0 24 24",
|
|
8850
|
+
stroke: "currentColor",
|
|
8851
|
+
"stroke-width": "2",
|
|
8852
|
+
children: /* @__PURE__ */ jsx("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M19 9l-7 7-7-7" })
|
|
8853
|
+
}
|
|
8854
|
+
) }),
|
|
8855
|
+
state.isOpen && /* @__PURE__ */ jsx(Teleport, { to: "body", children: /* @__PURE__ */ jsx(
|
|
8856
|
+
"div",
|
|
8857
|
+
{
|
|
8858
|
+
"data-enum-multiselect-dropdown": props.field,
|
|
8859
|
+
class: "bg-base-100 border border-base-300 rounded-box shadow-lg overflow-auto",
|
|
8860
|
+
style: dropdownStyle.value,
|
|
8861
|
+
onMousedown: (e) => {
|
|
8862
|
+
e.preventDefault();
|
|
8863
|
+
},
|
|
8864
|
+
children: props.isOptionsLoading ? /* @__PURE__ */ jsx("div", { class: "p-2", children: /* @__PURE__ */ jsx("span", { class: "text-sm text-base-content/70", children: "Loading options..." }) }) : props.options.length === 0 ? /* @__PURE__ */ jsx("div", { class: "p-2", children: /* @__PURE__ */ jsx("span", { class: "text-sm text-base-content/70", children: "No options available" }) }) : filteredOptions.value.length === 0 ? /* @__PURE__ */ jsx("div", { class: "p-2", children: /* @__PURE__ */ jsxs("span", { class: "text-sm text-base-content/70", children: [
|
|
8865
|
+
'No options found matching "',
|
|
8866
|
+
state.searchQuery,
|
|
8867
|
+
'"'
|
|
8868
|
+
] }) }) : /* @__PURE__ */ jsx("ul", { class: "menu menu-sm w-full p-2", role: "listbox", "aria-label": `${props.label} options`, children: filteredOptions.value.map((option, index) => {
|
|
8869
|
+
const isSelected = selectedValues.some((v) => valuesEqual(v, option.value));
|
|
8870
|
+
const isHighlighted = index === state.selectedIndex;
|
|
8871
|
+
return /* @__PURE__ */ jsx(
|
|
8872
|
+
"li",
|
|
8873
|
+
{
|
|
8874
|
+
"data-option-index": index,
|
|
8875
|
+
role: "option",
|
|
8876
|
+
"aria-selected": isSelected,
|
|
8877
|
+
class: isHighlighted ? "bg-base-200 rounded" : "",
|
|
8878
|
+
children: /* @__PURE__ */ jsxs(
|
|
8879
|
+
"label",
|
|
8880
|
+
{
|
|
8881
|
+
class: "label cursor-pointer gap-2 p-2 rounded hover:bg-base-200",
|
|
8882
|
+
onMousedown: (e) => {
|
|
8883
|
+
e.preventDefault();
|
|
8884
|
+
},
|
|
8885
|
+
children: [
|
|
8886
|
+
/* @__PURE__ */ jsx(
|
|
8887
|
+
"input",
|
|
8888
|
+
{
|
|
8889
|
+
type: "checkbox",
|
|
8890
|
+
checked: isSelected,
|
|
8891
|
+
onChange: () => toggleOption(option),
|
|
8892
|
+
class: "checkbox checkbox-sm checkbox-primary",
|
|
8893
|
+
"data-testid": `datatable-filter-${props.field}-option-${option.value}`,
|
|
8894
|
+
tabindex: -1
|
|
8895
|
+
}
|
|
8896
|
+
),
|
|
8897
|
+
/* @__PURE__ */ jsx("span", { class: "label-text flex-1 truncate", title: option.label, children: option.label })
|
|
8898
|
+
]
|
|
8899
|
+
}
|
|
8900
|
+
)
|
|
8901
|
+
},
|
|
8902
|
+
String(option.value)
|
|
8903
|
+
);
|
|
8904
|
+
}) })
|
|
8905
|
+
}
|
|
8906
|
+
) })
|
|
8907
|
+
] }),
|
|
8908
|
+
/* @__PURE__ */ jsx("div", { class: "text-xs text-base-content/60", children: props.operator === "isOneOf" ? "Select one or more values" : "Exclude one or more values" })
|
|
8909
|
+
] });
|
|
8910
|
+
};
|
|
8911
|
+
var SelectFilter = (props) => {
|
|
8912
|
+
const currentOperator = props.operator || OPERATORS.IS_ONE_OF;
|
|
8913
|
+
const isArrayOperator = currentOperator === OPERATORS.IS_ONE_OF || currentOperator === OPERATORS.IS_NOT_ONE_OF;
|
|
8914
|
+
const isDefaultOperator = currentOperator === OPERATORS.IS_ONE_OF;
|
|
8915
|
+
const showOperatorSelect = !isDefaultOperator;
|
|
8916
|
+
const selectedValues = Array.isArray(props.value) ? props.value : props.value ? [props.value] : [];
|
|
8917
|
+
const handleValueChange = (value) => {
|
|
8918
|
+
if (value === "" || value === null || value === void 0) {
|
|
8919
|
+
props.onFilterChange(props.field, { operator: currentOperator, value: "" });
|
|
8920
|
+
return;
|
|
8921
|
+
}
|
|
8922
|
+
const filter = {
|
|
8923
|
+
operator: currentOperator
|
|
8924
|
+
};
|
|
8925
|
+
if (isArrayOperator) {
|
|
8926
|
+
filter.values = Array.isArray(value) ? value : [value];
|
|
8927
|
+
} else {
|
|
8928
|
+
filter.value = value;
|
|
8929
|
+
}
|
|
8930
|
+
props.onFilterChange(props.field, filter);
|
|
8931
|
+
};
|
|
8932
|
+
if (isArrayOperator) {
|
|
8933
|
+
return /* @__PURE__ */ jsxs("div", { class: "flex flex-col gap-1.5 md:gap-2 w-full", children: [
|
|
8934
|
+
showOperatorSelect && /* @__PURE__ */ jsxs(
|
|
8935
|
+
"select",
|
|
8936
|
+
{
|
|
8937
|
+
value: currentOperator,
|
|
8938
|
+
onChange: (e) => {
|
|
8939
|
+
const newOperator = e.target.value;
|
|
8940
|
+
const currentValue = props.value;
|
|
8941
|
+
if (currentValue !== "" && currentValue !== null && currentValue !== void 0) {
|
|
8942
|
+
const filter = {
|
|
8943
|
+
operator: newOperator
|
|
8944
|
+
};
|
|
8945
|
+
if (newOperator === OPERATORS.IS_ONE_OF || newOperator === OPERATORS.IS_NOT_ONE_OF) {
|
|
8946
|
+
filter.values = Array.isArray(currentValue) ? currentValue : [currentValue];
|
|
8947
|
+
} else {
|
|
8948
|
+
filter.value = Array.isArray(currentValue) ? currentValue[0] : currentValue;
|
|
8949
|
+
}
|
|
8950
|
+
props.onFilterChange(props.field, filter);
|
|
8951
|
+
}
|
|
8952
|
+
},
|
|
8953
|
+
class: "select select-bordered select-sm w-full text-xs font-medium",
|
|
8954
|
+
"data-testid": `datatable-filter-${props.field}-operator`,
|
|
8955
|
+
children: [
|
|
8956
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.IS_ONE_OF, children: "in" }),
|
|
8957
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.IS_NOT_ONE_OF, children: "notIn" })
|
|
8958
|
+
]
|
|
8959
|
+
}
|
|
8960
|
+
),
|
|
8961
|
+
/* @__PURE__ */ jsx(
|
|
8962
|
+
EnumMultiSelectFilter,
|
|
8963
|
+
{
|
|
8964
|
+
field: props.field,
|
|
8965
|
+
value: selectedValues,
|
|
8966
|
+
options: props.options,
|
|
8967
|
+
label: props.label,
|
|
8968
|
+
isLoading: props.isLoading,
|
|
8969
|
+
isOptionsLoading: props.isOptionsLoading,
|
|
8970
|
+
operator: currentOperator,
|
|
8971
|
+
onFilterChange: props.onFilterChange,
|
|
8972
|
+
onClearFilter: props.onClearFilter
|
|
8973
|
+
}
|
|
8974
|
+
)
|
|
8975
|
+
] });
|
|
8976
|
+
}
|
|
8977
|
+
return /* @__PURE__ */ jsx("div", { class: "flex flex-col gap-1.5 md:gap-2 w-full", children: /* @__PURE__ */ jsxs("div", { class: "flex gap-1.5 md:gap-2 w-full items-center", children: [
|
|
8978
|
+
showOperatorSelect && /* @__PURE__ */ jsxs(
|
|
8979
|
+
"select",
|
|
8980
|
+
{
|
|
8981
|
+
value: currentOperator,
|
|
8982
|
+
onChange: (e) => {
|
|
8983
|
+
const newOperator = e.target.value;
|
|
8984
|
+
const currentValue = props.value;
|
|
8985
|
+
if (currentValue !== "" && currentValue !== null && currentValue !== void 0) {
|
|
8986
|
+
const filter = {
|
|
8987
|
+
operator: newOperator,
|
|
8988
|
+
value: Array.isArray(currentValue) ? currentValue[0] : currentValue
|
|
8989
|
+
};
|
|
8990
|
+
props.onFilterChange(props.field, filter);
|
|
8991
|
+
}
|
|
8992
|
+
},
|
|
8993
|
+
class: "select select-bordered select-sm w-20 md:w-24 flex-shrink-0 text-xs font-medium",
|
|
8994
|
+
"data-testid": `datatable-filter-${props.field}-operator`,
|
|
8995
|
+
children: [
|
|
8996
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.EQUALS, children: "eq" }),
|
|
8997
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.NOT_EQUALS, children: "ne" })
|
|
8998
|
+
]
|
|
8999
|
+
}
|
|
9000
|
+
),
|
|
9001
|
+
/* @__PURE__ */ jsxs(
|
|
9002
|
+
"select",
|
|
9003
|
+
{
|
|
9004
|
+
value: props.value || "",
|
|
9005
|
+
onChange: (e) => {
|
|
9006
|
+
const value = e.target.value;
|
|
9007
|
+
handleValueChange(value);
|
|
9008
|
+
},
|
|
9009
|
+
class: "select select-bordered select-sm flex-1 min-w-0",
|
|
9010
|
+
disabled: props.isLoading || props.isOptionsLoading,
|
|
9011
|
+
"data-testid": `datatable-filter-${props.field}-input`,
|
|
9012
|
+
children: [
|
|
9013
|
+
props.showAllOption !== false && /* @__PURE__ */ jsx("option", { value: "", children: props.isOptionsLoading ? "Loading options..." : props.allOptionText !== void 0 ? props.allOptionText : `All ${props.label}` }),
|
|
9014
|
+
props.options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
|
|
9015
|
+
]
|
|
9016
|
+
}
|
|
9017
|
+
)
|
|
9018
|
+
] }) });
|
|
9019
|
+
};
|
|
9020
|
+
|
|
9021
|
+
// src/fields/daisy_ui/datatable/helpers/debounce.ts
|
|
9022
|
+
var timeoutStore = /* @__PURE__ */ new Map();
|
|
9023
|
+
function debounce(key, callback, delay = 300) {
|
|
9024
|
+
const existing = timeoutStore.get(key);
|
|
9025
|
+
if (existing) {
|
|
9026
|
+
clearTimeout(existing);
|
|
9027
|
+
}
|
|
9028
|
+
const timeoutId = setTimeout(() => {
|
|
9029
|
+
callback();
|
|
9030
|
+
timeoutStore.delete(key);
|
|
9031
|
+
}, delay);
|
|
9032
|
+
timeoutStore.set(key, timeoutId);
|
|
9033
|
+
}
|
|
9034
|
+
var TextFilter = (props) => {
|
|
9035
|
+
const currentOperator = props.filterOperators.value[props.field] || OPERATORS.CONTAINS;
|
|
9036
|
+
const isArrayOperator = currentOperator === OPERATORS.IS_ONE_OF || currentOperator === OPERATORS.IS_NOT_ONE_OF;
|
|
9037
|
+
const isDefaultOperator = currentOperator === OPERATORS.CONTAINS;
|
|
9038
|
+
const showOperatorSelect = !isDefaultOperator;
|
|
9039
|
+
const filterInputValue = props.filterInputValues.value[props.field];
|
|
9040
|
+
const displayValue = filterInputValue !== void 0 && filterInputValue !== "" ? filterInputValue : props.value || "";
|
|
9041
|
+
const handleFilterChange = (value, operator = currentOperator) => {
|
|
9042
|
+
const filter = {
|
|
9043
|
+
operator,
|
|
9044
|
+
// Strings are always case-insensitive (contains is default)
|
|
9045
|
+
caseSensitive: false
|
|
9046
|
+
};
|
|
9047
|
+
if (isArrayOperator) {
|
|
9048
|
+
if (typeof value === "string" && value.trim()) {
|
|
9049
|
+
filter.values = value.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
9050
|
+
} else if (Array.isArray(value)) {
|
|
9051
|
+
filter.values = value;
|
|
9052
|
+
}
|
|
9053
|
+
} else {
|
|
9054
|
+
filter.value = value;
|
|
9055
|
+
}
|
|
9056
|
+
props.onFilterChange(props.field, filter);
|
|
9057
|
+
};
|
|
9058
|
+
return /* @__PURE__ */ jsxs("div", { class: "flex flex-col gap-1.5 md:gap-2 w-full", children: [
|
|
9059
|
+
/* @__PURE__ */ jsxs("div", { class: "flex gap-1.5 md:gap-2 w-full items-center", children: [
|
|
9060
|
+
showOperatorSelect && /* @__PURE__ */ jsxs(
|
|
9061
|
+
"select",
|
|
9062
|
+
{
|
|
9063
|
+
value: currentOperator,
|
|
9064
|
+
onChange: (e) => {
|
|
9065
|
+
const newOperator = e.target.value;
|
|
9066
|
+
props.filterOperators.value[props.field] = newOperator;
|
|
9067
|
+
const currentValue = props.filterInputValues.value[props.field];
|
|
9068
|
+
if (currentValue) {
|
|
9069
|
+
handleFilterChange(currentValue, newOperator);
|
|
9070
|
+
}
|
|
9071
|
+
},
|
|
9072
|
+
class: "select select-bordered select-sm w-20 md:w-24 flex-shrink-0 text-xs",
|
|
9073
|
+
"data-testid": `datatable-filter-${props.field}-operator`,
|
|
9074
|
+
children: [
|
|
9075
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.CONTAINS, children: "contains" }),
|
|
9076
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.EQUALS, children: "eq" }),
|
|
9077
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.NOT_EQUALS, children: "ne" }),
|
|
9078
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.STARTS_WITH, children: "sw" }),
|
|
9079
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.ENDS_WITH, children: "ew" }),
|
|
9080
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.IS_ONE_OF, children: "in" }),
|
|
9081
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.IS_NOT_ONE_OF, children: "notIn" })
|
|
9082
|
+
]
|
|
9083
|
+
}
|
|
9084
|
+
),
|
|
9085
|
+
/* @__PURE__ */ jsx(
|
|
9086
|
+
"input",
|
|
9087
|
+
{
|
|
9088
|
+
type: "text",
|
|
9089
|
+
value: displayValue,
|
|
9090
|
+
onInput: (e) => {
|
|
9091
|
+
const value = e.target.value;
|
|
9092
|
+
props.filterInputValues.value[props.field] = value;
|
|
9093
|
+
debounce(
|
|
9094
|
+
`filter_${props.field}`,
|
|
9095
|
+
() => {
|
|
9096
|
+
handleFilterChange(value);
|
|
9097
|
+
},
|
|
9098
|
+
300
|
|
9099
|
+
);
|
|
9100
|
+
},
|
|
9101
|
+
onKeydown: (e) => {
|
|
9102
|
+
if (e.key === "Escape" && props.filterInputValues.value[props.field]) {
|
|
9103
|
+
e.preventDefault();
|
|
9104
|
+
props.filterInputValues.value[props.field] = "";
|
|
9105
|
+
handleFilterChange("");
|
|
9106
|
+
}
|
|
9107
|
+
},
|
|
9108
|
+
placeholder: isArrayOperator ? `Filter by ${props.label} (comma-separated)` : `Filter by ${props.label}`,
|
|
9109
|
+
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
9110
|
+
"data-testid": `datatable-filter-${props.field}-input`
|
|
9111
|
+
}
|
|
9112
|
+
)
|
|
9113
|
+
] }),
|
|
9114
|
+
isArrayOperator && /* @__PURE__ */ jsx("div", { class: "text-xs text-base-content/60", children: 'Enter multiple values separated by commas (e.g., "value1, value2, value3")' })
|
|
9115
|
+
] });
|
|
9116
|
+
};
|
|
9117
|
+
var NumberFilter = (props) => {
|
|
9118
|
+
if (!props.filterOperators.value[props.field]) {
|
|
9119
|
+
props.filterOperators.value[props.field] = OPERATORS.EQUALS;
|
|
9120
|
+
}
|
|
9121
|
+
const currentOperator = props.filterOperators.value[props.field] || OPERATORS.EQUALS;
|
|
9122
|
+
const isBetween = currentOperator === OPERATORS.BETWEEN;
|
|
9123
|
+
const filterInputValue = props.filterInputValues.value[props.field];
|
|
9124
|
+
const displayValue = filterInputValue !== void 0 && filterInputValue !== "" ? filterInputValue : props.value !== void 0 && props.value !== null && props.value !== "" ? String(props.value) : "";
|
|
9125
|
+
const handleFilterChange = (value, operator = currentOperator) => {
|
|
9126
|
+
const filter = {
|
|
9127
|
+
operator
|
|
9128
|
+
};
|
|
9129
|
+
if (isBetween) {
|
|
9130
|
+
if (typeof value === "string" && value.includes(",")) {
|
|
9131
|
+
const parts = value.split(",").map((v) => parseFloat(v.trim())).filter((v) => !isNaN(v));
|
|
9132
|
+
if (parts.length === 2) {
|
|
9133
|
+
filter.values = [Math.min(parts[0], parts[1]), Math.max(parts[0], parts[1])];
|
|
9134
|
+
}
|
|
9135
|
+
} else if (Array.isArray(value) && value.length === 2) {
|
|
9136
|
+
filter.values = [Math.min(value[0], value[1]), Math.max(value[0], value[1])];
|
|
9137
|
+
}
|
|
9138
|
+
} else {
|
|
9139
|
+
const numValue = typeof value === "number" ? value : parseFloat(value);
|
|
9140
|
+
if (!isNaN(numValue)) {
|
|
9141
|
+
filter.value = numValue;
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
props.onFilterChange(props.field, filter);
|
|
9145
|
+
};
|
|
9146
|
+
return /* @__PURE__ */ jsxs("div", { class: "flex flex-col gap-1.5 md:gap-2 w-full", children: [
|
|
9147
|
+
/* @__PURE__ */ jsxs("div", { class: "flex gap-1.5 md:gap-2 w-full items-center", children: [
|
|
9148
|
+
/* @__PURE__ */ jsxs(
|
|
9149
|
+
"select",
|
|
9150
|
+
{
|
|
9151
|
+
value: currentOperator,
|
|
9152
|
+
onChange: (e) => {
|
|
9153
|
+
const newOperator = e.target.value;
|
|
9154
|
+
props.filterOperators.value[props.field] = newOperator;
|
|
9155
|
+
const currentValue = props.filterInputValues.value[props.field];
|
|
9156
|
+
if (currentValue) {
|
|
9157
|
+
handleFilterChange(currentValue, newOperator);
|
|
9158
|
+
}
|
|
9159
|
+
},
|
|
9160
|
+
class: "select select-bordered select-sm w-20 md:w-24 flex-shrink-0 text-xs font-medium",
|
|
9161
|
+
"data-testid": `datatable-filter-${props.field}-operator`,
|
|
9162
|
+
children: [
|
|
9163
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.EQUALS, children: "eq" }),
|
|
9164
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.NOT_EQUALS, children: "ne" }),
|
|
9165
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.GREATER_THAN, children: "gt" }),
|
|
9166
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.GREATER_THAN_OR_EQUAL, children: "gte" }),
|
|
9167
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.LESS_THAN, children: "lt" }),
|
|
9168
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.LESS_THAN_OR_EQUAL, children: "lte" }),
|
|
9169
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.BETWEEN, children: "between" })
|
|
9170
|
+
]
|
|
9171
|
+
}
|
|
9172
|
+
),
|
|
9173
|
+
!isBetween && /* @__PURE__ */ jsx(
|
|
9174
|
+
"input",
|
|
9175
|
+
{
|
|
9176
|
+
type: "number",
|
|
9177
|
+
value: displayValue,
|
|
9178
|
+
onInput: (e) => {
|
|
9179
|
+
const value = e.target.value;
|
|
9180
|
+
props.filterInputValues.value[props.field] = value;
|
|
9181
|
+
debounce(
|
|
9182
|
+
`filter_${props.field}`,
|
|
9183
|
+
() => {
|
|
9184
|
+
handleFilterChange(value);
|
|
9185
|
+
},
|
|
9186
|
+
300
|
|
9187
|
+
);
|
|
9188
|
+
},
|
|
9189
|
+
onKeydown: (e) => {
|
|
9190
|
+
if (e.key === "Escape" && props.filterInputValues.value[props.field]) {
|
|
9191
|
+
e.preventDefault();
|
|
9192
|
+
props.filterInputValues.value[props.field] = "";
|
|
9193
|
+
handleFilterChange("");
|
|
9194
|
+
}
|
|
9195
|
+
},
|
|
9196
|
+
placeholder: `Filter by ${props.label}`,
|
|
9197
|
+
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
9198
|
+
"data-testid": `datatable-filter-${props.field}-input`
|
|
9199
|
+
}
|
|
9200
|
+
)
|
|
9201
|
+
] }),
|
|
9202
|
+
isBetween && /* @__PURE__ */ jsxs("div", { class: "flex gap-2 items-center w-full", children: [
|
|
9203
|
+
/* @__PURE__ */ jsx(
|
|
9204
|
+
"input",
|
|
9205
|
+
{
|
|
9206
|
+
type: "number",
|
|
9207
|
+
placeholder: "Min",
|
|
9208
|
+
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
9209
|
+
"data-testid": `datatable-filter-${props.field}-input-min`,
|
|
9210
|
+
onInput: (e) => {
|
|
9211
|
+
const minValue = e.target.value;
|
|
9212
|
+
const maxValue = props.filterInputValues.value[`${props.field}_max`] || "";
|
|
9213
|
+
if (minValue && maxValue) {
|
|
9214
|
+
props.filterInputValues.value[props.field] = `${minValue},${maxValue}`;
|
|
9215
|
+
handleFilterChange(`${minValue},${maxValue}`);
|
|
9216
|
+
} else {
|
|
9217
|
+
props.filterInputValues.value[`${props.field}_min`] = minValue;
|
|
9218
|
+
}
|
|
9219
|
+
}
|
|
9220
|
+
}
|
|
9221
|
+
),
|
|
9222
|
+
/* @__PURE__ */ jsx("span", { class: "text-xs text-base-content/60", children: "to" }),
|
|
9223
|
+
/* @__PURE__ */ jsx(
|
|
9224
|
+
"input",
|
|
9225
|
+
{
|
|
9226
|
+
type: "number",
|
|
9227
|
+
placeholder: "Max",
|
|
9228
|
+
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
9229
|
+
"data-testid": `datatable-filter-${props.field}-input-max`,
|
|
9230
|
+
onInput: (e) => {
|
|
9231
|
+
const maxValue = e.target.value;
|
|
9232
|
+
const minValue = props.filterInputValues.value[`${props.field}_min`] || props.filterInputValues.value[props.field]?.split(",")[0] || "";
|
|
9233
|
+
if (minValue && maxValue) {
|
|
9234
|
+
props.filterInputValues.value[props.field] = `${minValue},${maxValue}`;
|
|
9235
|
+
handleFilterChange(`${minValue},${maxValue}`);
|
|
9236
|
+
} else {
|
|
9237
|
+
props.filterInputValues.value[`${props.field}_max`] = maxValue;
|
|
9238
|
+
}
|
|
9239
|
+
}
|
|
9240
|
+
}
|
|
9241
|
+
)
|
|
9242
|
+
] })
|
|
9243
|
+
] });
|
|
9244
|
+
};
|
|
9245
|
+
var BooleanFilter = (props) => {
|
|
9246
|
+
const currentOperator = props.operator || OPERATORS.EQUALS;
|
|
9247
|
+
const isDefaultOperator = currentOperator === OPERATORS.EQUALS;
|
|
9248
|
+
const showOperatorSelect = !isDefaultOperator;
|
|
9249
|
+
const trueOption = props.options?.find((opt) => opt.value === true) || { label: "Yes"};
|
|
9250
|
+
const falseOption = props.options?.find((opt) => opt.value === false) || { label: "No"};
|
|
9251
|
+
return /* @__PURE__ */ jsx("div", { class: "flex flex-col gap-1.5 md:gap-2 w-full", children: /* @__PURE__ */ jsxs("div", { class: "flex gap-1.5 md:gap-2 w-full items-center", children: [
|
|
9252
|
+
showOperatorSelect && /* @__PURE__ */ jsxs(
|
|
7749
9253
|
"select",
|
|
7750
9254
|
{
|
|
7751
9255
|
value: currentOperator,
|
|
7752
9256
|
onChange: (e) => {
|
|
7753
9257
|
const newOperator = e.target.value;
|
|
7754
|
-
props.
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
9258
|
+
const currentValue = props.value;
|
|
9259
|
+
if (currentValue !== "" && currentValue !== null && currentValue !== void 0) {
|
|
9260
|
+
const filter = {
|
|
9261
|
+
operator: newOperator,
|
|
9262
|
+
value: currentValue === true || currentValue === "true"
|
|
9263
|
+
};
|
|
9264
|
+
props.onFilterChange(props.field, filter);
|
|
7758
9265
|
}
|
|
7759
9266
|
},
|
|
7760
|
-
class: "select select-bordered select-sm w-
|
|
9267
|
+
class: "select select-bordered select-sm w-20 md:w-24 flex-shrink-0 text-xs font-medium",
|
|
7761
9268
|
"data-testid": `datatable-filter-${props.field}-operator`,
|
|
7762
9269
|
children: [
|
|
7763
|
-
/* @__PURE__ */ jsx("option", { value:
|
|
7764
|
-
/* @__PURE__ */ jsx("option", { value:
|
|
9270
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.EQUALS, children: "eq" }),
|
|
9271
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.NOT_EQUALS, children: "ne" })
|
|
7765
9272
|
]
|
|
7766
9273
|
}
|
|
7767
9274
|
),
|
|
7768
|
-
/* @__PURE__ */ jsx(
|
|
7769
|
-
"input",
|
|
7770
|
-
{
|
|
7771
|
-
type: "text",
|
|
7772
|
-
value: props.filterInputValues.value[props.field] || "",
|
|
7773
|
-
onInput: (e) => {
|
|
7774
|
-
const value = e.target.value;
|
|
7775
|
-
props.filterInputValues.value[props.field] = value;
|
|
7776
|
-
debounce(
|
|
7777
|
-
`filter_${props.field}`,
|
|
7778
|
-
() => {
|
|
7779
|
-
props.onFilterChange(props.field, value, currentOperator);
|
|
7780
|
-
},
|
|
7781
|
-
300
|
|
7782
|
-
);
|
|
7783
|
-
},
|
|
7784
|
-
onKeydown: (e) => {
|
|
7785
|
-
if (e.key === "Escape" && props.filterInputValues.value[props.field]) {
|
|
7786
|
-
e.preventDefault();
|
|
7787
|
-
props.filterInputValues.value[props.field] = "";
|
|
7788
|
-
props.onFilterChange(props.field, "", currentOperator);
|
|
7789
|
-
}
|
|
7790
|
-
},
|
|
7791
|
-
placeholder: `Filter by ${props.label}`,
|
|
7792
|
-
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
7793
|
-
"data-testid": `datatable-filter-${props.field}-input`
|
|
7794
|
-
}
|
|
7795
|
-
)
|
|
7796
|
-
] });
|
|
7797
|
-
};
|
|
7798
|
-
var NumberFilter = (props) => {
|
|
7799
|
-
const currentOperator = props.filterOperators.value[props.field] || "eq";
|
|
7800
|
-
return /* @__PURE__ */ jsxs("div", { class: "flex gap-1.5 md:gap-2 w-full items-center", children: [
|
|
7801
9275
|
/* @__PURE__ */ jsxs(
|
|
7802
9276
|
"select",
|
|
7803
9277
|
{
|
|
7804
|
-
value:
|
|
9278
|
+
value: props.value === true || props.value === "true" ? "true" : props.value === false || props.value === "false" ? "false" : "",
|
|
7805
9279
|
onChange: (e) => {
|
|
7806
|
-
const
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
9280
|
+
const stringValue = e.target.value;
|
|
9281
|
+
if (stringValue === "") {
|
|
9282
|
+
props.onFilterChange(props.field, { operator: currentOperator, value: "" });
|
|
9283
|
+
} else {
|
|
9284
|
+
const boolValue = stringValue === "true";
|
|
9285
|
+
const filter = {
|
|
9286
|
+
operator: currentOperator,
|
|
9287
|
+
value: boolValue
|
|
9288
|
+
};
|
|
9289
|
+
props.onFilterChange(props.field, filter);
|
|
7811
9290
|
}
|
|
7812
9291
|
},
|
|
7813
|
-
class: "select select-bordered select-sm
|
|
7814
|
-
|
|
9292
|
+
class: "select select-bordered select-sm flex-1 min-w-0",
|
|
9293
|
+
disabled: props.isLoading,
|
|
9294
|
+
"data-testid": `datatable-filter-${props.field}-input`,
|
|
7815
9295
|
children: [
|
|
7816
|
-
/* @__PURE__ */ jsx("option", { value: "
|
|
7817
|
-
/* @__PURE__ */ jsx("option", { value: "
|
|
7818
|
-
/* @__PURE__ */ jsx("option", { value: "
|
|
9296
|
+
props.showAllOption !== false && /* @__PURE__ */ jsx("option", { value: "", children: props.allOptionText ?? "All" }),
|
|
9297
|
+
/* @__PURE__ */ jsx("option", { value: "true", children: trueOption.label }),
|
|
9298
|
+
/* @__PURE__ */ jsx("option", { value: "false", children: falseOption.label })
|
|
7819
9299
|
]
|
|
7820
9300
|
}
|
|
7821
|
-
),
|
|
7822
|
-
/* @__PURE__ */ jsx(
|
|
7823
|
-
"input",
|
|
7824
|
-
{
|
|
7825
|
-
type: "number",
|
|
7826
|
-
value: props.filterInputValues.value[props.field] || "",
|
|
7827
|
-
onInput: (e) => {
|
|
7828
|
-
const value = e.target.value;
|
|
7829
|
-
props.filterInputValues.value[props.field] = value;
|
|
7830
|
-
debounce(
|
|
7831
|
-
`filter_${props.field}`,
|
|
7832
|
-
() => {
|
|
7833
|
-
props.onFilterChange(props.field, value, currentOperator);
|
|
7834
|
-
},
|
|
7835
|
-
300
|
|
7836
|
-
);
|
|
7837
|
-
},
|
|
7838
|
-
onKeydown: (e) => {
|
|
7839
|
-
if (e.key === "Escape" && props.filterInputValues.value[props.field]) {
|
|
7840
|
-
e.preventDefault();
|
|
7841
|
-
props.filterInputValues.value[props.field] = "";
|
|
7842
|
-
props.onFilterChange(props.field, "", currentOperator);
|
|
7843
|
-
}
|
|
7844
|
-
},
|
|
7845
|
-
placeholder: `Filter by ${props.label}`,
|
|
7846
|
-
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
7847
|
-
"data-testid": `datatable-filter-${props.field}-input`
|
|
7848
|
-
}
|
|
7849
9301
|
)
|
|
7850
|
-
] });
|
|
7851
|
-
};
|
|
7852
|
-
var BooleanFilter = (props) => {
|
|
7853
|
-
return /* @__PURE__ */ jsxs(
|
|
7854
|
-
"select",
|
|
7855
|
-
{
|
|
7856
|
-
value: props.value || "",
|
|
7857
|
-
onChange: (e) => {
|
|
7858
|
-
const stringValue = e.target.value;
|
|
7859
|
-
const value = stringValue === "true" ? true : stringValue === "false" ? false : stringValue;
|
|
7860
|
-
props.onFilterChange(props.field, value, "eq");
|
|
7861
|
-
},
|
|
7862
|
-
class: "select select-bordered select-sm w-full",
|
|
7863
|
-
disabled: props.isLoading,
|
|
7864
|
-
"data-testid": `datatable-filter-${props.field}-input`,
|
|
7865
|
-
children: [
|
|
7866
|
-
props.showAllOption !== false && /* @__PURE__ */ jsx("option", { value: "", children: props.allOptionText ?? "All" }),
|
|
7867
|
-
/* @__PURE__ */ jsx("option", { value: "true", children: "Yes" }),
|
|
7868
|
-
/* @__PURE__ */ jsx("option", { value: "false", children: "No" })
|
|
7869
|
-
]
|
|
7870
|
-
}
|
|
7871
|
-
);
|
|
9302
|
+
] }) });
|
|
7872
9303
|
};
|
|
7873
9304
|
var comboboxState = reactive({});
|
|
7874
|
-
var
|
|
7875
|
-
var
|
|
9305
|
+
var DROPDOWN_MAX_HEIGHT2 = 240;
|
|
9306
|
+
var MIN_SPACE_BELOW2 = 100;
|
|
7876
9307
|
var ComboboxFilter = (props) => {
|
|
7877
9308
|
if (!comboboxState[props.field]) {
|
|
7878
9309
|
comboboxState[props.field] = {
|
|
@@ -7888,7 +9319,7 @@ var ComboboxFilter = (props) => {
|
|
|
7888
9319
|
const rect = input.getBoundingClientRect();
|
|
7889
9320
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
7890
9321
|
const spaceAbove = rect.top;
|
|
7891
|
-
const openUpward = spaceBelow <
|
|
9322
|
+
const openUpward = spaceBelow < MIN_SPACE_BELOW2 && spaceAbove > spaceBelow;
|
|
7892
9323
|
state.position = {
|
|
7893
9324
|
top: openUpward ? rect.top - 4 : rect.bottom + 4,
|
|
7894
9325
|
// 4px gap
|
|
@@ -7953,14 +9384,26 @@ var ComboboxFilter = (props) => {
|
|
|
7953
9384
|
};
|
|
7954
9385
|
const handleSelectOption = (option) => {
|
|
7955
9386
|
props.filterInputValues.value[props.field] = option.label;
|
|
7956
|
-
props.
|
|
9387
|
+
const operator = props.filterOperators?.value[props.field] || (props.fieldType === "string" ? DEFAULT_OPERATORS.string : OPERATORS.EQUALS);
|
|
9388
|
+
const filter = {
|
|
9389
|
+
operator,
|
|
9390
|
+
value: option.value,
|
|
9391
|
+
caseSensitive: props.caseSensitive?.value[props.field] || false
|
|
9392
|
+
};
|
|
9393
|
+
props.onFilterChange(props.field, filter);
|
|
7957
9394
|
state.isOpen = false;
|
|
7958
9395
|
state.selectedIndex = -1;
|
|
7959
9396
|
};
|
|
7960
9397
|
const handleSelectNewValue = () => {
|
|
7961
9398
|
const newValue = searchQuery.value.trim();
|
|
7962
9399
|
if (newValue) {
|
|
7963
|
-
props.
|
|
9400
|
+
const operator = props.filterOperators?.value[props.field] || (props.fieldType === "string" ? DEFAULT_OPERATORS.string : OPERATORS.EQUALS);
|
|
9401
|
+
const filter = {
|
|
9402
|
+
operator,
|
|
9403
|
+
value: newValue,
|
|
9404
|
+
caseSensitive: props.caseSensitive?.value[props.field] || false
|
|
9405
|
+
};
|
|
9406
|
+
props.onFilterChange(props.field, filter);
|
|
7964
9407
|
state.isOpen = false;
|
|
7965
9408
|
state.selectedIndex = -1;
|
|
7966
9409
|
}
|
|
@@ -7993,7 +9436,8 @@ var ComboboxFilter = (props) => {
|
|
|
7993
9436
|
state.isOpen = false;
|
|
7994
9437
|
state.selectedIndex = -1;
|
|
7995
9438
|
if (props.value) {
|
|
7996
|
-
props.
|
|
9439
|
+
const operator = props.filterOperators?.value[props.field] || (props.fieldType === "string" ? DEFAULT_OPERATORS.string : OPERATORS.EQUALS);
|
|
9440
|
+
props.onFilterChange(props.field, { operator, value: "" });
|
|
7997
9441
|
props.filterInputValues.value[props.field] = "";
|
|
7998
9442
|
}
|
|
7999
9443
|
return;
|
|
@@ -8103,7 +9547,8 @@ var ComboboxFilter = (props) => {
|
|
|
8103
9547
|
const currentLabel = selectedOption.value;
|
|
8104
9548
|
if (currentQuery === "") {
|
|
8105
9549
|
if (currentValue) {
|
|
8106
|
-
props.
|
|
9550
|
+
const operator = props.filterOperators?.value[props.field] || (props.fieldType === "string" ? DEFAULT_OPERATORS.string : OPERATORS.EQUALS);
|
|
9551
|
+
props.onFilterChange(props.field, { operator, value: "" });
|
|
8107
9552
|
props.filterInputValues.value[props.field] = "";
|
|
8108
9553
|
}
|
|
8109
9554
|
} else {
|
|
@@ -8119,9 +9564,23 @@ var ComboboxFilter = (props) => {
|
|
|
8119
9564
|
if (!queryMatchesCurrentValue) {
|
|
8120
9565
|
const matchingOption = props.options.find((opt) => opt.label.toLowerCase() === currentQuery.toLowerCase());
|
|
8121
9566
|
if (matchingOption) {
|
|
8122
|
-
props.
|
|
9567
|
+
const operator = props.filterOperators?.value[props.field] || (props.fieldType === "string" ? DEFAULT_OPERATORS.string : OPERATORS.EQUALS);
|
|
9568
|
+
const filter = {
|
|
9569
|
+
operator,
|
|
9570
|
+
value: matchingOption.value,
|
|
9571
|
+
// Strings are always case-insensitive (contains is default)
|
|
9572
|
+
caseSensitive: false
|
|
9573
|
+
};
|
|
9574
|
+
props.onFilterChange(props.field, filter);
|
|
8123
9575
|
} else if (props.allowCreate) {
|
|
8124
|
-
props.
|
|
9576
|
+
const operator = props.filterOperators?.value[props.field] || (props.fieldType === "string" ? DEFAULT_OPERATORS.string : OPERATORS.EQUALS);
|
|
9577
|
+
const filter = {
|
|
9578
|
+
operator,
|
|
9579
|
+
value: currentQuery,
|
|
9580
|
+
// Strings are always case-insensitive (contains is default)
|
|
9581
|
+
caseSensitive: false
|
|
9582
|
+
};
|
|
9583
|
+
props.onFilterChange(props.field, filter);
|
|
8125
9584
|
} else {
|
|
8126
9585
|
if (currentLabel) {
|
|
8127
9586
|
props.filterInputValues.value[props.field] = currentLabel;
|
|
@@ -8142,7 +9601,7 @@ var ComboboxFilter = (props) => {
|
|
|
8142
9601
|
bottom: pos.openUpward ? `${window.innerHeight - pos.top}px` : "auto",
|
|
8143
9602
|
left: `${pos.left}px`,
|
|
8144
9603
|
width: `${pos.width}px`,
|
|
8145
|
-
maxHeight: `${
|
|
9604
|
+
maxHeight: `${DROPDOWN_MAX_HEIGHT2}px`,
|
|
8146
9605
|
zIndex: 9999
|
|
8147
9606
|
};
|
|
8148
9607
|
});
|
|
@@ -8290,6 +9749,296 @@ var ComboboxFilter = (props) => {
|
|
|
8290
9749
|
) })
|
|
8291
9750
|
] });
|
|
8292
9751
|
};
|
|
9752
|
+
function formatDateForInput(dateValue) {
|
|
9753
|
+
if (!dateValue) return "";
|
|
9754
|
+
try {
|
|
9755
|
+
const date = typeof dateValue === "string" ? new Date(dateValue) : dateValue;
|
|
9756
|
+
if (isNaN(date.getTime())) return "";
|
|
9757
|
+
const year = date.getFullYear();
|
|
9758
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
9759
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
9760
|
+
return `${year}-${month}-${day}`;
|
|
9761
|
+
} catch {
|
|
9762
|
+
return "";
|
|
9763
|
+
}
|
|
9764
|
+
}
|
|
9765
|
+
function parseDateValue(value) {
|
|
9766
|
+
if (!value) return "";
|
|
9767
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return value;
|
|
9768
|
+
return formatDateForInput(value);
|
|
9769
|
+
}
|
|
9770
|
+
var DateFilter = (props) => {
|
|
9771
|
+
if (!props.filterOperators.value[props.field]) {
|
|
9772
|
+
props.filterOperators.value[props.field] = OPERATORS.EQUALS;
|
|
9773
|
+
}
|
|
9774
|
+
const currentOperator = props.filterOperators.value[props.field] || OPERATORS.EQUALS;
|
|
9775
|
+
const isBetween = currentOperator === OPERATORS.BETWEEN;
|
|
9776
|
+
const isNullCheck = currentOperator === OPERATORS.IS_EMPTY || currentOperator === OPERATORS.IS_NOT_EMPTY;
|
|
9777
|
+
const currentFilterValue = props.value?.value;
|
|
9778
|
+
const currentFilterValues = props.value?.values;
|
|
9779
|
+
if (props.value?.operator && props.value.operator !== currentOperator) {
|
|
9780
|
+
props.filterOperators.value[props.field] = props.value.operator;
|
|
9781
|
+
}
|
|
9782
|
+
const singleDateValue = parseDateValue(
|
|
9783
|
+
props.filterInputValues.value[props.field] || (currentFilterValue ? String(currentFilterValue) : "")
|
|
9784
|
+
);
|
|
9785
|
+
let startDateValue = "";
|
|
9786
|
+
let endDateValue = "";
|
|
9787
|
+
if (isBetween) {
|
|
9788
|
+
if (currentFilterValues && Array.isArray(currentFilterValues) && currentFilterValues.length === 2) {
|
|
9789
|
+
startDateValue = parseDateValue(String(currentFilterValues[0]));
|
|
9790
|
+
endDateValue = parseDateValue(String(currentFilterValues[1]));
|
|
9791
|
+
if (startDateValue && endDateValue) {
|
|
9792
|
+
props.filterInputValues.value[`${props.field}_start`] = startDateValue;
|
|
9793
|
+
props.filterInputValues.value[`${props.field}_end`] = endDateValue;
|
|
9794
|
+
}
|
|
9795
|
+
} else {
|
|
9796
|
+
const commaValue = props.filterInputValues.value[props.field];
|
|
9797
|
+
if (commaValue && typeof commaValue === "string" && commaValue.includes(",")) {
|
|
9798
|
+
const parts = commaValue.split(",").map((v) => v.trim());
|
|
9799
|
+
if (parts.length === 2) {
|
|
9800
|
+
startDateValue = parseDateValue(parts[0]);
|
|
9801
|
+
endDateValue = parseDateValue(parts[1]);
|
|
9802
|
+
props.filterInputValues.value[`${props.field}_start`] = startDateValue;
|
|
9803
|
+
props.filterInputValues.value[`${props.field}_end`] = endDateValue;
|
|
9804
|
+
}
|
|
9805
|
+
} else {
|
|
9806
|
+
startDateValue = parseDateValue(props.filterInputValues.value[`${props.field}_start`]);
|
|
9807
|
+
endDateValue = parseDateValue(props.filterInputValues.value[`${props.field}_end`]);
|
|
9808
|
+
}
|
|
9809
|
+
}
|
|
9810
|
+
}
|
|
9811
|
+
const handleFilterChange = (value, operator = currentOperator) => {
|
|
9812
|
+
const isNullCheckOp = operator === OPERATORS.IS_EMPTY || operator === OPERATORS.IS_NOT_EMPTY;
|
|
9813
|
+
if (!value && !isNullCheckOp) {
|
|
9814
|
+
if (props.onClearFilter) {
|
|
9815
|
+
props.onClearFilter(props.field);
|
|
9816
|
+
}
|
|
9817
|
+
return;
|
|
9818
|
+
}
|
|
9819
|
+
const filter = {
|
|
9820
|
+
operator
|
|
9821
|
+
};
|
|
9822
|
+
if (isNullCheckOp) ; else if (operator === OPERATORS.BETWEEN) {
|
|
9823
|
+
if (typeof value === "string" && value.includes(",")) {
|
|
9824
|
+
const parts = value.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
9825
|
+
if (parts.length === 2) {
|
|
9826
|
+
const date1 = new Date(parts[0]);
|
|
9827
|
+
const date2 = new Date(parts[1]);
|
|
9828
|
+
if (!isNaN(date1.getTime()) && !isNaN(date2.getTime())) {
|
|
9829
|
+
filter.values = date1 <= date2 ? [parts[0], parts[1]] : [parts[1], parts[0]];
|
|
9830
|
+
} else {
|
|
9831
|
+
if (props.onClearFilter) {
|
|
9832
|
+
props.onClearFilter(props.field);
|
|
9833
|
+
}
|
|
9834
|
+
return;
|
|
9835
|
+
}
|
|
9836
|
+
} else {
|
|
9837
|
+
if (props.onClearFilter) {
|
|
9838
|
+
props.onClearFilter(props.field);
|
|
9839
|
+
}
|
|
9840
|
+
return;
|
|
9841
|
+
}
|
|
9842
|
+
} else if (Array.isArray(value) && value.length === 2) {
|
|
9843
|
+
const date1 = new Date(value[0]);
|
|
9844
|
+
const date2 = new Date(value[1]);
|
|
9845
|
+
if (!isNaN(date1.getTime()) && !isNaN(date2.getTime())) {
|
|
9846
|
+
filter.values = date1 <= date2 ? value : [value[1], value[0]];
|
|
9847
|
+
} else {
|
|
9848
|
+
if (props.onClearFilter) {
|
|
9849
|
+
props.onClearFilter(props.field);
|
|
9850
|
+
}
|
|
9851
|
+
return;
|
|
9852
|
+
}
|
|
9853
|
+
} else {
|
|
9854
|
+
if (props.onClearFilter) {
|
|
9855
|
+
props.onClearFilter(props.field);
|
|
9856
|
+
}
|
|
9857
|
+
return;
|
|
9858
|
+
}
|
|
9859
|
+
} else {
|
|
9860
|
+
if (value) {
|
|
9861
|
+
if (typeof value === "string") {
|
|
9862
|
+
const date = new Date(value);
|
|
9863
|
+
if (!isNaN(date.getTime())) {
|
|
9864
|
+
filter.value = value;
|
|
9865
|
+
} else {
|
|
9866
|
+
if (props.onClearFilter) {
|
|
9867
|
+
props.onClearFilter(props.field);
|
|
9868
|
+
}
|
|
9869
|
+
return;
|
|
9870
|
+
}
|
|
9871
|
+
} else if (value instanceof Date) {
|
|
9872
|
+
filter.value = value.toISOString();
|
|
9873
|
+
} else {
|
|
9874
|
+
if (props.onClearFilter) {
|
|
9875
|
+
props.onClearFilter(props.field);
|
|
9876
|
+
}
|
|
9877
|
+
return;
|
|
9878
|
+
}
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
props.onFilterChange(props.field, filter);
|
|
9882
|
+
};
|
|
9883
|
+
const handleNullCheckToggle = (operator) => {
|
|
9884
|
+
props.filterOperators.value[props.field] = operator;
|
|
9885
|
+
handleFilterChange("", operator);
|
|
9886
|
+
};
|
|
9887
|
+
return /* @__PURE__ */ jsxs("div", { class: "flex flex-col gap-1.5 md:gap-2 w-full", children: [
|
|
9888
|
+
/* @__PURE__ */ jsxs("div", { class: "flex gap-1.5 md:gap-2 w-full items-center", children: [
|
|
9889
|
+
/* @__PURE__ */ jsxs(
|
|
9890
|
+
"select",
|
|
9891
|
+
{
|
|
9892
|
+
value: currentOperator,
|
|
9893
|
+
onChange: (e) => {
|
|
9894
|
+
const newOperator = e.target.value;
|
|
9895
|
+
props.filterOperators.value[props.field] = newOperator;
|
|
9896
|
+
const currentValue = props.filterInputValues.value[props.field];
|
|
9897
|
+
const isNewNullCheck = newOperator === OPERATORS.IS_EMPTY || newOperator === OPERATORS.IS_NOT_EMPTY;
|
|
9898
|
+
if (currentValue || isNewNullCheck) {
|
|
9899
|
+
handleFilterChange(currentValue, newOperator);
|
|
9900
|
+
} else if (!currentValue && props.onClearFilter) {
|
|
9901
|
+
props.onClearFilter(props.field);
|
|
9902
|
+
}
|
|
9903
|
+
},
|
|
9904
|
+
class: "select select-bordered select-sm w-20 md:w-24 flex-shrink-0 text-xs font-medium",
|
|
9905
|
+
"data-testid": `datatable-filter-${props.field}-operator`,
|
|
9906
|
+
children: [
|
|
9907
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.EQUALS, children: "eq" }),
|
|
9908
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.NOT_EQUALS, children: "ne" }),
|
|
9909
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.GREATER_THAN, children: "gt" }),
|
|
9910
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.GREATER_THAN_OR_EQUAL, children: "gte" }),
|
|
9911
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.LESS_THAN, children: "lt" }),
|
|
9912
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.LESS_THAN_OR_EQUAL, children: "lte" }),
|
|
9913
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.BETWEEN, children: "between" }),
|
|
9914
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.IS_EMPTY, children: "isEmpty" }),
|
|
9915
|
+
/* @__PURE__ */ jsx("option", { value: OPERATORS.IS_NOT_EMPTY, children: "isNotEmpty" })
|
|
9916
|
+
]
|
|
9917
|
+
}
|
|
9918
|
+
),
|
|
9919
|
+
!isBetween && /* @__PURE__ */ jsx(Fragment, { children: isNullCheck ? /* @__PURE__ */ jsxs("div", { class: "flex gap-2 items-center flex-1", children: [
|
|
9920
|
+
/* @__PURE__ */ jsx(
|
|
9921
|
+
"button",
|
|
9922
|
+
{
|
|
9923
|
+
onClick: () => handleNullCheckToggle(OPERATORS.IS_EMPTY),
|
|
9924
|
+
class: `btn btn-sm ${currentOperator === OPERATORS.IS_EMPTY ? "btn-primary" : "btn-outline"}`,
|
|
9925
|
+
"data-testid": `datatable-filter-${props.field}-empty`,
|
|
9926
|
+
children: "Empty"
|
|
9927
|
+
}
|
|
9928
|
+
),
|
|
9929
|
+
/* @__PURE__ */ jsx(
|
|
9930
|
+
"button",
|
|
9931
|
+
{
|
|
9932
|
+
onClick: () => handleNullCheckToggle(OPERATORS.IS_NOT_EMPTY),
|
|
9933
|
+
class: `btn btn-sm ${currentOperator === OPERATORS.IS_NOT_EMPTY ? "btn-primary" : "btn-outline"}`,
|
|
9934
|
+
"data-testid": `datatable-filter-${props.field}-not-empty`,
|
|
9935
|
+
children: "Not Empty"
|
|
9936
|
+
}
|
|
9937
|
+
)
|
|
9938
|
+
] }) : /* @__PURE__ */ jsx(
|
|
9939
|
+
"input",
|
|
9940
|
+
{
|
|
9941
|
+
type: "date",
|
|
9942
|
+
value: singleDateValue,
|
|
9943
|
+
onInput: (e) => {
|
|
9944
|
+
const value = e.target.value;
|
|
9945
|
+
props.filterInputValues.value[props.field] = value;
|
|
9946
|
+
if (!value) {
|
|
9947
|
+
if (props.onClearFilter) {
|
|
9948
|
+
props.onClearFilter(props.field);
|
|
9949
|
+
}
|
|
9950
|
+
return;
|
|
9951
|
+
}
|
|
9952
|
+
debounce(
|
|
9953
|
+
`filter_${props.field}`,
|
|
9954
|
+
() => {
|
|
9955
|
+
handleFilterChange(value);
|
|
9956
|
+
},
|
|
9957
|
+
300
|
|
9958
|
+
);
|
|
9959
|
+
},
|
|
9960
|
+
onKeydown: (e) => {
|
|
9961
|
+
if (e.key === "Escape" && singleDateValue) {
|
|
9962
|
+
e.preventDefault();
|
|
9963
|
+
props.filterInputValues.value[props.field] = "";
|
|
9964
|
+
if (props.onClearFilter) {
|
|
9965
|
+
props.onClearFilter(props.field);
|
|
9966
|
+
}
|
|
9967
|
+
}
|
|
9968
|
+
},
|
|
9969
|
+
placeholder: `Filter by ${props.label}`,
|
|
9970
|
+
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
9971
|
+
"data-testid": `datatable-filter-${props.field}-input`
|
|
9972
|
+
}
|
|
9973
|
+
) })
|
|
9974
|
+
] }),
|
|
9975
|
+
isBetween && /* @__PURE__ */ jsxs("div", { class: "flex gap-2 items-center w-full", children: [
|
|
9976
|
+
/* @__PURE__ */ jsx(
|
|
9977
|
+
"input",
|
|
9978
|
+
{
|
|
9979
|
+
type: "date",
|
|
9980
|
+
value: startDateValue,
|
|
9981
|
+
placeholder: "Start date",
|
|
9982
|
+
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
9983
|
+
"data-testid": `datatable-filter-${props.field}-input-start`,
|
|
9984
|
+
onInput: (e) => {
|
|
9985
|
+
const startValue = e.target.value;
|
|
9986
|
+
props.filterInputValues.value[`${props.field}_start`] = startValue;
|
|
9987
|
+
const endValue = props.filterInputValues.value[`${props.field}_end`] || endDateValue;
|
|
9988
|
+
if (startValue && endValue) {
|
|
9989
|
+
props.filterInputValues.value[props.field] = `${startValue},${endValue}`;
|
|
9990
|
+
handleFilterChange(`${startValue},${endValue}`);
|
|
9991
|
+
} else if (!startValue && !endValue) {
|
|
9992
|
+
if (props.onClearFilter) {
|
|
9993
|
+
props.onClearFilter(props.field);
|
|
9994
|
+
}
|
|
9995
|
+
}
|
|
9996
|
+
}
|
|
9997
|
+
}
|
|
9998
|
+
),
|
|
9999
|
+
/* @__PURE__ */ jsx("span", { class: "text-xs text-base-content/60", children: "to" }),
|
|
10000
|
+
/* @__PURE__ */ jsx(
|
|
10001
|
+
"input",
|
|
10002
|
+
{
|
|
10003
|
+
type: "date",
|
|
10004
|
+
value: endDateValue,
|
|
10005
|
+
placeholder: "End date",
|
|
10006
|
+
class: "input input-bordered input-sm flex-1 min-w-0",
|
|
10007
|
+
"data-testid": `datatable-filter-${props.field}-input-end`,
|
|
10008
|
+
onInput: (e) => {
|
|
10009
|
+
const endValue = e.target.value;
|
|
10010
|
+
props.filterInputValues.value[`${props.field}_end`] = endValue;
|
|
10011
|
+
const startValue = props.filterInputValues.value[`${props.field}_start`] || startDateValue;
|
|
10012
|
+
if (startValue && endValue) {
|
|
10013
|
+
props.filterInputValues.value[props.field] = `${startValue},${endValue}`;
|
|
10014
|
+
handleFilterChange(`${startValue},${endValue}`);
|
|
10015
|
+
} else if (!startValue && !endValue) {
|
|
10016
|
+
if (props.onClearFilter) {
|
|
10017
|
+
props.onClearFilter(props.field);
|
|
10018
|
+
}
|
|
10019
|
+
}
|
|
10020
|
+
}
|
|
10021
|
+
}
|
|
10022
|
+
)
|
|
10023
|
+
] })
|
|
10024
|
+
] });
|
|
10025
|
+
};
|
|
10026
|
+
function convertToDataType(metadataType) {
|
|
10027
|
+
switch (metadataType) {
|
|
10028
|
+
case "string":
|
|
10029
|
+
case "number":
|
|
10030
|
+
case "boolean":
|
|
10031
|
+
case "date":
|
|
10032
|
+
case "enum":
|
|
10033
|
+
return metadataType;
|
|
10034
|
+
case "array":
|
|
10035
|
+
case "object":
|
|
10036
|
+
case "union":
|
|
10037
|
+
return "string";
|
|
10038
|
+
default:
|
|
10039
|
+
return "string";
|
|
10040
|
+
}
|
|
10041
|
+
}
|
|
8293
10042
|
var FilterDrawer = (props) => {
|
|
8294
10043
|
const injectedFilterOptionsState = inject(
|
|
8295
10044
|
ZINIA_DATA_TABLE_FILTER_OPTIONS_STATE_KEY,
|
|
@@ -8391,7 +10140,7 @@ var FilterDrawer = (props) => {
|
|
|
8391
10140
|
SelectFilter,
|
|
8392
10141
|
{
|
|
8393
10142
|
field,
|
|
8394
|
-
value: currentValue,
|
|
10143
|
+
value: Array.isArray(currentFilter?.values) ? currentFilter.values : currentValue,
|
|
8395
10144
|
options: getFilterOptions(
|
|
8396
10145
|
field,
|
|
8397
10146
|
column,
|
|
@@ -8403,7 +10152,10 @@ var FilterDrawer = (props) => {
|
|
|
8403
10152
|
isOptionsLoading: filterOptionsLoading?.value?.[field] ?? false,
|
|
8404
10153
|
allOptionText: column.filterAllOptionText,
|
|
8405
10154
|
showAllOption: column.filterShowAllOption,
|
|
8406
|
-
|
|
10155
|
+
operator: currentFilter?.operator,
|
|
10156
|
+
fieldType: convertToDataType(props.fieldsMetadata[field]?.type || "enum"),
|
|
10157
|
+
onFilterChange: props.onFilterChange,
|
|
10158
|
+
onClearFilter: props.onClearFilter
|
|
8407
10159
|
}
|
|
8408
10160
|
),
|
|
8409
10161
|
filterType === "text" && /* @__PURE__ */ jsx(
|
|
@@ -8414,6 +10166,8 @@ var FilterDrawer = (props) => {
|
|
|
8414
10166
|
label: column.label,
|
|
8415
10167
|
filterInputValues: props.filterInputValues,
|
|
8416
10168
|
filterOperators: props.filterOperators,
|
|
10169
|
+
caseSensitive: props.caseSensitive || { value: {} },
|
|
10170
|
+
fieldType: convertToDataType(props.fieldsMetadata[field]?.type || "string"),
|
|
8417
10171
|
onFilterChange: props.onFilterChange
|
|
8418
10172
|
}
|
|
8419
10173
|
),
|
|
@@ -8425,6 +10179,7 @@ var FilterDrawer = (props) => {
|
|
|
8425
10179
|
label: column.label,
|
|
8426
10180
|
filterInputValues: props.filterInputValues,
|
|
8427
10181
|
filterOperators: props.filterOperators,
|
|
10182
|
+
fieldType: convertToDataType(props.fieldsMetadata[field]?.type || "number"),
|
|
8428
10183
|
onFilterChange: props.onFilterChange
|
|
8429
10184
|
}
|
|
8430
10185
|
),
|
|
@@ -8437,6 +10192,14 @@ var FilterDrawer = (props) => {
|
|
|
8437
10192
|
isLoading: props.isLoading,
|
|
8438
10193
|
allOptionText: column.filterAllOptionText,
|
|
8439
10194
|
showAllOption: column.filterShowAllOption,
|
|
10195
|
+
operator: currentFilter?.operator,
|
|
10196
|
+
fieldType: convertToDataType(props.fieldsMetadata[field]?.type || "boolean"),
|
|
10197
|
+
options: getFilterOptions(
|
|
10198
|
+
field,
|
|
10199
|
+
column,
|
|
10200
|
+
props.fieldsMetadata,
|
|
10201
|
+
filterOptionsState?.value || {}
|
|
10202
|
+
).map((opt) => ({ label: opt.label, value: opt.value })),
|
|
8440
10203
|
onFilterChange: props.onFilterChange
|
|
8441
10204
|
}
|
|
8442
10205
|
),
|
|
@@ -8456,8 +10219,24 @@ var FilterDrawer = (props) => {
|
|
|
8456
10219
|
isOptionsLoading: filterOptionsLoading?.value?.[field] ?? false,
|
|
8457
10220
|
allowCreate: column.filterAllowCreate ?? false,
|
|
8458
10221
|
filterInputValues: props.filterInputValues,
|
|
10222
|
+
filterOperators: props.filterOperators,
|
|
10223
|
+
caseSensitive: props.caseSensitive || { value: {} },
|
|
10224
|
+
fieldType: convertToDataType(props.fieldsMetadata[field]?.type || "string"),
|
|
8459
10225
|
onFilterChange: props.onFilterChange
|
|
8460
10226
|
}
|
|
10227
|
+
),
|
|
10228
|
+
filterType === "date" && /* @__PURE__ */ jsx(
|
|
10229
|
+
DateFilter,
|
|
10230
|
+
{
|
|
10231
|
+
field,
|
|
10232
|
+
value: currentFilter,
|
|
10233
|
+
label: column.label,
|
|
10234
|
+
filterInputValues: props.filterInputValues,
|
|
10235
|
+
filterOperators: props.filterOperators,
|
|
10236
|
+
fieldType: convertToDataType(props.fieldsMetadata[field]?.type || "date"),
|
|
10237
|
+
onFilterChange: props.onFilterChange,
|
|
10238
|
+
onClearFilter: props.onClearFilter
|
|
10239
|
+
}
|
|
8461
10240
|
)
|
|
8462
10241
|
]
|
|
8463
10242
|
},
|
|
@@ -8480,30 +10259,81 @@ var FilterDrawer = (props) => {
|
|
|
8480
10259
|
}
|
|
8481
10260
|
);
|
|
8482
10261
|
};
|
|
8483
|
-
function formatFilterValue(field,
|
|
10262
|
+
function formatFilterValue(field, filter, column, fieldsMetadata, filterOptionsState) {
|
|
10263
|
+
if (filter.operator === "isEmpty" || filter.operator === "isNotEmpty" || filter.operator === "isNull" || filter.operator === "isNotNull") {
|
|
10264
|
+
return filter.operator === "isEmpty" || filter.operator === "isNull" ? "Empty" : "Not Empty";
|
|
10265
|
+
}
|
|
10266
|
+
if (Array.isArray(filter.values) && filter.values.length > 0) {
|
|
10267
|
+
if (filter.operator === "between" && filter.values.length === 2) {
|
|
10268
|
+
return `${filter.values[0]} - ${filter.values[1]}`;
|
|
10269
|
+
}
|
|
10270
|
+
if (filter.operator === "in" || filter.operator === "notIn") {
|
|
10271
|
+
const options2 = getFilterOptions(field, column, fieldsMetadata, filterOptionsState);
|
|
10272
|
+
if (options2.length > 0) {
|
|
10273
|
+
const labels = filter.values.map((val) => {
|
|
10274
|
+
const option = options2.find((opt) => opt.value === val);
|
|
10275
|
+
return option ? option.label : String(val);
|
|
10276
|
+
});
|
|
10277
|
+
return labels.join(", ");
|
|
10278
|
+
}
|
|
10279
|
+
}
|
|
10280
|
+
return filter.values.join(", ");
|
|
10281
|
+
}
|
|
10282
|
+
const value = filter.value;
|
|
8484
10283
|
if (value === "" || value === null || value === void 0) return "";
|
|
10284
|
+
const options = getFilterOptions(field, column, fieldsMetadata, filterOptionsState);
|
|
8485
10285
|
if (typeof value === "boolean") {
|
|
10286
|
+
const option = options.find((opt) => opt.value === value);
|
|
10287
|
+
if (option) return option.label;
|
|
8486
10288
|
return value ? "Yes" : "No";
|
|
8487
10289
|
}
|
|
8488
|
-
const options = getFilterOptions(field, column, fieldsMetadata, filterOptionsState);
|
|
8489
10290
|
if (options.length > 0) {
|
|
8490
10291
|
const option = options.find((opt) => opt.value === value);
|
|
8491
10292
|
if (option) return option.label;
|
|
8492
10293
|
}
|
|
8493
|
-
if (value
|
|
8494
|
-
|
|
10294
|
+
if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}/.test(value)) {
|
|
10295
|
+
try {
|
|
10296
|
+
const date = new Date(value);
|
|
10297
|
+
if (!isNaN(date.getTime())) {
|
|
10298
|
+
return date.toLocaleDateString();
|
|
10299
|
+
}
|
|
10300
|
+
} catch {
|
|
10301
|
+
}
|
|
8495
10302
|
}
|
|
8496
10303
|
const strValue = String(value);
|
|
8497
10304
|
return strValue.length > 20 ? strValue.substring(0, 20) + "..." : strValue;
|
|
8498
10305
|
}
|
|
8499
10306
|
function getOperatorLabel(operator) {
|
|
8500
10307
|
const labels = {
|
|
8501
|
-
|
|
10308
|
+
// Shorthand operators (current standard)
|
|
10309
|
+
eq: "eq",
|
|
10310
|
+
ne: "ne",
|
|
8502
10311
|
contains: "contains",
|
|
8503
|
-
|
|
8504
|
-
|
|
10312
|
+
sw: "sw",
|
|
10313
|
+
ew: "ew",
|
|
10314
|
+
gt: "gt",
|
|
10315
|
+
gte: "gte",
|
|
10316
|
+
lt: "lt",
|
|
10317
|
+
lte: "lte",
|
|
10318
|
+
between: "between",
|
|
8505
10319
|
in: "in",
|
|
8506
|
-
|
|
10320
|
+
notIn: "notIn",
|
|
10321
|
+
isEmpty: "isEmpty",
|
|
10322
|
+
isNotEmpty: "isNotEmpty",
|
|
10323
|
+
// Legacy aliases (map to new names for backward compatibility)
|
|
10324
|
+
isNull: "isEmpty",
|
|
10325
|
+
isNotNull: "isNotEmpty",
|
|
10326
|
+
// Legacy full names (map to shorthand for consistency)
|
|
10327
|
+
equals: "eq",
|
|
10328
|
+
notEquals: "ne",
|
|
10329
|
+
startsWith: "sw",
|
|
10330
|
+
endsWith: "ew",
|
|
10331
|
+
greaterThan: "gt",
|
|
10332
|
+
greaterThanOrEqual: "gte",
|
|
10333
|
+
lessThan: "lt",
|
|
10334
|
+
lessThanOrEqual: "lte",
|
|
10335
|
+
isOneOf: "in",
|
|
10336
|
+
isNotOneOf: "notIn"
|
|
8507
10337
|
};
|
|
8508
10338
|
return labels[operator] || operator;
|
|
8509
10339
|
}
|
|
@@ -8511,12 +10341,18 @@ var FiltersRow = (props) => {
|
|
|
8511
10341
|
const filterableColumns = Object.entries(props.columns).filter(([_, column]) => column?.filterable);
|
|
8512
10342
|
const activeFilterBadges = filterableColumns.map(([field, column]) => {
|
|
8513
10343
|
const filter = props.activeFilters[field];
|
|
8514
|
-
if (!filter
|
|
10344
|
+
if (!filter) {
|
|
10345
|
+
return null;
|
|
10346
|
+
}
|
|
10347
|
+
const hasValue = filter.value !== void 0 && filter.value !== "" && filter.value !== null;
|
|
10348
|
+
const hasValues = Array.isArray(filter.values) && filter.values.length > 0;
|
|
10349
|
+
const isNullCheck = filter.operator === "isEmpty" || filter.operator === "isNotEmpty" || filter.operator === "isNull" || filter.operator === "isNotNull";
|
|
10350
|
+
if (!hasValue && !hasValues && !isNullCheck) {
|
|
8515
10351
|
return null;
|
|
8516
10352
|
}
|
|
8517
10353
|
const displayValue = formatFilterValue(
|
|
8518
10354
|
field,
|
|
8519
|
-
filter
|
|
10355
|
+
filter,
|
|
8520
10356
|
column,
|
|
8521
10357
|
props.fieldsMetadata,
|
|
8522
10358
|
props.filterOptionsState?.value
|
|
@@ -8537,7 +10373,17 @@ var FiltersRow = (props) => {
|
|
|
8537
10373
|
if (props.filterOperators.value[field]) {
|
|
8538
10374
|
delete props.filterOperators.value[field];
|
|
8539
10375
|
}
|
|
8540
|
-
props.
|
|
10376
|
+
if (props.onClearFilter) {
|
|
10377
|
+
props.onClearFilter(field);
|
|
10378
|
+
} else {
|
|
10379
|
+
const currentFilter = props.activeFilters[field];
|
|
10380
|
+
if (currentFilter) {
|
|
10381
|
+
props.onFilterChange(field, {
|
|
10382
|
+
operator: currentFilter.operator,
|
|
10383
|
+
value: ""
|
|
10384
|
+
});
|
|
10385
|
+
}
|
|
10386
|
+
}
|
|
8541
10387
|
};
|
|
8542
10388
|
return /* @__PURE__ */ jsxs("div", { class: "mb-4 space-y-2", children: [
|
|
8543
10389
|
/* @__PURE__ */ jsxs("div", { class: "flex items-center gap-2", children: [
|
|
@@ -8592,7 +10438,7 @@ var FiltersRow = (props) => {
|
|
|
8592
10438
|
badge.label,
|
|
8593
10439
|
":"
|
|
8594
10440
|
] }),
|
|
8595
|
-
|
|
10441
|
+
/* @__PURE__ */ jsx("span", { class: "opacity-70", children: badge.operatorLabel }),
|
|
8596
10442
|
/* @__PURE__ */ jsx("span", { children: badge.displayValue }),
|
|
8597
10443
|
/* @__PURE__ */ jsx(
|
|
8598
10444
|
"button",
|
|
@@ -9056,9 +10902,8 @@ function createDaisyUIDataTable() {
|
|
|
9056
10902
|
const actions = inject(ZINIA_DATA_TABLE_ACTIONS_KEY);
|
|
9057
10903
|
const searchInputValue = inject(ZINIA_DATA_TABLE_SEARCH_INPUT_KEY);
|
|
9058
10904
|
const filterInputValues = inject(ZINIA_DATA_TABLE_FILTER_INPUTS_KEY);
|
|
9059
|
-
const filterOperators = inject(
|
|
9060
|
-
|
|
9061
|
-
);
|
|
10905
|
+
const filterOperators = inject(ZINIA_DATA_TABLE_FILTER_OPERATORS_KEY);
|
|
10906
|
+
const filterCaseSensitive = inject(ZINIA_DATA_TABLE_FILTER_CASE_SENSITIVE_KEY, ref({}));
|
|
9062
10907
|
const filterOptionsState = inject(ZINIA_DATA_TABLE_FILTER_OPTIONS_STATE_KEY);
|
|
9063
10908
|
const filterOptionsLoading = inject(ZINIA_DATA_TABLE_FILTER_OPTIONS_LOADING_KEY);
|
|
9064
10909
|
const tableName = inject(ZINIA_DATA_TABLE_NAME_KEY);
|
|
@@ -9114,11 +10959,10 @@ function createDaisyUIDataTable() {
|
|
|
9114
10959
|
const currentFilter = table.filters.active[field];
|
|
9115
10960
|
filterInputValues.value[field] = currentFilter?.value || "";
|
|
9116
10961
|
if (filterOperators) {
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
}
|
|
10962
|
+
const fieldMetadata = table.fieldsMetadata[field];
|
|
10963
|
+
const fieldType = fieldMetadata?.type;
|
|
10964
|
+
const defaultOperator = fieldType ? DEFAULT_OPERATORS[fieldType] || OPERATORS.EQUALS : filterType === "text" ? OPERATORS.CONTAINS : OPERATORS.EQUALS;
|
|
10965
|
+
filterOperators.value[field] = currentFilter?.operator || defaultOperator;
|
|
9122
10966
|
}
|
|
9123
10967
|
}
|
|
9124
10968
|
}
|
|
@@ -9171,7 +11015,8 @@ function createDaisyUIDataTable() {
|
|
|
9171
11015
|
isLoading: table.isLoading,
|
|
9172
11016
|
filterCount: table.filters.count,
|
|
9173
11017
|
filterDrawerOpen: table.ui.filterDrawerOpen,
|
|
9174
|
-
onFilterChange: (field,
|
|
11018
|
+
onFilterChange: (field, filter) => table.setFilter(field, filter),
|
|
11019
|
+
onClearFilter: (field) => table.clearFilter(field),
|
|
9175
11020
|
onClearAllFilters: () => table.clearAllFilters(),
|
|
9176
11021
|
onRefresh: () => table.refresh(),
|
|
9177
11022
|
onOpenFilterDrawer: () => table.ui.openFilterDrawer(),
|
|
@@ -9291,7 +11136,9 @@ function createDaisyUIDataTable() {
|
|
|
9291
11136
|
isLoading: table.isLoading,
|
|
9292
11137
|
tableName,
|
|
9293
11138
|
onClose: () => table.ui.closeFilterDrawer(),
|
|
9294
|
-
|
|
11139
|
+
caseSensitive: filterCaseSensitive,
|
|
11140
|
+
onFilterChange: (field, filter) => table.setFilter(field, filter),
|
|
11141
|
+
onClearFilter: (field) => table.clearFilter(field),
|
|
9295
11142
|
onClearAllFilters: () => table.clearAllFilters()
|
|
9296
11143
|
}
|
|
9297
11144
|
)
|
|
@@ -9403,6 +11250,7 @@ function useCursorDataTableState(initialData = [], options = {}) {
|
|
|
9403
11250
|
field: null,
|
|
9404
11251
|
direction: "asc"
|
|
9405
11252
|
});
|
|
11253
|
+
const fieldRegistry = ref(null);
|
|
9406
11254
|
const filters = ref({});
|
|
9407
11255
|
const search = reactive({
|
|
9408
11256
|
query: "",
|
|
@@ -9480,6 +11328,9 @@ function useCursorDataTableState(initialData = [], options = {}) {
|
|
|
9480
11328
|
filters.value = newFilters;
|
|
9481
11329
|
pagination.currentPageNumber = 0;
|
|
9482
11330
|
};
|
|
11331
|
+
const setFieldRegistry = (registry) => {
|
|
11332
|
+
fieldRegistry.value = registry;
|
|
11333
|
+
};
|
|
9483
11334
|
const setSearch = (query, searchableFields) => {
|
|
9484
11335
|
search.query = query;
|
|
9485
11336
|
if (searchableFields) {
|
|
@@ -9538,6 +11389,7 @@ function useCursorDataTableState(initialData = [], options = {}) {
|
|
|
9538
11389
|
pagination,
|
|
9539
11390
|
sorting,
|
|
9540
11391
|
filters,
|
|
11392
|
+
fieldRegistry,
|
|
9541
11393
|
search,
|
|
9542
11394
|
selection,
|
|
9543
11395
|
// UI state
|
|
@@ -9562,6 +11414,7 @@ function useCursorDataTableState(initialData = [], options = {}) {
|
|
|
9562
11414
|
setPageSize,
|
|
9563
11415
|
setSorting,
|
|
9564
11416
|
setFilters,
|
|
11417
|
+
setFieldRegistry,
|
|
9565
11418
|
setSearch,
|
|
9566
11419
|
// Selection methods
|
|
9567
11420
|
selectRow,
|
|
@@ -9577,12 +11430,15 @@ function useCursorDataTable(schema, options) {
|
|
|
9577
11430
|
schemaId: options.schemaId,
|
|
9578
11431
|
storeName: "dataTable"
|
|
9579
11432
|
});
|
|
11433
|
+
const fieldRegistry = buildFieldRegistryFromSchema(schema, fieldsMetadata);
|
|
9580
11434
|
const tableState = useCursorDataTableState([], {
|
|
9581
11435
|
debug: options.debug
|
|
9582
11436
|
});
|
|
11437
|
+
tableState.setFieldRegistry(fieldRegistry);
|
|
9583
11438
|
const searchInputValue = ref("");
|
|
9584
11439
|
const filterInputValues = ref({});
|
|
9585
11440
|
const filterOperators = ref({});
|
|
11441
|
+
const filterCaseSensitive = ref({});
|
|
9586
11442
|
const filterOptionsState = ref({});
|
|
9587
11443
|
const filterOptionsLoading = ref({});
|
|
9588
11444
|
if (options.pagination?.pageSize) {
|
|
@@ -9599,15 +11455,20 @@ function useCursorDataTable(schema, options) {
|
|
|
9599
11455
|
Object.entries(options.initialFilters).forEach(([field, filter]) => {
|
|
9600
11456
|
if (filter && filter.value !== "" && filter.value !== null && filter.value !== void 0) {
|
|
9601
11457
|
filterInputValues.value[field] = String(filter.value);
|
|
9602
|
-
|
|
11458
|
+
const fieldMetadata = fieldRegistry[field];
|
|
11459
|
+
const defaultOperator = fieldMetadata ? DEFAULT_OPERATORS[fieldMetadata.type] || OPERATORS.EQUALS : OPERATORS.EQUALS;
|
|
11460
|
+
filterOperators.value[field] = filter.operator || defaultOperator;
|
|
9603
11461
|
}
|
|
9604
11462
|
});
|
|
9605
11463
|
}
|
|
9606
11464
|
const extractActiveFilters = (filterState) => {
|
|
9607
11465
|
const activeFilters = {};
|
|
9608
|
-
Object.entries(filterState).forEach(([
|
|
9609
|
-
|
|
9610
|
-
|
|
11466
|
+
Object.entries(filterState).forEach(([fieldName, filter]) => {
|
|
11467
|
+
const hasValue = filter.value !== void 0 && filter.value !== "" && filter.value !== null;
|
|
11468
|
+
const hasValues = filter.values !== void 0 && Array.isArray(filter.values) && filter.values.length > 0;
|
|
11469
|
+
const isNullCheck = filter.operator === "isEmpty" || filter.operator === "isNotEmpty";
|
|
11470
|
+
if (hasValue || hasValues || isNullCheck) {
|
|
11471
|
+
activeFilters[fieldName] = filter;
|
|
9611
11472
|
}
|
|
9612
11473
|
});
|
|
9613
11474
|
return activeFilters;
|
|
@@ -9637,6 +11498,16 @@ function useCursorDataTable(schema, options) {
|
|
|
9637
11498
|
try {
|
|
9638
11499
|
tableState.setLoading(true);
|
|
9639
11500
|
tableState.setError(null);
|
|
11501
|
+
const activeFilters = extractActiveFilters(tableState.state.filters.value);
|
|
11502
|
+
const validation = validateFilterConfiguration(activeFilters, fieldRegistry);
|
|
11503
|
+
if (!validation.valid) {
|
|
11504
|
+
const errorMessage = `Filter validation failed: ${validation.errors.map((e) => e.message).join(", ")}`;
|
|
11505
|
+
tableState.setError(errorMessage);
|
|
11506
|
+
if (options.debug) {
|
|
11507
|
+
console.error("\u274C Filter validation errors:", validation.errors);
|
|
11508
|
+
}
|
|
11509
|
+
throw new Error(errorMessage);
|
|
11510
|
+
}
|
|
9640
11511
|
if (options.debug) {
|
|
9641
11512
|
console.log("\u{1F504} Fetching cursor table data with params:", {
|
|
9642
11513
|
cursor: tableState.state.pagination.currentCursor,
|
|
@@ -9645,7 +11516,7 @@ function useCursorDataTable(schema, options) {
|
|
|
9645
11516
|
field: tableState.state.sorting.field,
|
|
9646
11517
|
direction: tableState.state.sorting.direction
|
|
9647
11518
|
} : null,
|
|
9648
|
-
filters:
|
|
11519
|
+
filters: activeFilters,
|
|
9649
11520
|
search: {
|
|
9650
11521
|
query: tableState.state.search.query,
|
|
9651
11522
|
searchableFields: tableState.state.search.searchableFields
|
|
@@ -9659,7 +11530,7 @@ function useCursorDataTable(schema, options) {
|
|
|
9659
11530
|
field: tableState.state.sorting.field,
|
|
9660
11531
|
direction: tableState.state.sorting.direction
|
|
9661
11532
|
} : null,
|
|
9662
|
-
filters:
|
|
11533
|
+
filters: activeFilters,
|
|
9663
11534
|
search: {
|
|
9664
11535
|
query: tableState.state.search.query,
|
|
9665
11536
|
searchableFields: tableState.state.search.searchableFields
|
|
@@ -9700,13 +11571,29 @@ function useCursorDataTable(schema, options) {
|
|
|
9700
11571
|
tableState.setCursorNavigation(void 0);
|
|
9701
11572
|
await fetchData();
|
|
9702
11573
|
};
|
|
9703
|
-
const setFilter = async (field,
|
|
11574
|
+
const setFilter = async (field, filter) => {
|
|
9704
11575
|
const currentFilters = { ...tableState.state.filters.value };
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
11576
|
+
const fieldName = field;
|
|
11577
|
+
const hasValue = filter.value !== void 0 && filter.value !== "" && filter.value !== null;
|
|
11578
|
+
const hasValues = filter.values !== void 0 && Array.isArray(filter.values) && filter.values.length > 0;
|
|
11579
|
+
const isNullCheck = filter.operator === "isEmpty" || filter.operator === "isNotEmpty";
|
|
11580
|
+
const isEmpty = !hasValue && !hasValues && !isNullCheck;
|
|
11581
|
+
if (isEmpty) {
|
|
11582
|
+
delete currentFilters[fieldName];
|
|
11583
|
+
tableState.setFilters(currentFilters);
|
|
11584
|
+
tableState.setCursorNavigation(void 0);
|
|
11585
|
+
return;
|
|
11586
|
+
}
|
|
11587
|
+
const validation = validateFilterValueObject(fieldName, filter, fieldRegistry);
|
|
11588
|
+
if (!validation.valid) {
|
|
11589
|
+
const errorMessage = `Invalid filter for field "${fieldName}": ${validation.errors.map((e) => e.message).join(", ")}`;
|
|
11590
|
+
tableState.setError(errorMessage);
|
|
11591
|
+
if (options.debug) {
|
|
11592
|
+
console.error("\u274C Filter validation errors:", validation.errors);
|
|
11593
|
+
}
|
|
11594
|
+
throw new Error(errorMessage);
|
|
9709
11595
|
}
|
|
11596
|
+
currentFilters[fieldName] = filter;
|
|
9710
11597
|
tableState.setFilters(currentFilters);
|
|
9711
11598
|
tableState.setCursorNavigation(void 0);
|
|
9712
11599
|
await fetchData();
|
|
@@ -9716,13 +11603,39 @@ function useCursorDataTable(schema, options) {
|
|
|
9716
11603
|
delete currentFilters[field];
|
|
9717
11604
|
tableState.setFilters(currentFilters);
|
|
9718
11605
|
tableState.setCursorNavigation(void 0);
|
|
11606
|
+
delete filterInputValues.value[field];
|
|
11607
|
+
delete filterInputValues.value[`${field}_start`];
|
|
11608
|
+
delete filterInputValues.value[`${field}_end`];
|
|
11609
|
+
delete filterOperators.value[field];
|
|
11610
|
+
delete filterCaseSensitive.value[field];
|
|
9719
11611
|
await fetchData();
|
|
9720
11612
|
};
|
|
9721
11613
|
const clearAllFilters = async () => {
|
|
9722
11614
|
tableState.setFilters({});
|
|
9723
11615
|
tableState.setCursorNavigation(void 0);
|
|
11616
|
+
filterInputValues.value = {};
|
|
11617
|
+
filterOperators.value = {};
|
|
11618
|
+
filterCaseSensitive.value = {};
|
|
9724
11619
|
await fetchData();
|
|
9725
11620
|
};
|
|
11621
|
+
const setFiltersFromQueryParams = async (params) => {
|
|
11622
|
+
const filters = deserializeFiltersFromQueryParams(params, fieldRegistry);
|
|
11623
|
+
tableState.setFilters(filters);
|
|
11624
|
+
tableState.setCursorNavigation(void 0);
|
|
11625
|
+
await fetchData();
|
|
11626
|
+
};
|
|
11627
|
+
const setFiltersFromQueryString = async (queryString) => {
|
|
11628
|
+
if (queryString && queryString.trim() !== "") {
|
|
11629
|
+
const params = new URLSearchParams(queryString);
|
|
11630
|
+
const paramsObj = {};
|
|
11631
|
+
params.forEach((value, key) => {
|
|
11632
|
+
paramsObj[key] = value;
|
|
11633
|
+
});
|
|
11634
|
+
await setFiltersFromQueryParams(paramsObj);
|
|
11635
|
+
} else {
|
|
11636
|
+
await setFiltersFromQueryParams({});
|
|
11637
|
+
}
|
|
11638
|
+
};
|
|
9726
11639
|
const setSearch = async (query) => {
|
|
9727
11640
|
tableState.setSearch(query);
|
|
9728
11641
|
tableState.setCursorNavigation(void 0);
|
|
@@ -9861,8 +11774,20 @@ function useCursorDataTable(schema, options) {
|
|
|
9861
11774
|
},
|
|
9862
11775
|
get count() {
|
|
9863
11776
|
return Object.keys(extractActiveFilters(tableState.state.filters.value)).length;
|
|
11777
|
+
},
|
|
11778
|
+
get queryParams() {
|
|
11779
|
+
const activeFilters = extractActiveFilters(tableState.state.filters.value);
|
|
11780
|
+
return serializeFiltersToQueryParams(activeFilters, fieldRegistry);
|
|
11781
|
+
},
|
|
11782
|
+
// Legacy: queryString for backward compatibility (returns empty string, use queryParams instead)
|
|
11783
|
+
get queryString() {
|
|
11784
|
+
return "";
|
|
9864
11785
|
}
|
|
9865
11786
|
},
|
|
11787
|
+
// Field registry
|
|
11788
|
+
get fieldRegistry() {
|
|
11789
|
+
return fieldRegistry;
|
|
11790
|
+
},
|
|
9866
11791
|
// Search (same interface)
|
|
9867
11792
|
search: {
|
|
9868
11793
|
get query() {
|
|
@@ -9895,6 +11820,8 @@ function useCursorDataTable(schema, options) {
|
|
|
9895
11820
|
setFilter,
|
|
9896
11821
|
clearFilter,
|
|
9897
11822
|
clearAllFilters,
|
|
11823
|
+
setFiltersFromQueryParams,
|
|
11824
|
+
setFiltersFromQueryString,
|
|
9898
11825
|
setSearch,
|
|
9899
11826
|
clearSearch,
|
|
9900
11827
|
nextPage,
|
|
@@ -9928,6 +11855,7 @@ function useCursorDataTable(schema, options) {
|
|
|
9928
11855
|
provide(ZINIA_DATA_TABLE_SEARCH_INPUT_KEY, searchInputValue);
|
|
9929
11856
|
provide(ZINIA_DATA_TABLE_FILTER_INPUTS_KEY, filterInputValues);
|
|
9930
11857
|
provide(ZINIA_DATA_TABLE_FILTER_OPERATORS_KEY, filterOperators);
|
|
11858
|
+
provide(ZINIA_DATA_TABLE_FILTER_CASE_SENSITIVE_KEY, filterCaseSensitive);
|
|
9931
11859
|
provide(ZINIA_DATA_TABLE_FILTER_OPTIONS_STATE_KEY, filterOptionsState);
|
|
9932
11860
|
provide(ZINIA_DATA_TABLE_FILTER_OPTIONS_LOADING_KEY, filterOptionsLoading);
|
|
9933
11861
|
provide(ZINIA_DATA_TABLE_NAME_KEY, options.name || "datatable");
|
|
@@ -9937,11 +11865,13 @@ function useCursorDataTable(schema, options) {
|
|
|
9937
11865
|
console.error("Failed to load filter options:", err);
|
|
9938
11866
|
}
|
|
9939
11867
|
});
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
9944
|
-
|
|
11868
|
+
if (options.autoLoad !== false) {
|
|
11869
|
+
fetchData().catch((err) => {
|
|
11870
|
+
if (options.debug) {
|
|
11871
|
+
console.error("Failed to auto-load cursor table data:", err);
|
|
11872
|
+
}
|
|
11873
|
+
});
|
|
11874
|
+
}
|
|
9945
11875
|
return {
|
|
9946
11876
|
table,
|
|
9947
11877
|
// Components
|
|
@@ -9954,6 +11884,7 @@ function useCursorDataTable(schema, options) {
|
|
|
9954
11884
|
setFilter,
|
|
9955
11885
|
clearFilter,
|
|
9956
11886
|
clearAllFilters,
|
|
11887
|
+
setFiltersFromQueryString,
|
|
9957
11888
|
setSearch,
|
|
9958
11889
|
clearSearch,
|
|
9959
11890
|
nextPage,
|
|
@@ -9966,7 +11897,79 @@ function useCursorDataTable(schema, options) {
|
|
|
9966
11897
|
isRowSelected
|
|
9967
11898
|
};
|
|
9968
11899
|
}
|
|
11900
|
+
function useDataTableUrlSync(table, router, options = {}) {
|
|
11901
|
+
const { loadOnMount = true } = options;
|
|
11902
|
+
const isUpdatingFromRoute = ref(false);
|
|
11903
|
+
const originalSetFilter = table.setFilter.bind(table);
|
|
11904
|
+
const originalClearAllFilters = table.clearAllFilters.bind(table);
|
|
11905
|
+
const extractFilterParams = (query) => {
|
|
11906
|
+
const filterParams = {};
|
|
11907
|
+
Object.keys(query).forEach((key) => {
|
|
11908
|
+
if (key.startsWith("flt[")) {
|
|
11909
|
+
filterParams[key] = query[key];
|
|
11910
|
+
}
|
|
11911
|
+
});
|
|
11912
|
+
return filterParams;
|
|
11913
|
+
};
|
|
11914
|
+
const updateUrlFromFilters = () => {
|
|
11915
|
+
if (isUpdatingFromRoute.value) return;
|
|
11916
|
+
const queryParams = table.filters.queryParams || {};
|
|
11917
|
+
const query = { ...router.currentRoute.value.query };
|
|
11918
|
+
Object.keys(query).forEach((key) => {
|
|
11919
|
+
if (key.startsWith("flt[")) delete query[key];
|
|
11920
|
+
});
|
|
11921
|
+
if (Object.keys(queryParams).length > 0) {
|
|
11922
|
+
Object.assign(query, queryParams);
|
|
11923
|
+
}
|
|
11924
|
+
router.push({ query });
|
|
11925
|
+
};
|
|
11926
|
+
table.setFilter = async (field, filter) => {
|
|
11927
|
+
await originalSetFilter(field, filter);
|
|
11928
|
+
updateUrlFromFilters();
|
|
11929
|
+
};
|
|
11930
|
+
table.clearAllFilters = async () => {
|
|
11931
|
+
await originalClearAllFilters();
|
|
11932
|
+
updateUrlFromFilters();
|
|
11933
|
+
};
|
|
11934
|
+
watch(
|
|
11935
|
+
() => router.currentRoute.value.query,
|
|
11936
|
+
async (newQuery) => {
|
|
11937
|
+
if (isUpdatingFromRoute.value) return;
|
|
11938
|
+
const filterParams = extractFilterParams(newQuery);
|
|
11939
|
+
const currentParams = table.filters.queryParams || {};
|
|
11940
|
+
const paramsMatch = Object.keys(filterParams).length === Object.keys(currentParams).length && Object.keys(filterParams).every((key) => filterParams[key] === currentParams[key]);
|
|
11941
|
+
if (paramsMatch) return;
|
|
11942
|
+
isUpdatingFromRoute.value = true;
|
|
11943
|
+
try {
|
|
11944
|
+
if (Object.keys(filterParams).length > 0) {
|
|
11945
|
+
await table.setFiltersFromQueryParams(filterParams);
|
|
11946
|
+
} else if (Object.keys(currentParams).length > 0) {
|
|
11947
|
+
await originalClearAllFilters();
|
|
11948
|
+
}
|
|
11949
|
+
} finally {
|
|
11950
|
+
await nextTick();
|
|
11951
|
+
isUpdatingFromRoute.value = false;
|
|
11952
|
+
}
|
|
11953
|
+
},
|
|
11954
|
+
{ deep: true }
|
|
11955
|
+
);
|
|
11956
|
+
if (loadOnMount) {
|
|
11957
|
+
onMounted(async () => {
|
|
11958
|
+
const filterParams = extractFilterParams(router.currentRoute.value.query);
|
|
11959
|
+
if (Object.keys(filterParams).length > 0) {
|
|
11960
|
+
try {
|
|
11961
|
+
await table.setFiltersFromQueryParams(filterParams);
|
|
11962
|
+
} catch (error) {
|
|
11963
|
+
console.error("Failed to load filters from URL:", error);
|
|
11964
|
+
await table.load();
|
|
11965
|
+
}
|
|
11966
|
+
} else {
|
|
11967
|
+
await table.load();
|
|
11968
|
+
}
|
|
11969
|
+
});
|
|
11970
|
+
}
|
|
11971
|
+
}
|
|
9969
11972
|
|
|
9970
|
-
export { ActionIcons, COMBOBOX_FIELD_PROP_NAMES, DEBUG_CONFIG, ErrorDisplay, LoadingDisplay, NoDataDisplay, SCHEMA_ID_SYMBOL, SELECT_FIELD_PROP_NAMES, ZINIA_DATA_TABLE_ACTIONS_KEY, ZINIA_DATA_TABLE_COLUMNS_KEY, ZINIA_DATA_TABLE_FILTER_INPUTS_KEY, ZINIA_DATA_TABLE_FILTER_OPERATORS_KEY, ZINIA_DATA_TABLE_FILTER_OPTIONS_LOADING_KEY, ZINIA_DATA_TABLE_FILTER_OPTIONS_STATE_KEY, ZINIA_DATA_TABLE_KEY, ZINIA_DATA_TABLE_NAME_KEY, ZINIA_DATA_TABLE_OPTIONS_KEY, ZINIA_DATA_TABLE_SEARCH_INPUT_KEY, ZINIA_DELETE_MODAL_FIELDS_GENERIC_KEY, ZINIA_DELETE_MODAL_FIELDS_KEY, ZINIA_DELETE_MODAL_KEY, ZINIA_DELETE_MODAL_SCHEMA_KEY, ZINIA_DISPLAY_FIELDS_GENERIC_KEY, ZINIA_DISPLAY_FIELDS_KEY, ZINIA_DISPLAY_KEY, ZINIA_DISPLAY_SCHEMA_KEY, ZINIA_FIELDS_GENERIC_KEY, ZINIA_FIELDS_KEY, ZINIA_FORM_KEY, ZINIA_FORM_SCHEMA_KEY, ZiniaPlugin, clearAllMetadata, clearSchemaMetadata, createBaseComponents, createBaseDisplayComponents, createPartialStyle, createStyleTemplate, createTypedArrayField, createTypedComboboxField, createTypedSelectField, daisyUIStyle, extendStyle, generateDisplayComponents, generateFieldComponents, getAllSchemaMetadata, getDefaultStyle, getFieldMetadata, getRegisteredStyle, getRegisteredStyleNames, getSchemaId, hasRegisteredStyle, hasSchemaMetadata, isDebugEnabled, mergeStyles, registerSchemaMetadata, registerStyle, setSchemaMetadata, useCursorDataTable, useDataTable, useDeleteModal, useDisplay, useForm, withMetadata };
|
|
11973
|
+
export { ActionIcons, COMBOBOX_FIELD_PROP_NAMES, DEBUG_CONFIG, ErrorDisplay, LoadingDisplay, NoDataDisplay, SCHEMA_ID_SYMBOL, SELECT_FIELD_PROP_NAMES, ZINIA_DATA_TABLE_ACTIONS_KEY, ZINIA_DATA_TABLE_COLUMNS_KEY, ZINIA_DATA_TABLE_FILTER_CASE_SENSITIVE_KEY, ZINIA_DATA_TABLE_FILTER_INPUTS_KEY, ZINIA_DATA_TABLE_FILTER_OPERATORS_KEY, ZINIA_DATA_TABLE_FILTER_OPTIONS_LOADING_KEY, ZINIA_DATA_TABLE_FILTER_OPTIONS_STATE_KEY, ZINIA_DATA_TABLE_KEY, ZINIA_DATA_TABLE_NAME_KEY, ZINIA_DATA_TABLE_OPTIONS_KEY, ZINIA_DATA_TABLE_SEARCH_INPUT_KEY, ZINIA_DELETE_MODAL_FIELDS_GENERIC_KEY, ZINIA_DELETE_MODAL_FIELDS_KEY, ZINIA_DELETE_MODAL_KEY, ZINIA_DELETE_MODAL_SCHEMA_KEY, ZINIA_DISPLAY_FIELDS_GENERIC_KEY, ZINIA_DISPLAY_FIELDS_KEY, ZINIA_DISPLAY_KEY, ZINIA_DISPLAY_SCHEMA_KEY, ZINIA_FIELDS_GENERIC_KEY, ZINIA_FIELDS_KEY, ZINIA_FORM_KEY, ZINIA_FORM_SCHEMA_KEY, ZiniaPlugin, buildFieldRegistryFromSchema, clearAllMetadata, clearSchemaMetadata, createBaseComponents, createBaseDisplayComponents, createPartialStyle, createStyleTemplate, createTypedArrayField, createTypedComboboxField, createTypedSelectField, daisyUIStyle, deserializeFiltersFromQueryString, extendStyle, generateDisplayComponents, generateFieldComponents, getAllSchemaMetadata, getDefaultStyle, getFieldMetadata, getRegisteredStyle, getRegisteredStyleNames, getSchemaId, hasRegisteredStyle, hasSchemaMetadata, isDebugEnabled, isValidOperatorForType, mergeStyles, registerSchemaMetadata, registerStyle, serializeFiltersToQueryString, setSchemaMetadata, useCursorDataTable, useDataTable, useDataTableUrlSync, useDeleteModal, useDisplay, useForm, validateFieldRegistry, validateFilterConfiguration, withMetadata };
|
|
9971
11974
|
//# sourceMappingURL=index.js.map
|
|
9972
11975
|
//# sourceMappingURL=index.js.map
|