tailjng 0.1.13 → 0.1.14
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/cli/component-manager.js +3 -1
- package/cli/file-operations.js +95 -4
- package/cli/settings/path-utils.js +35 -0
- package/package.json +1 -1
- package/src/lib/components/filter/filter-complete/complete-filter.component.html +58 -62
- package/src/lib/components/filter/filter-complete/complete-filter.component.ts +23 -4
- package/src/lib/components/filter/filter-complete/complete-filter.types.ts +68 -1
- package/src/lib/components/filter/filter-complete/complete-filter.util.ts +16 -16
package/cli/component-manager.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// component-manager.js
|
|
2
2
|
|
|
3
|
-
const { copyComponentFiles } = require("./file-operations");
|
|
3
|
+
const { copyComponentFiles, resetInstalledSharedFolders } = require("./file-operations");
|
|
4
4
|
const { installDependencies } = require("./dependency-manager");
|
|
5
5
|
const { COLORS } = require("./settings/colors");
|
|
6
6
|
const { collectDependencyTree } = require("./settings/lib-utils");
|
|
@@ -50,6 +50,7 @@ function logInstallPlan(componentName, componentList) {
|
|
|
50
50
|
|
|
51
51
|
async function addComponent(componentName, componentList) {
|
|
52
52
|
resetOverwritePolicy();
|
|
53
|
+
resetInstalledSharedFolders();
|
|
53
54
|
installedComponentsGlobal.clear();
|
|
54
55
|
|
|
55
56
|
const componentData = componentList[componentName];
|
|
@@ -96,6 +97,7 @@ async function addComponent(componentName, componentList) {
|
|
|
96
97
|
|
|
97
98
|
async function installAllComponents(componentList) {
|
|
98
99
|
resetOverwritePolicy();
|
|
100
|
+
resetInstalledSharedFolders();
|
|
99
101
|
installedComponentsGlobal.clear();
|
|
100
102
|
|
|
101
103
|
const allNames = Object.keys(componentList);
|
package/cli/file-operations.js
CHANGED
|
@@ -5,14 +5,29 @@ const path = require("path")
|
|
|
5
5
|
const { COLORS } = require("./settings/colors")
|
|
6
6
|
const { generateHeaderComment } = require("./settings/header-generator")
|
|
7
7
|
const { askOverwrite } = require("./settings/prompt-utils")
|
|
8
|
-
const {
|
|
8
|
+
const { getPackageRoot } = require("./settings/lib-utils")
|
|
9
|
+
const {
|
|
10
|
+
buildTargetPath,
|
|
11
|
+
buildSharedFolderTargetPath,
|
|
12
|
+
getSharedFolderInfo,
|
|
13
|
+
parseComponentPath,
|
|
14
|
+
} = require("./settings/path-utils")
|
|
15
|
+
|
|
16
|
+
/** Avoid copying the same group `shared/` folder twice per install session. */
|
|
17
|
+
const installedSharedFolders = new Set()
|
|
9
18
|
|
|
10
19
|
const TEXT_EXTENSIONS = new Set([".ts", ".js", ".html", ".css", ".scss", ".json", ".md"])
|
|
20
|
+
const LIB_PACKAGE_IMPORT_RE =
|
|
21
|
+
/from\s+['"](?:\.\.\/)+(?:interfaces|services|config)\/[^'"]+['"]/g
|
|
11
22
|
|
|
12
23
|
function shouldAddHeader(fileName) {
|
|
13
24
|
return TEXT_EXTENSIONS.has(path.extname(fileName).toLowerCase())
|
|
14
25
|
}
|
|
15
26
|
|
|
27
|
+
function patchConsumerImports(content) {
|
|
28
|
+
return content.replace(LIB_PACKAGE_IMPORT_RE, "from 'tailjng'")
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
function copyFileWithHeader(srcFile, destFile) {
|
|
17
32
|
fs.mkdirSync(path.dirname(destFile), { recursive: true })
|
|
18
33
|
|
|
@@ -22,6 +37,9 @@ function copyFileWithHeader(srcFile, destFile) {
|
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
let content = fs.readFileSync(srcFile, "utf8")
|
|
40
|
+
if (path.extname(srcFile).toLowerCase() === ".ts") {
|
|
41
|
+
content = patchConsumerImports(content)
|
|
42
|
+
}
|
|
25
43
|
const headerComment = generateHeaderComment(path.basename(srcFile))
|
|
26
44
|
content = headerComment + "\n\n" + content
|
|
27
45
|
fs.writeFileSync(destFile, content)
|
|
@@ -49,7 +67,11 @@ function copyDirectoryRecursive(srcDir, destDir, projectRoot) {
|
|
|
49
67
|
}
|
|
50
68
|
}
|
|
51
69
|
|
|
52
|
-
|
|
70
|
+
function resetInstalledSharedFolders() {
|
|
71
|
+
installedSharedFolders.clear()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function findTailjngPackageRoot() {
|
|
53
75
|
let currentDir = process.cwd()
|
|
54
76
|
while (!fs.existsSync(path.join(currentDir, "node_modules"))) {
|
|
55
77
|
currentDir = path.dirname(currentDir)
|
|
@@ -59,7 +81,74 @@ async function copyComponentFiles(componentName, componentPath, isDependency = f
|
|
|
59
81
|
}
|
|
60
82
|
}
|
|
61
83
|
|
|
62
|
-
|
|
84
|
+
return getPackageRoot(path.join(currentDir, "node_modules", "tailjng", "cli"))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function copySharedFolderIfNeeded(componentPath, isDependency = false, options = {}) {
|
|
88
|
+
const { forceSync = false } = options
|
|
89
|
+
const sharedInfo = getSharedFolderInfo(componentPath)
|
|
90
|
+
if (!sharedInfo) {
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (installedSharedFolders.has(sharedInfo.groupKey)) {
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const packageRoot = findTailjngPackageRoot()
|
|
99
|
+
const sharedSrc = path.join(packageRoot, ...sharedInfo.sourceRelative.split("/"))
|
|
100
|
+
if (!fs.existsSync(sharedSrc)) {
|
|
101
|
+
console.error(
|
|
102
|
+
`${COLORS.red}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.red}ERROR: Shared utilities ${COLORS.bright}"${sharedInfo.groupKey}/shared"${COLORS.reset} ${COLORS.red}not found in package.${COLORS.reset}`,
|
|
103
|
+
)
|
|
104
|
+
console.error(`${COLORS.dim} Expected: ${sharedSrc}${COLORS.reset}`)
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const projectRoot = process.cwd()
|
|
109
|
+
const sharedDest = buildSharedFolderTargetPath(projectRoot, componentPath)
|
|
110
|
+
if (!sharedDest) {
|
|
111
|
+
return true
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const sharedLabel = `${sharedInfo.groupKey}/shared`
|
|
115
|
+
const relativeTargetPath = path.relative(projectRoot, sharedDest)
|
|
116
|
+
const sharedMissing = !fs.existsSync(sharedDest)
|
|
117
|
+
|
|
118
|
+
if (!forceSync && !sharedMissing) {
|
|
119
|
+
const shouldOverwrite = await askOverwrite(sharedLabel, relativeTargetPath, isDependency)
|
|
120
|
+
if (!shouldOverwrite) {
|
|
121
|
+
console.log(
|
|
122
|
+
`${COLORS.dim}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.dim}Skipping ${COLORS.bright}"${sharedLabel}"${COLORS.reset} ${COLORS.dim}- keeping existing version.${COLORS.reset}`,
|
|
123
|
+
)
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log(
|
|
128
|
+
`${COLORS.yellow}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.yellow}Removing existing ${COLORS.bright}"${sharedLabel}"${COLORS.reset} ${COLORS.yellow}to overwrite...${COLORS.reset}`,
|
|
129
|
+
)
|
|
130
|
+
fs.rmSync(sharedDest, { recursive: true, force: true })
|
|
131
|
+
} else if (!sharedMissing) {
|
|
132
|
+
console.log(
|
|
133
|
+
`${COLORS.yellow}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.yellow}Updating shared utilities ${COLORS.bright}"${sharedLabel}"${COLORS.reset} ${COLORS.yellow}with the installed component...${COLORS.reset}`,
|
|
134
|
+
)
|
|
135
|
+
fs.rmSync(sharedDest, { recursive: true, force: true })
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log(
|
|
139
|
+
`${COLORS.blue}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.blue}Copying shared utilities ${COLORS.bright}"${sharedLabel}"${COLORS.reset} → ${relativeTargetPath}${COLORS.reset}`,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
fs.mkdirSync(sharedDest, { recursive: true })
|
|
143
|
+
copyDirectoryRecursive(sharedSrc, sharedDest, projectRoot)
|
|
144
|
+
installedSharedFolders.add(sharedInfo.groupKey)
|
|
145
|
+
|
|
146
|
+
return true
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function copyComponentFiles(componentName, componentPath, isDependency = false) {
|
|
150
|
+
const packageRoot = findTailjngPackageRoot()
|
|
151
|
+
const nodeModulesPath = path.join(packageRoot, ...componentPath.split("/"))
|
|
63
152
|
const projectRoot = process.cwd()
|
|
64
153
|
const targetPath = buildTargetPath(projectRoot, componentName, componentPath)
|
|
65
154
|
const pathInfo = parseComponentPath(componentPath)
|
|
@@ -77,6 +166,7 @@ async function copyComponentFiles(componentName, componentPath, isDependency = f
|
|
|
77
166
|
|
|
78
167
|
if (!shouldOverwrite) {
|
|
79
168
|
console.log(`${COLORS.dim}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.dim}Skipping ${COLORS.bright}"${componentName}"${COLORS.reset} ${COLORS.dim}- keeping existing version.${COLORS.reset}`)
|
|
169
|
+
await copySharedFolderIfNeeded(componentPath, isDependency, { forceSync: false })
|
|
80
170
|
return false
|
|
81
171
|
}
|
|
82
172
|
|
|
@@ -99,8 +189,9 @@ async function copyComponentFiles(componentName, componentPath, isDependency = f
|
|
|
99
189
|
|
|
100
190
|
fs.mkdirSync(targetPath, { recursive: true })
|
|
101
191
|
copyDirectoryRecursive(nodeModulesPath, targetPath, projectRoot)
|
|
192
|
+
await copySharedFolderIfNeeded(componentPath, isDependency, { forceSync: true })
|
|
102
193
|
|
|
103
194
|
return true
|
|
104
195
|
}
|
|
105
196
|
|
|
106
|
-
module.exports = { copyComponentFiles }
|
|
197
|
+
module.exports = { copyComponentFiles, resetInstalledSharedFolders }
|
|
@@ -58,9 +58,44 @@ function isComponentInstalled(projectRoot, componentName, componentPath) {
|
|
|
58
58
|
return fs.existsSync(buildTargetPath(projectRoot, componentName, componentPath))
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* When a registry path is nested (e.g. toggle-radio/toggle-radio), returns sibling `shared/` info.
|
|
63
|
+
* Used by the CLI to copy group-level utilities alongside the component folder.
|
|
64
|
+
*/
|
|
65
|
+
function getSharedFolderInfo(componentPath) {
|
|
66
|
+
if (!componentPath.startsWith(COMPONENTS_SOURCE_PREFIX)) {
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const relativePath = componentPath.slice(COMPONENTS_SOURCE_PREFIX.length)
|
|
71
|
+
const pathParts = relativePath.split("/")
|
|
72
|
+
if (pathParts.length < 2) {
|
|
73
|
+
return null
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const groupKey = pathParts.slice(0, -1).join("/")
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
groupKey,
|
|
80
|
+
sourceRelative: `${COMPONENTS_SOURCE_PREFIX}${groupKey}/shared`,
|
|
81
|
+
targetRelative: `${groupKey}/shared`,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildSharedFolderTargetPath(projectRoot, componentPath) {
|
|
86
|
+
const sharedInfo = getSharedFolderInfo(componentPath)
|
|
87
|
+
if (!sharedInfo) {
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return path.join(getComponentsBasePath(projectRoot), sharedInfo.targetRelative)
|
|
92
|
+
}
|
|
93
|
+
|
|
61
94
|
module.exports = {
|
|
62
95
|
parseComponentPath,
|
|
63
96
|
buildTargetPath,
|
|
97
|
+
buildSharedFolderTargetPath,
|
|
98
|
+
getSharedFolderInfo,
|
|
64
99
|
isComponentInstalled,
|
|
65
100
|
isConfigPath,
|
|
66
101
|
CONFIG_SOURCE_PREFIX,
|
package/package.json
CHANGED
|
@@ -139,68 +139,64 @@
|
|
|
139
139
|
<div class="flex flex-col gap-2">
|
|
140
140
|
@for (filter of filtersSelect; track $index) {
|
|
141
141
|
@if (filter.isVisible ?? true) {
|
|
142
|
-
@
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
[isFilterSelect]="true"
|
|
201
|
-
/>
|
|
202
|
-
</div>
|
|
203
|
-
}
|
|
142
|
+
@if (isDropdownFilter(filter)) {
|
|
143
|
+
<div class="relative">
|
|
144
|
+
<JDropdownSelect
|
|
145
|
+
[type]="'dropdown'"
|
|
146
|
+
[(ngModel)]="filter.selected"
|
|
147
|
+
(selectionChange)="
|
|
148
|
+
filter.onSelected ? filter.onSelected($event) : null
|
|
149
|
+
"
|
|
150
|
+
[optionLabel]="filter.optionLabel ?? ''"
|
|
151
|
+
[optionValue]="filter.optionValue ?? ''"
|
|
152
|
+
[labelSeparator]="filter.labelSeparator ?? ''"
|
|
153
|
+
[optionLabelTemplate]="filter.optionLabelTemplate ?? ''"
|
|
154
|
+
[optionLabelFn]="filter.optionLabelFn"
|
|
155
|
+
[placeholder]="filter.placeholder ?? ''"
|
|
156
|
+
[showClear]="filter.showClear ?? false"
|
|
157
|
+
[options]="filter.options"
|
|
158
|
+
[sort]="filter.sort ?? 'ASC'"
|
|
159
|
+
[showAllOption]="filter.showAllOption ?? false"
|
|
160
|
+
[isFilterSelect]="true"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
} @else if (isSearchableFilter(filter)) {
|
|
164
|
+
<div class="relative">
|
|
165
|
+
<JDropdownSelect
|
|
166
|
+
[type]="'searchable'"
|
|
167
|
+
[(ngModel)]="filter.selected"
|
|
168
|
+
(selectionChange)="filter.onSelected?.($event)"
|
|
169
|
+
[endpoint]="filter.endpoint"
|
|
170
|
+
[optionLabel]="filter.optionLabel"
|
|
171
|
+
[optionValue]="filter.optionValue"
|
|
172
|
+
[labelSeparator]="filter.labelSeparator ?? ''"
|
|
173
|
+
[optionLabelTemplate]="filter.optionLabelTemplate ?? ''"
|
|
174
|
+
[optionLabelFn]="filter.optionLabelFn"
|
|
175
|
+
[responseKey]="filter.responseKey ?? ''"
|
|
176
|
+
[dataPath]="filter.dataPath ?? ''"
|
|
177
|
+
[placeholder]="filter.placeholder ?? ''"
|
|
178
|
+
[showClear]="filter.showClear ?? false"
|
|
179
|
+
[loadOnInit]="filter.loadOnInit ?? false"
|
|
180
|
+
[isSearch]="filter.isSearch ?? true"
|
|
181
|
+
[searchFields]="filter.searchFields || []"
|
|
182
|
+
[defaultFilters]="filter.defaultFilters || {}"
|
|
183
|
+
[sort]="filter.sort ?? 'ASC'"
|
|
184
|
+
[showAllOption]="filter.showAllOption ?? false"
|
|
185
|
+
[isFilterSelect]="true"
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
} @else if (isMultiTableFilter(filter)) {
|
|
189
|
+
<div class="relative">
|
|
190
|
+
<JMultiTableSelect
|
|
191
|
+
[(ngModel)]="filter.selected"
|
|
192
|
+
(selectionChange)="
|
|
193
|
+
filter.onSelected ? filter.onSelected($event) : null
|
|
194
|
+
"
|
|
195
|
+
[columns]="filter.columns"
|
|
196
|
+
[btnText]="filter.btnText ?? ''"
|
|
197
|
+
[isFilterSelect]="true"
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
204
200
|
}
|
|
205
201
|
}
|
|
206
202
|
}
|
|
@@ -3,20 +3,27 @@ import { NgClass } from '@angular/common';
|
|
|
3
3
|
import { FormsModule } from '@angular/forms';
|
|
4
4
|
import { Subject, Subscription, debounceTime, filter, forkJoin } from 'rxjs';
|
|
5
5
|
import { TableColumn, FilterButton, JUploadFilterService, JAlertDialogService, JAlertToastService, JGenericCrudService, LoadingState, JExcelService, JExcelFilterService } from 'tailjng';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
CompleteFilterAdditionalButtonLoading,
|
|
8
|
+
CompleteFilterDropdownSelect,
|
|
9
|
+
CompleteFilterMultiTableSelect,
|
|
10
|
+
CompleteFilterSearchableSelect,
|
|
11
|
+
CompleteFilterSelect,
|
|
12
|
+
} from './complete-filter.types';
|
|
7
13
|
import { JDropdownSelectComponent } from '../../select/select-dropdown/dropdown-select.component';
|
|
8
14
|
import { JMultiTableSelectComponent } from '../../select/select-multi-table/multi-table-select.component';
|
|
9
15
|
import { JSwitchCheckboxComponent } from '../../checkbox/checkbox-switch/switch-checkbox.component';
|
|
10
16
|
import { JDialogComponent } from '../../dialog/dialog.component';
|
|
11
17
|
import { JButtonComponent } from '../../button/button.component';
|
|
12
|
-
import type { CompleteFilterAdditionalButtonLoading } from './complete-filter.types';
|
|
13
18
|
import { hasActiveFilters } from './complete-filter.util';
|
|
14
19
|
import { Icons } from '../../.config/icons/icons.lucide';
|
|
15
20
|
import { JIconComponent } from '../../icon/icon.component';
|
|
16
21
|
|
|
17
22
|
export type {
|
|
18
23
|
CompleteFilterAdditionalButtonLoading,
|
|
19
|
-
CompleteFilterClearReason
|
|
24
|
+
CompleteFilterClearReason,
|
|
25
|
+
CompleteFilterSelect,
|
|
26
|
+
} from './complete-filter.types';
|
|
20
27
|
export { hasActiveFilters } from './complete-filter.util';
|
|
21
28
|
|
|
22
29
|
/**
|
|
@@ -111,7 +118,19 @@ export class JCompleteFilterComponent implements OnInit, OnDestroy {
|
|
|
111
118
|
@Input() filtersButton: FilterButton[] = [];
|
|
112
119
|
|
|
113
120
|
// Filtros de tabla
|
|
114
|
-
@Input() filtersSelect:
|
|
121
|
+
@Input() filtersSelect: CompleteFilterSelect[] = [];
|
|
122
|
+
|
|
123
|
+
isDropdownFilter(filter: CompleteFilterSelect): filter is CompleteFilterDropdownSelect {
|
|
124
|
+
return filter.type === 'dropdown';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
isSearchableFilter(filter: CompleteFilterSelect): filter is CompleteFilterSearchableSelect {
|
|
128
|
+
return filter.type === 'searchable';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
isMultiTableFilter(filter: CompleteFilterSelect): filter is CompleteFilterMultiTableSelect {
|
|
132
|
+
return filter.type === 'multi-table';
|
|
133
|
+
}
|
|
115
134
|
|
|
116
135
|
get visibleColumns(): TableColumn<any>[] {
|
|
117
136
|
return this.columns.filter(col => !col.hidden);
|
|
@@ -1,4 +1,71 @@
|
|
|
1
|
-
import type { LoadingState } from 'tailjng';
|
|
1
|
+
import type { LoadingState, TableColumn } from 'tailjng';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Filter-select shapes for JFilter templates.
|
|
5
|
+
* Prefixed with `CompleteFilter*` to avoid clashing with `tailjng` exports during strict template narrowing.
|
|
6
|
+
*/
|
|
7
|
+
export interface CompleteFilterDropdownSelect {
|
|
8
|
+
type: 'dropdown';
|
|
9
|
+
selected: unknown;
|
|
10
|
+
initSelected?: unknown;
|
|
11
|
+
onSelected?: (value: unknown) => void;
|
|
12
|
+
optionLabel?: string | string[];
|
|
13
|
+
optionValue?: string;
|
|
14
|
+
labelSeparator?: string;
|
|
15
|
+
optionLabelTemplate?: string;
|
|
16
|
+
optionLabelFn?: (option: Record<string, unknown>) => string;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
showClear?: boolean;
|
|
19
|
+
options: unknown[];
|
|
20
|
+
deep?: string;
|
|
21
|
+
sort?: 'ASC' | 'DESC';
|
|
22
|
+
isVisible?: ((data?: unknown) => boolean) | boolean;
|
|
23
|
+
showAllOption?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CompleteFilterSearchableSelect {
|
|
27
|
+
type: 'searchable';
|
|
28
|
+
selected: unknown;
|
|
29
|
+
initSelected?: unknown;
|
|
30
|
+
onSelected?: (value: unknown) => void;
|
|
31
|
+
endpoint: string;
|
|
32
|
+
isSearch?: boolean;
|
|
33
|
+
optionLabel: string | string[];
|
|
34
|
+
optionValue: string;
|
|
35
|
+
labelSeparator?: string;
|
|
36
|
+
optionLabelTemplate?: string;
|
|
37
|
+
optionLabelFn?: (option: Record<string, unknown>) => string;
|
|
38
|
+
responseKey?: string;
|
|
39
|
+
dataPath?: string;
|
|
40
|
+
placeholder?: string;
|
|
41
|
+
showClear?: boolean;
|
|
42
|
+
loadOnInit?: boolean;
|
|
43
|
+
searchFields?: string[];
|
|
44
|
+
defaultFilters?: Record<string, unknown>;
|
|
45
|
+
deep?: string;
|
|
46
|
+
sort?: 'ASC' | 'DESC';
|
|
47
|
+
isVisible?: ((data?: unknown) => boolean) | boolean;
|
|
48
|
+
showAllOption?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface CompleteFilterMultiTableSelect {
|
|
52
|
+
type: 'multi-table';
|
|
53
|
+
selected: unknown;
|
|
54
|
+
initSelected?: unknown;
|
|
55
|
+
onSelected?: (value: unknown) => void;
|
|
56
|
+
columns: TableColumn<unknown>[];
|
|
57
|
+
btnText?: string;
|
|
58
|
+
placeholder?: string;
|
|
59
|
+
deep?: string;
|
|
60
|
+
sort?: 'ASC' | 'DESC';
|
|
61
|
+
isVisible?: ((data?: unknown) => boolean) | boolean;
|
|
62
|
+
showAllOption?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type CompleteFilterSelect =
|
|
66
|
+
| CompleteFilterDropdownSelect
|
|
67
|
+
| CompleteFilterSearchableSelect
|
|
68
|
+
| CompleteFilterMultiTableSelect;
|
|
2
69
|
|
|
3
70
|
/** Event payload from `(clearFilters)` — typically `'clear'`. */
|
|
4
71
|
export type CompleteFilterClearReason = string;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
/** Whether search text or any filter select differs from its initial value. */
|
|
4
|
-
export function hasActiveFilters(
|
|
5
|
-
searchQuery: string,
|
|
6
|
-
filtersSelect:
|
|
7
|
-
): boolean {
|
|
8
|
-
const hasActiveSearch = searchQuery?.trim().length > 0;
|
|
9
|
-
const hasSelectedFilters = filtersSelect.some((filter) => {
|
|
10
|
-
const actual = filter.selected;
|
|
11
|
-
const initial = filter.initSelected ?? null;
|
|
12
|
-
return JSON.stringify(actual) !== JSON.stringify(initial);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
return hasActiveSearch || hasSelectedFilters;
|
|
16
|
-
}
|
|
1
|
+
import type { CompleteFilterSelect } from './complete-filter.types';
|
|
2
|
+
|
|
3
|
+
/** Whether search text or any filter select differs from its initial value. */
|
|
4
|
+
export function hasActiveFilters(
|
|
5
|
+
searchQuery: string,
|
|
6
|
+
filtersSelect: CompleteFilterSelect[],
|
|
7
|
+
): boolean {
|
|
8
|
+
const hasActiveSearch = searchQuery?.trim().length > 0;
|
|
9
|
+
const hasSelectedFilters = filtersSelect.some((filter) => {
|
|
10
|
+
const actual = filter.selected;
|
|
11
|
+
const initial = filter.initSelected ?? null;
|
|
12
|
+
return JSON.stringify(actual) !== JSON.stringify(initial);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
return hasActiveSearch || hasSelectedFilters;
|
|
16
|
+
}
|