@aiao/rxdb-model-angular 0.0.18 → 0.0.28

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.
@@ -0,0 +1,3163 @@
1
+ import { UUID_RE, applyPopoverPosition, buildFieldTree, findNodeByValue, findOptionLabel, getDefaultOperatorRegistry, getDefaultValueForType, getDisplayNamePath, getInputType, parseCommaSeparatedInput } from "@aiao/rxdb-model";
2
+ import * as i0 from "@angular/core";
3
+ import { ChangeDetectionStrategy, Component, Directive, InjectionToken, Injector, ViewChild, ViewContainerRef, afterNextRender, computed, effect, inject, input, output, signal, untracked, viewChild, viewChildren } from "@angular/core";
4
+ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
5
+ import * as i1 from "@angular/common";
6
+ import { CommonModule, NgComponentOutlet } from "@angular/common";
7
+ import { LucideBraces, LucideCalendar, LucideChevronDown, LucideChevronRight, LucideDynamicIcon, LucideHash, LucideLink2, LucideList, LucideListChecks, LucideTable, LucideToggleLeft, LucideType } from "@lucide/angular";
8
+ import { ActiveDescendantKeyManager } from "@angular/cdk/a11y";
9
+ import * as i1$1 from "@angular/forms";
10
+ import { FormsModule } from "@angular/forms";
11
+ let _uid$1 = 0;
12
+ var TreeItemDirective = class TreeItemDirective {
13
+ _active = signal(false, ...ngDevMode ? [{ debugName: "_active" }] : /* istanbul ignore next */ []);
14
+ isActive = this._active.asReadonly();
15
+ setActiveStyles() {
16
+ this._active.set(true);
17
+ }
18
+ setInactiveStyles() {
19
+ this._active.set(false);
20
+ }
21
+ static ɵfac = i0.ɵɵngDeclareFactory({
22
+ minVersion: "12.0.0",
23
+ version: "22.2.3",
24
+ ngImport: i0,
25
+ type: TreeItemDirective,
26
+ deps: [],
27
+ target: i0.ɵɵFactoryTarget.Directive
28
+ });
29
+ static ɵdir = i0.ɵɵngDeclareDirective({
30
+ minVersion: "14.0.0",
31
+ version: "22.2.3",
32
+ type: TreeItemDirective,
33
+ isStandalone: true,
34
+ selector: "[rxdbTreeItem]",
35
+ exportAs: ["rxdbTreeItem"],
36
+ ngImport: i0
37
+ });
38
+ };
39
+ i0.ɵɵngDeclareClassMetadata({
40
+ minVersion: "12.0.0",
41
+ version: "22.2.3",
42
+ ngImport: i0,
43
+ type: TreeItemDirective,
44
+ decorators: [{
45
+ type: Directive,
46
+ args: [{
47
+ selector: "[rxdbTreeItem]",
48
+ standalone: true,
49
+ exportAs: "rxdbTreeItem"
50
+ }]
51
+ }]
52
+ });
53
+ var TreeSelectComponent = class TreeSelectComponent {
54
+ trigger = viewChild.required("trigger", ...ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []);
55
+ popoverEl = viewChild.required("popoverEl", ...ngDevMode ? [{ debugName: "popoverEl" }] : /* istanbul ignore next */ []);
56
+ filterInput = viewChild("filterInput", ...ngDevMode ? [{ debugName: "filterInput" }] : /* istanbul ignore next */ []);
57
+ treeItemDirs = viewChildren(TreeItemDirective, ...ngDevMode ? [{ debugName: "treeItemDirs" }] : /* istanbul ignore next */ []);
58
+ expandedPaths = signal(/* @__PURE__ */ new Set(), ...ngDevMode ? [{ debugName: "expandedPaths" }] : /* istanbul ignore next */ []);
59
+ keyManager;
60
+ _activeIdx = signal(-1, ...ngDevMode ? [{ debugName: "_activeIdx" }] : /* istanbul ignore next */ []);
61
+ uid = `rxdb-ts-${++_uid$1}`;
62
+ listId = `${this.uid}-list`;
63
+ filterText = signal("", ...ngDevMode ? [{ debugName: "filterText" }] : /* istanbul ignore next */ []);
64
+ ChevronDownIcon = LucideChevronDown;
65
+ ChevronRightIcon = LucideChevronRight;
66
+ typeIconMap = {
67
+ string: LucideType,
68
+ number: LucideHash,
69
+ boolean: LucideToggleLeft,
70
+ date: LucideCalendar,
71
+ array: LucideList,
72
+ object: LucideBraces,
73
+ relation: LucideLink2,
74
+ enum: LucideListChecks,
75
+ keyValue: LucideTable
76
+ };
77
+ typeColorMap = {
78
+ string: "text-success",
79
+ number: "text-warning",
80
+ boolean: "text-info",
81
+ date: "text-secondary",
82
+ array: "text-accent",
83
+ object: "text-base-content/60",
84
+ relation: "text-primary",
85
+ enum: "text-warning",
86
+ keyValue: "text-accent"
87
+ };
88
+ defaultTypeIcon = LucideType;
89
+ defaultTypeColor = "text-base-content/40";
90
+ nodes = input.required(...ngDevMode ? [{ debugName: "nodes" }] : /* istanbul ignore next */ []);
91
+ selected = input("", ...ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []);
92
+ placeholder = input("请选择", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
93
+ minWidth = input("14rem", ...ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []);
94
+ selectChange = output();
95
+ displayValue = computed(() => {
96
+ const sel = this.selected();
97
+ return sel ? getDisplayNamePath(this.nodes(), sel) || sel : this.placeholder();
98
+ }, ...ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []);
99
+ selectedNode = computed(() => {
100
+ const sel = this.selected();
101
+ return sel ? findNodeByValue(this.nodes(), sel) : null;
102
+ }, ...ngDevMode ? [{ debugName: "selectedNode" }] : /* istanbul ignore next */ []);
103
+ visibleItems = computed(() => {
104
+ const filter = this.filterText().toLowerCase().trim();
105
+ const expanded = this.expandedPaths();
106
+ return filter ? buildFilteredItems(this.nodes(), filter) : buildExpandedItems(this.nodes(), expanded, 0);
107
+ }, ...ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ []);
108
+ activeItemId = computed(() => {
109
+ const idx = this._activeIdx();
110
+ if (idx < 0) return null;
111
+ const vis = this.visibleItems();
112
+ return idx < vis.length ? `${this.uid}-item-${vis[idx].node.value}` : null;
113
+ }, ...ngDevMode ? [{ debugName: "activeItemId" }] : /* istanbul ignore next */ []);
114
+ constructor() {
115
+ const injector = inject(Injector);
116
+ this.keyManager = new ActiveDescendantKeyManager(this.treeItemDirs, injector).withWrap().withHomeAndEnd();
117
+ this.keyManager.change.pipe(takeUntilDestroyed()).subscribe((idx) => this._activeIdx.set(idx));
118
+ effect(() => {
119
+ this.filterText();
120
+ untracked(() => {
121
+ setTimeout(() => {
122
+ this.keyManager.setFirstItemActive();
123
+ this._activeIdx.set(this.keyManager.activeItemIndex ?? -1);
124
+ });
125
+ });
126
+ });
127
+ }
128
+ onBeforeToggle(event) {
129
+ if (event.newState === "open") {
130
+ applyPopoverPosition(this.popoverEl().nativeElement, this.trigger().nativeElement.getBoundingClientRect());
131
+ this.filterText.set("");
132
+ } else {
133
+ this._activeIdx.set(-1);
134
+ this.keyManager.setActiveItem(-1);
135
+ }
136
+ }
137
+ onToggle(event) {
138
+ if (event.newState === "open") {
139
+ this.filterInput()?.nativeElement.focus();
140
+ setTimeout(() => {
141
+ this.keyManager.setFirstItemActive();
142
+ this._activeIdx.set(this.keyManager.activeItemIndex ?? -1);
143
+ });
144
+ }
145
+ }
146
+ onKeydown(event) {
147
+ switch (event.key) {
148
+ case "ArrowDown":
149
+ case "ArrowUp":
150
+ event.preventDefault();
151
+ this.keyManager.onKeydown(event);
152
+ this.scrollActiveIntoView();
153
+ break;
154
+ case "ArrowRight": {
155
+ event.preventDefault();
156
+ const item = this.visibleItems()[this.keyManager.activeItemIndex ?? -1];
157
+ if (item?.isExpandable && !item.isExpanded) this.toggleExpand(item.node.value);
158
+ break;
159
+ }
160
+ case "ArrowLeft": {
161
+ event.preventDefault();
162
+ const item = this.visibleItems()[this.keyManager.activeItemIndex ?? -1];
163
+ if (item?.isExpandable && item.isExpanded) this.toggleExpand(item.node.value);
164
+ break;
165
+ }
166
+ case "Enter": {
167
+ event.preventDefault();
168
+ const item = this.visibleItems()[this.keyManager.activeItemIndex ?? -1];
169
+ if (item) this.onItemClick(item);
170
+ break;
171
+ }
172
+ case "Escape":
173
+ event.preventDefault();
174
+ this.popoverEl().nativeElement.hidePopover();
175
+ this.trigger().nativeElement.focus();
176
+ break;
177
+ case "Tab": this.popoverEl().nativeElement.hidePopover();
178
+ }
179
+ }
180
+ onItemClick(item) {
181
+ if (item.isExpandable) this.toggleExpand(item.node.value);
182
+ else {
183
+ this.selectChange.emit(item.node.value);
184
+ this.popoverEl().nativeElement.hidePopover();
185
+ this.trigger().nativeElement.focus();
186
+ }
187
+ }
188
+ trackItem(item) {
189
+ return item.node.isExistsOption ? `${item.node.value}:exists` : item.node.value;
190
+ }
191
+ scrollActiveIntoView() {
192
+ const idx = this.keyManager.activeItemIndex ?? -1;
193
+ const item = this.visibleItems()[idx];
194
+ if (!item) return;
195
+ document.getElementById(`${this.uid}-item-${item.node.value}`)?.scrollIntoView({ block: "nearest" });
196
+ }
197
+ toggleExpand(value) {
198
+ this.expandedPaths.update((prev) => {
199
+ const next = new Set(prev);
200
+ if (next.has(value)) next.delete(value);
201
+ else next.add(value);
202
+ return next;
203
+ });
204
+ }
205
+ static ɵfac = i0.ɵɵngDeclareFactory({
206
+ minVersion: "12.0.0",
207
+ version: "22.2.3",
208
+ ngImport: i0,
209
+ type: TreeSelectComponent,
210
+ deps: [],
211
+ target: i0.ɵɵFactoryTarget.Component
212
+ });
213
+ static ɵcmp = i0.ɵɵngDeclareComponent({
214
+ minVersion: "17.0.0",
215
+ version: "22.2.3",
216
+ type: TreeSelectComponent,
217
+ isStandalone: true,
218
+ selector: "rxdb-tree-select",
219
+ inputs: {
220
+ nodes: {
221
+ classPropertyName: "nodes",
222
+ publicName: "nodes",
223
+ isSignal: true,
224
+ isRequired: true,
225
+ transformFunction: null
226
+ },
227
+ selected: {
228
+ classPropertyName: "selected",
229
+ publicName: "selected",
230
+ isSignal: true,
231
+ isRequired: false,
232
+ transformFunction: null
233
+ },
234
+ placeholder: {
235
+ classPropertyName: "placeholder",
236
+ publicName: "placeholder",
237
+ isSignal: true,
238
+ isRequired: false,
239
+ transformFunction: null
240
+ },
241
+ minWidth: {
242
+ classPropertyName: "minWidth",
243
+ publicName: "minWidth",
244
+ isSignal: true,
245
+ isRequired: false,
246
+ transformFunction: null
247
+ }
248
+ },
249
+ outputs: { selectChange: "selectChange" },
250
+ viewQueries: [
251
+ {
252
+ propertyName: "trigger",
253
+ first: true,
254
+ predicate: ["trigger"],
255
+ descendants: true,
256
+ isSignal: true
257
+ },
258
+ {
259
+ propertyName: "popoverEl",
260
+ first: true,
261
+ predicate: ["popoverEl"],
262
+ descendants: true,
263
+ isSignal: true
264
+ },
265
+ {
266
+ propertyName: "filterInput",
267
+ first: true,
268
+ predicate: ["filterInput"],
269
+ descendants: true,
270
+ isSignal: true
271
+ },
272
+ {
273
+ propertyName: "treeItemDirs",
274
+ predicate: TreeItemDirective,
275
+ descendants: true,
276
+ isSignal: true
277
+ }
278
+ ],
279
+ ngImport: i0,
280
+ template: `
281
+ <!-- 触发按钮 -->
282
+ <button
283
+ class="select select-sm text-left"
284
+ #trigger
285
+ [attr.popovertarget]="uid"
286
+ [style.min-width]="minWidth()"
287
+ aria-haspopup="tree"
288
+ type="button"
289
+ >
290
+ @if (selectedNode(); as node) {
291
+ <svg
292
+ class="w-3.5 shrink-0"
293
+ [class]="typeColorMap[node.type] ?? defaultTypeColor"
294
+ [lucideIcon]="typeIconMap[node.type] ?? defaultTypeIcon"
295
+ size="14"
296
+ ></svg>
297
+ }
298
+ <span class="min-w-0 grow truncate">{{ displayValue() }}</span>
299
+ </button>
300
+
301
+ <!-- Popover 浮层 -->
302
+ <div
303
+ class="bg-base-100 text-base-content rounded-box border-base-300 border shadow-xl"
304
+ #popoverEl
305
+ [id]="uid"
306
+ [style.min-width]="minWidth()"
307
+ (beforetoggle)="onBeforeToggle($event)"
308
+ (keydown)="onKeydown($event)"
309
+ (toggle)="onToggle($event)"
310
+ popover
311
+ tabindex="-1"
312
+ >
313
+ <!-- 过滤输入框 -->
314
+ <div class="mb-1 p-1">
315
+ <input
316
+ class="input input-sm w-full"
317
+ #filterInput
318
+ [attr.aria-activedescendant]="activeItemId()"
319
+ [attr.aria-controls]="listId"
320
+ [attr.aria-expanded]="true"
321
+ [attr.aria-owns]="listId"
322
+ [ngModel]="filterText()"
323
+ (ngModelChange)="filterText.set($event)"
324
+ aria-autocomplete="list"
325
+ placeholder="搜索字段..."
326
+ role="combobox"
327
+ type="text"
328
+ />
329
+ </div>
330
+
331
+ <!-- 树形列表 -->
332
+ <ul
333
+ class="menu menu-sm max-h-60 w-full flex-nowrap overflow-y-auto px-2"
334
+ [attr.aria-label]="placeholder()"
335
+ [id]="listId"
336
+ role="tree"
337
+ >
338
+ @for (item of visibleItems(); track trackItem(item)) {
339
+ <li [style.padding-left.rem]="item.depth * 0.75" role="none">
340
+ <button
341
+ class="flex w-full items-center gap-1.5 text-left text-sm"
342
+ #treeItemRef="rxdbTreeItem"
343
+ [attr.aria-expanded]="item.isExpandable ? item.isExpanded : null"
344
+ [attr.aria-selected]="!item.isExpandable ? item.node.value === selected() : null"
345
+ [class.bg-primary/15]="!item.isExpandable && item.node.value === selected()"
346
+ [class.font-semibold]="!item.isExpandable && item.node.value === selected()"
347
+ [class.menu-focus]="treeItemRef.isActive()"
348
+ [id]="uid + '-item-' + item.node.value"
349
+ (click)="onItemClick(item)"
350
+ role="treeitem"
351
+ rxdbTreeItem
352
+ type="button"
353
+ >
354
+ <svg
355
+ class="w-3.5 shrink-0"
356
+ [class]="typeColorMap[item.node.type] ?? defaultTypeColor"
357
+ [lucideIcon]="typeIconMap[item.node.type] ?? defaultTypeIcon"
358
+ size="14"
359
+ ></svg>
360
+ <span class="min-w-0 grow truncate">{{ item.node.displayName || item.node.label }}</span>
361
+ @if (!item.isExpandable && item.node.value === selected()) {
362
+ <svg
363
+ class="text-primary h-3 w-3 shrink-0"
364
+ fill="none"
365
+ stroke="currentColor"
366
+ stroke-linecap="round"
367
+ stroke-linejoin="round"
368
+ stroke-width="2.5"
369
+ viewBox="0 0 24 24"
370
+ xmlns="http://www.w3.org/2000/svg"
371
+ >
372
+ <polyline points="20 6 9 17 4 12" />
373
+ </svg>
374
+ }
375
+ @if (item.isExpandable) {
376
+ <span class="shrink-0 text-xs opacity-40">{{ item.node.children!.length }}</span>
377
+ @if (item.isExpanded) {
378
+ <svg class="w-3.5 shrink-0 opacity-50" [lucideIcon]="ChevronDownIcon" size="14"></svg>
379
+ } @else {
380
+ <svg class="w-3.5 shrink-0 opacity-50" [lucideIcon]="ChevronRightIcon" size="14"></svg>
381
+ }
382
+ }
383
+ </button>
384
+ </li>
385
+ } @empty {
386
+ <li class="px-3 py-1.5 text-sm opacity-50">无匹配字段</li>
387
+ }
388
+ </ul>
389
+ </div>
390
+ `,
391
+ isInline: true,
392
+ dependencies: [
393
+ {
394
+ kind: "ngmodule",
395
+ type: FormsModule
396
+ },
397
+ {
398
+ kind: "directive",
399
+ type: i1$1.DefaultValueAccessor,
400
+ selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"
401
+ },
402
+ {
403
+ kind: "directive",
404
+ type: i1$1.NgControlStatus,
405
+ selector: "[formControlName],[ngModel],[formControl]"
406
+ },
407
+ {
408
+ kind: "directive",
409
+ type: i1$1.NgModel,
410
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
411
+ inputs: [
412
+ "name",
413
+ "disabled",
414
+ "ngModel",
415
+ "ngModelOptions"
416
+ ],
417
+ outputs: ["ngModelChange"],
418
+ exportAs: ["ngModel"]
419
+ },
420
+ {
421
+ kind: "directive",
422
+ type: TreeItemDirective,
423
+ selector: "[rxdbTreeItem]",
424
+ exportAs: ["rxdbTreeItem"]
425
+ },
426
+ {
427
+ kind: "component",
428
+ type: LucideDynamicIcon,
429
+ selector: "svg[lucideIcon]",
430
+ inputs: ["lucideIcon"]
431
+ }
432
+ ],
433
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
434
+ });
435
+ };
436
+ i0.ɵɵngDeclareClassMetadata({
437
+ minVersion: "12.0.0",
438
+ version: "22.2.3",
439
+ ngImport: i0,
440
+ type: TreeSelectComponent,
441
+ decorators: [{
442
+ type: Component,
443
+ args: [{
444
+ selector: "rxdb-tree-select",
445
+ standalone: true,
446
+ imports: [
447
+ FormsModule,
448
+ TreeItemDirective,
449
+ LucideDynamicIcon
450
+ ],
451
+ changeDetection: ChangeDetectionStrategy.OnPush,
452
+ template: `
453
+ <!-- 触发按钮 -->
454
+ <button
455
+ class="select select-sm text-left"
456
+ #trigger
457
+ [attr.popovertarget]="uid"
458
+ [style.min-width]="minWidth()"
459
+ aria-haspopup="tree"
460
+ type="button"
461
+ >
462
+ @if (selectedNode(); as node) {
463
+ <svg
464
+ class="w-3.5 shrink-0"
465
+ [class]="typeColorMap[node.type] ?? defaultTypeColor"
466
+ [lucideIcon]="typeIconMap[node.type] ?? defaultTypeIcon"
467
+ size="14"
468
+ ></svg>
469
+ }
470
+ <span class="min-w-0 grow truncate">{{ displayValue() }}</span>
471
+ </button>
472
+
473
+ <!-- Popover 浮层 -->
474
+ <div
475
+ class="bg-base-100 text-base-content rounded-box border-base-300 border shadow-xl"
476
+ #popoverEl
477
+ [id]="uid"
478
+ [style.min-width]="minWidth()"
479
+ (beforetoggle)="onBeforeToggle($event)"
480
+ (keydown)="onKeydown($event)"
481
+ (toggle)="onToggle($event)"
482
+ popover
483
+ tabindex="-1"
484
+ >
485
+ <!-- 过滤输入框 -->
486
+ <div class="mb-1 p-1">
487
+ <input
488
+ class="input input-sm w-full"
489
+ #filterInput
490
+ [attr.aria-activedescendant]="activeItemId()"
491
+ [attr.aria-controls]="listId"
492
+ [attr.aria-expanded]="true"
493
+ [attr.aria-owns]="listId"
494
+ [ngModel]="filterText()"
495
+ (ngModelChange)="filterText.set($event)"
496
+ aria-autocomplete="list"
497
+ placeholder="搜索字段..."
498
+ role="combobox"
499
+ type="text"
500
+ />
501
+ </div>
502
+
503
+ <!-- 树形列表 -->
504
+ <ul
505
+ class="menu menu-sm max-h-60 w-full flex-nowrap overflow-y-auto px-2"
506
+ [attr.aria-label]="placeholder()"
507
+ [id]="listId"
508
+ role="tree"
509
+ >
510
+ @for (item of visibleItems(); track trackItem(item)) {
511
+ <li [style.padding-left.rem]="item.depth * 0.75" role="none">
512
+ <button
513
+ class="flex w-full items-center gap-1.5 text-left text-sm"
514
+ #treeItemRef="rxdbTreeItem"
515
+ [attr.aria-expanded]="item.isExpandable ? item.isExpanded : null"
516
+ [attr.aria-selected]="!item.isExpandable ? item.node.value === selected() : null"
517
+ [class.bg-primary/15]="!item.isExpandable && item.node.value === selected()"
518
+ [class.font-semibold]="!item.isExpandable && item.node.value === selected()"
519
+ [class.menu-focus]="treeItemRef.isActive()"
520
+ [id]="uid + '-item-' + item.node.value"
521
+ (click)="onItemClick(item)"
522
+ role="treeitem"
523
+ rxdbTreeItem
524
+ type="button"
525
+ >
526
+ <svg
527
+ class="w-3.5 shrink-0"
528
+ [class]="typeColorMap[item.node.type] ?? defaultTypeColor"
529
+ [lucideIcon]="typeIconMap[item.node.type] ?? defaultTypeIcon"
530
+ size="14"
531
+ ></svg>
532
+ <span class="min-w-0 grow truncate">{{ item.node.displayName || item.node.label }}</span>
533
+ @if (!item.isExpandable && item.node.value === selected()) {
534
+ <svg
535
+ class="text-primary h-3 w-3 shrink-0"
536
+ fill="none"
537
+ stroke="currentColor"
538
+ stroke-linecap="round"
539
+ stroke-linejoin="round"
540
+ stroke-width="2.5"
541
+ viewBox="0 0 24 24"
542
+ xmlns="http://www.w3.org/2000/svg"
543
+ >
544
+ <polyline points="20 6 9 17 4 12" />
545
+ </svg>
546
+ }
547
+ @if (item.isExpandable) {
548
+ <span class="shrink-0 text-xs opacity-40">{{ item.node.children!.length }}</span>
549
+ @if (item.isExpanded) {
550
+ <svg class="w-3.5 shrink-0 opacity-50" [lucideIcon]="ChevronDownIcon" size="14"></svg>
551
+ } @else {
552
+ <svg class="w-3.5 shrink-0 opacity-50" [lucideIcon]="ChevronRightIcon" size="14"></svg>
553
+ }
554
+ }
555
+ </button>
556
+ </li>
557
+ } @empty {
558
+ <li class="px-3 py-1.5 text-sm opacity-50">无匹配字段</li>
559
+ }
560
+ </ul>
561
+ </div>
562
+ `
563
+ }]
564
+ }],
565
+ ctorParameters: () => [],
566
+ propDecorators: {
567
+ trigger: [{
568
+ type: i0.ViewChild,
569
+ args: ["trigger", { isSignal: true }]
570
+ }],
571
+ popoverEl: [{
572
+ type: i0.ViewChild,
573
+ args: ["popoverEl", { isSignal: true }]
574
+ }],
575
+ filterInput: [{
576
+ type: i0.ViewChild,
577
+ args: ["filterInput", { isSignal: true }]
578
+ }],
579
+ treeItemDirs: [{
580
+ type: i0.ViewChildren,
581
+ args: [i0.forwardRef(() => TreeItemDirective), { isSignal: true }]
582
+ }],
583
+ nodes: [{
584
+ type: i0.Input,
585
+ args: [{
586
+ isSignal: true,
587
+ alias: "nodes",
588
+ required: true
589
+ }]
590
+ }],
591
+ selected: [{
592
+ type: i0.Input,
593
+ args: [{
594
+ isSignal: true,
595
+ alias: "selected",
596
+ required: false
597
+ }]
598
+ }],
599
+ placeholder: [{
600
+ type: i0.Input,
601
+ args: [{
602
+ isSignal: true,
603
+ alias: "placeholder",
604
+ required: false
605
+ }]
606
+ }],
607
+ minWidth: [{
608
+ type: i0.Input,
609
+ args: [{
610
+ isSignal: true,
611
+ alias: "minWidth",
612
+ required: false
613
+ }]
614
+ }],
615
+ selectChange: [{
616
+ type: i0.Output,
617
+ args: ["selectChange"]
618
+ }]
619
+ }
620
+ });
621
+ function buildExpandedItems(nodes, expanded, depth) {
622
+ const result = [];
623
+ for (const node of nodes) {
624
+ const isExpandable = !!node.children?.length;
625
+ const isExpanded = isExpandable && expanded.has(node.value);
626
+ result.push({
627
+ node,
628
+ depth,
629
+ isExpandable,
630
+ isExpanded
631
+ });
632
+ if (isExpanded) result.push(...buildExpandedItems(node.children, expanded, depth + 1));
633
+ }
634
+ return result;
635
+ }
636
+ function buildFilteredItems(nodes, filter, depth = 0) {
637
+ const result = [];
638
+ for (const node of nodes) {
639
+ const selfMatches = nodeMatchesFilter(node, filter);
640
+ const isExpandable = !!node.children?.length;
641
+ const childMatches = isExpandable && hasMatchingDescendant(node.children, filter);
642
+ if (selfMatches || childMatches) {
643
+ result.push({
644
+ node,
645
+ depth,
646
+ isExpandable,
647
+ isExpanded: isExpandable && childMatches
648
+ });
649
+ if (isExpandable && (selfMatches || childMatches)) result.push(...buildFilteredItems(node.children, filter, depth + 1));
650
+ }
651
+ }
652
+ return result;
653
+ }
654
+ function nodeMatchesFilter(node, filter) {
655
+ return node.label.toLowerCase().includes(filter) || node.displayName.toLowerCase().includes(filter) || (node.description?.toLowerCase().includes(filter) ?? false);
656
+ }
657
+ function hasMatchingDescendant(nodes, filter) {
658
+ return nodes.some((n) => nodeMatchesFilter(n, filter) || (n.children?.length ? hasMatchingDescendant(n.children, filter) : false));
659
+ }
660
+ var FieldSelectorComponent = class FieldSelectorComponent {
661
+ fields = input.required(...ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []);
662
+ selectedField = input("", ...ngDevMode ? [{ debugName: "selectedField" }] : /* istanbul ignore next */ []);
663
+ placeholder = input("选择字段", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
664
+ fieldChangeFn = input(void 0, ...ngDevMode ? [{ debugName: "fieldChangeFn" }] : /* istanbul ignore next */ []);
665
+ fieldChange = output();
666
+ fieldTree = computed(() => buildFieldTree(this.fields()), ...ngDevMode ? [{ debugName: "fieldTree" }] : /* istanbul ignore next */ []);
667
+ onSelect(field) {
668
+ this.fieldChange.emit(field);
669
+ this.fieldChangeFn()?.(field);
670
+ }
671
+ static ɵfac = i0.ɵɵngDeclareFactory({
672
+ minVersion: "12.0.0",
673
+ version: "22.2.3",
674
+ ngImport: i0,
675
+ type: FieldSelectorComponent,
676
+ deps: [],
677
+ target: i0.ɵɵFactoryTarget.Component
678
+ });
679
+ static ɵcmp = i0.ɵɵngDeclareComponent({
680
+ minVersion: "17.1.0",
681
+ version: "22.2.3",
682
+ type: FieldSelectorComponent,
683
+ isStandalone: true,
684
+ selector: "rxdb-field-selector",
685
+ inputs: {
686
+ fields: {
687
+ classPropertyName: "fields",
688
+ publicName: "fields",
689
+ isSignal: true,
690
+ isRequired: true,
691
+ transformFunction: null
692
+ },
693
+ selectedField: {
694
+ classPropertyName: "selectedField",
695
+ publicName: "selectedField",
696
+ isSignal: true,
697
+ isRequired: false,
698
+ transformFunction: null
699
+ },
700
+ placeholder: {
701
+ classPropertyName: "placeholder",
702
+ publicName: "placeholder",
703
+ isSignal: true,
704
+ isRequired: false,
705
+ transformFunction: null
706
+ },
707
+ fieldChangeFn: {
708
+ classPropertyName: "fieldChangeFn",
709
+ publicName: "fieldChangeFn",
710
+ isSignal: true,
711
+ isRequired: false,
712
+ transformFunction: null
713
+ }
714
+ },
715
+ outputs: { fieldChange: "fieldChange" },
716
+ ngImport: i0,
717
+ template: `
718
+ <rxdb-tree-select
719
+ [nodes]="fieldTree()"
720
+ [placeholder]="placeholder()"
721
+ [selected]="selectedField()"
722
+ (selectChange)="onSelect($event)"
723
+ minWidth="14rem"
724
+ />
725
+ `,
726
+ isInline: true,
727
+ dependencies: [{
728
+ kind: "component",
729
+ type: TreeSelectComponent,
730
+ selector: "rxdb-tree-select",
731
+ inputs: [
732
+ "nodes",
733
+ "selected",
734
+ "placeholder",
735
+ "minWidth"
736
+ ],
737
+ outputs: ["selectChange"]
738
+ }],
739
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
740
+ });
741
+ };
742
+ i0.ɵɵngDeclareClassMetadata({
743
+ minVersion: "12.0.0",
744
+ version: "22.2.3",
745
+ ngImport: i0,
746
+ type: FieldSelectorComponent,
747
+ decorators: [{
748
+ type: Component,
749
+ args: [{
750
+ selector: "rxdb-field-selector",
751
+ standalone: true,
752
+ imports: [TreeSelectComponent],
753
+ template: `
754
+ <rxdb-tree-select
755
+ [nodes]="fieldTree()"
756
+ [placeholder]="placeholder()"
757
+ [selected]="selectedField()"
758
+ (selectChange)="onSelect($event)"
759
+ minWidth="14rem"
760
+ />
761
+ `,
762
+ changeDetection: ChangeDetectionStrategy.OnPush
763
+ }]
764
+ }],
765
+ propDecorators: {
766
+ fields: [{
767
+ type: i0.Input,
768
+ args: [{
769
+ isSignal: true,
770
+ alias: "fields",
771
+ required: true
772
+ }]
773
+ }],
774
+ selectedField: [{
775
+ type: i0.Input,
776
+ args: [{
777
+ isSignal: true,
778
+ alias: "selectedField",
779
+ required: false
780
+ }]
781
+ }],
782
+ placeholder: [{
783
+ type: i0.Input,
784
+ args: [{
785
+ isSignal: true,
786
+ alias: "placeholder",
787
+ required: false
788
+ }]
789
+ }],
790
+ fieldChangeFn: [{
791
+ type: i0.Input,
792
+ args: [{
793
+ isSignal: true,
794
+ alias: "fieldChangeFn",
795
+ required: false
796
+ }]
797
+ }],
798
+ fieldChange: [{
799
+ type: i0.Output,
800
+ args: ["fieldChange"]
801
+ }]
802
+ }
803
+ });
804
+ let _uid = 0;
805
+ var PopoverSelectComponent = class PopoverSelectComponent {
806
+ trigger = viewChild.required("trigger", ...ngDevMode ? [{ debugName: "trigger" }] : /* istanbul ignore next */ []);
807
+ popoverEl = viewChild.required("popoverEl", ...ngDevMode ? [{ debugName: "popoverEl" }] : /* istanbul ignore next */ []);
808
+ filterInput = viewChild("filterInput", ...ngDevMode ? [{ debugName: "filterInput" }] : /* istanbul ignore next */ []);
809
+ uid = `rxdb-ps-${++_uid}`;
810
+ listId = `${this.uid}-list`;
811
+ filterText = signal("", ...ngDevMode ? [{ debugName: "filterText" }] : /* istanbul ignore next */ []);
812
+ activeIndex = signal(0, ...ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []);
813
+ options = input.required(...ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []);
814
+ selected = input("", ...ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []);
815
+ placeholder = input("请选择", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
816
+ minWidth = input("8rem", ...ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []);
817
+ selectChange = output();
818
+ currentLabel = computed(() => findOptionLabel(this.options(), this.selected(), this.placeholder()), ...ngDevMode ? [{ debugName: "currentLabel" }] : /* istanbul ignore next */ []);
819
+ filteredOptions = computed(() => {
820
+ const filter = this.filterText().toLowerCase().trim();
821
+ if (!filter) return this.options();
822
+ return this.options().filter((opt) => opt.label.toLowerCase().includes(filter));
823
+ }, ...ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []);
824
+ activeItemId = computed(() => {
825
+ const idx = this.activeIndex();
826
+ const opts = this.filteredOptions();
827
+ return idx >= 0 && idx < opts.length ? `${this.uid}-opt-${opts[idx].value}` : null;
828
+ }, ...ngDevMode ? [{ debugName: "activeItemId" }] : /* istanbul ignore next */ []);
829
+ constructor() {
830
+ effect(() => {
831
+ this.filteredOptions();
832
+ untracked(() => this.activeIndex.set(0));
833
+ });
834
+ }
835
+ onBeforeToggle(event) {
836
+ if (event.newState === "open") {
837
+ applyPopoverPosition(this.popoverEl().nativeElement, this.trigger().nativeElement.getBoundingClientRect());
838
+ this.filterText.set("");
839
+ }
840
+ }
841
+ onToggle(event) {
842
+ if (event.newState === "open") {
843
+ this.filterInput()?.nativeElement.focus();
844
+ this.activeIndex.set(0);
845
+ }
846
+ }
847
+ onKeydown(event) {
848
+ const opts = this.filteredOptions();
849
+ const len = opts.length;
850
+ switch (event.key) {
851
+ case "ArrowDown":
852
+ event.preventDefault();
853
+ if (len > 0) {
854
+ this.activeIndex.update((i) => (i + 1) % len);
855
+ this.scrollActiveIntoView();
856
+ }
857
+ break;
858
+ case "ArrowUp":
859
+ event.preventDefault();
860
+ if (len > 0) {
861
+ this.activeIndex.update((i) => (i - 1 + len) % len);
862
+ this.scrollActiveIntoView();
863
+ }
864
+ break;
865
+ case "Enter": {
866
+ event.preventDefault();
867
+ const opt = opts[this.activeIndex()];
868
+ if (opt) this.onSelect(opt.value);
869
+ break;
870
+ }
871
+ case "Escape":
872
+ event.preventDefault();
873
+ this.popoverEl().nativeElement.hidePopover();
874
+ this.trigger().nativeElement.focus();
875
+ break;
876
+ case "Tab": this.popoverEl().nativeElement.hidePopover();
877
+ }
878
+ }
879
+ onSelect(value) {
880
+ this.selectChange.emit(value);
881
+ this.popoverEl().nativeElement.hidePopover();
882
+ this.trigger().nativeElement.focus();
883
+ }
884
+ scrollActiveIntoView() {
885
+ const opt = this.filteredOptions()[this.activeIndex()];
886
+ if (!opt) return;
887
+ document.getElementById(`${this.uid}-opt-${opt.value}`)?.scrollIntoView({ block: "nearest" });
888
+ }
889
+ static ɵfac = i0.ɵɵngDeclareFactory({
890
+ minVersion: "12.0.0",
891
+ version: "22.2.3",
892
+ ngImport: i0,
893
+ type: PopoverSelectComponent,
894
+ deps: [],
895
+ target: i0.ɵɵFactoryTarget.Component
896
+ });
897
+ static ɵcmp = i0.ɵɵngDeclareComponent({
898
+ minVersion: "17.0.0",
899
+ version: "22.2.3",
900
+ type: PopoverSelectComponent,
901
+ isStandalone: true,
902
+ selector: "rxdb-popover-select",
903
+ inputs: {
904
+ options: {
905
+ classPropertyName: "options",
906
+ publicName: "options",
907
+ isSignal: true,
908
+ isRequired: true,
909
+ transformFunction: null
910
+ },
911
+ selected: {
912
+ classPropertyName: "selected",
913
+ publicName: "selected",
914
+ isSignal: true,
915
+ isRequired: false,
916
+ transformFunction: null
917
+ },
918
+ placeholder: {
919
+ classPropertyName: "placeholder",
920
+ publicName: "placeholder",
921
+ isSignal: true,
922
+ isRequired: false,
923
+ transformFunction: null
924
+ },
925
+ minWidth: {
926
+ classPropertyName: "minWidth",
927
+ publicName: "minWidth",
928
+ isSignal: true,
929
+ isRequired: false,
930
+ transformFunction: null
931
+ }
932
+ },
933
+ outputs: { selectChange: "selectChange" },
934
+ viewQueries: [
935
+ {
936
+ propertyName: "trigger",
937
+ first: true,
938
+ predicate: ["trigger"],
939
+ descendants: true,
940
+ isSignal: true
941
+ },
942
+ {
943
+ propertyName: "popoverEl",
944
+ first: true,
945
+ predicate: ["popoverEl"],
946
+ descendants: true,
947
+ isSignal: true
948
+ },
949
+ {
950
+ propertyName: "filterInput",
951
+ first: true,
952
+ predicate: ["filterInput"],
953
+ descendants: true,
954
+ isSignal: true
955
+ }
956
+ ],
957
+ ngImport: i0,
958
+ template: `
959
+ <button
960
+ class="select select-sm text-left"
961
+ #trigger
962
+ [attr.popovertarget]="uid"
963
+ [style.min-width]="minWidth()"
964
+ type="button"
965
+ >
966
+ {{ currentLabel() }}
967
+ </button>
968
+ <div
969
+ class="bg-base-100 rounded-box border-base-300 border shadow-xl"
970
+ #popoverEl
971
+ [id]="uid"
972
+ [style.min-width]="minWidth()"
973
+ (beforetoggle)="onBeforeToggle($event)"
974
+ (keydown)="onKeydown($event)"
975
+ (toggle)="onToggle($event)"
976
+ popover
977
+ tabindex="-1"
978
+ >
979
+ <div class="mb-1 p-2">
980
+ <input
981
+ class="input input-sm w-full"
982
+ #filterInput
983
+ [attr.aria-activedescendant]="activeItemId()"
984
+ [attr.aria-controls]="listId"
985
+ [attr.aria-expanded]="true"
986
+ [ngModel]="filterText()"
987
+ (ngModelChange)="filterText.set($event)"
988
+ aria-autocomplete="list"
989
+ placeholder="搜索..."
990
+ role="combobox"
991
+ type="text"
992
+ />
993
+ </div>
994
+ <ul class="menu menu-sm max-h-60 w-full flex-nowrap overflow-y-auto px-2" [id]="listId" role="listbox">
995
+ @for (option of filteredOptions(); track option.value; let i = $index) {
996
+ <li role="none">
997
+ <button
998
+ class="flex w-full items-center text-left text-sm"
999
+ [attr.aria-selected]="option.value === selected()"
1000
+ [class.bg-primary/15]="option.value === selected()"
1001
+ [class.font-semibold]="option.value === selected()"
1002
+ [class.menu-focus]="activeIndex() === i"
1003
+ [id]="uid + '-opt-' + option.value"
1004
+ (click)="onSelect(option.value)"
1005
+ role="option"
1006
+ type="button"
1007
+ >
1008
+ <span class="grow">{{ option.label }}</span>
1009
+ @if (option.value === selected()) {
1010
+ <svg
1011
+ class="text-primary h-3 w-3 shrink-0"
1012
+ fill="none"
1013
+ stroke="currentColor"
1014
+ stroke-linecap="round"
1015
+ stroke-linejoin="round"
1016
+ stroke-width="2.5"
1017
+ viewBox="0 0 24 24"
1018
+ xmlns="http://www.w3.org/2000/svg"
1019
+ >
1020
+ <polyline points="20 6 9 17 4 12" />
1021
+ </svg>
1022
+ }
1023
+ </button>
1024
+ </li>
1025
+ } @empty {
1026
+ <li class="px-3 py-1.5 text-sm opacity-50">无匹配项</li>
1027
+ }
1028
+ </ul>
1029
+ </div>
1030
+ `,
1031
+ isInline: true,
1032
+ dependencies: [
1033
+ {
1034
+ kind: "ngmodule",
1035
+ type: FormsModule
1036
+ },
1037
+ {
1038
+ kind: "directive",
1039
+ type: i1$1.DefaultValueAccessor,
1040
+ selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"
1041
+ },
1042
+ {
1043
+ kind: "directive",
1044
+ type: i1$1.NgControlStatus,
1045
+ selector: "[formControlName],[ngModel],[formControl]"
1046
+ },
1047
+ {
1048
+ kind: "directive",
1049
+ type: i1$1.NgModel,
1050
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
1051
+ inputs: [
1052
+ "name",
1053
+ "disabled",
1054
+ "ngModel",
1055
+ "ngModelOptions"
1056
+ ],
1057
+ outputs: ["ngModelChange"],
1058
+ exportAs: ["ngModel"]
1059
+ }
1060
+ ],
1061
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
1062
+ });
1063
+ };
1064
+ i0.ɵɵngDeclareClassMetadata({
1065
+ minVersion: "12.0.0",
1066
+ version: "22.2.3",
1067
+ ngImport: i0,
1068
+ type: PopoverSelectComponent,
1069
+ decorators: [{
1070
+ type: Component,
1071
+ args: [{
1072
+ selector: "rxdb-popover-select",
1073
+ standalone: true,
1074
+ imports: [FormsModule],
1075
+ template: `
1076
+ <button
1077
+ class="select select-sm text-left"
1078
+ #trigger
1079
+ [attr.popovertarget]="uid"
1080
+ [style.min-width]="minWidth()"
1081
+ type="button"
1082
+ >
1083
+ {{ currentLabel() }}
1084
+ </button>
1085
+ <div
1086
+ class="bg-base-100 rounded-box border-base-300 border shadow-xl"
1087
+ #popoverEl
1088
+ [id]="uid"
1089
+ [style.min-width]="minWidth()"
1090
+ (beforetoggle)="onBeforeToggle($event)"
1091
+ (keydown)="onKeydown($event)"
1092
+ (toggle)="onToggle($event)"
1093
+ popover
1094
+ tabindex="-1"
1095
+ >
1096
+ <div class="mb-1 p-2">
1097
+ <input
1098
+ class="input input-sm w-full"
1099
+ #filterInput
1100
+ [attr.aria-activedescendant]="activeItemId()"
1101
+ [attr.aria-controls]="listId"
1102
+ [attr.aria-expanded]="true"
1103
+ [ngModel]="filterText()"
1104
+ (ngModelChange)="filterText.set($event)"
1105
+ aria-autocomplete="list"
1106
+ placeholder="搜索..."
1107
+ role="combobox"
1108
+ type="text"
1109
+ />
1110
+ </div>
1111
+ <ul class="menu menu-sm max-h-60 w-full flex-nowrap overflow-y-auto px-2" [id]="listId" role="listbox">
1112
+ @for (option of filteredOptions(); track option.value; let i = $index) {
1113
+ <li role="none">
1114
+ <button
1115
+ class="flex w-full items-center text-left text-sm"
1116
+ [attr.aria-selected]="option.value === selected()"
1117
+ [class.bg-primary/15]="option.value === selected()"
1118
+ [class.font-semibold]="option.value === selected()"
1119
+ [class.menu-focus]="activeIndex() === i"
1120
+ [id]="uid + '-opt-' + option.value"
1121
+ (click)="onSelect(option.value)"
1122
+ role="option"
1123
+ type="button"
1124
+ >
1125
+ <span class="grow">{{ option.label }}</span>
1126
+ @if (option.value === selected()) {
1127
+ <svg
1128
+ class="text-primary h-3 w-3 shrink-0"
1129
+ fill="none"
1130
+ stroke="currentColor"
1131
+ stroke-linecap="round"
1132
+ stroke-linejoin="round"
1133
+ stroke-width="2.5"
1134
+ viewBox="0 0 24 24"
1135
+ xmlns="http://www.w3.org/2000/svg"
1136
+ >
1137
+ <polyline points="20 6 9 17 4 12" />
1138
+ </svg>
1139
+ }
1140
+ </button>
1141
+ </li>
1142
+ } @empty {
1143
+ <li class="px-3 py-1.5 text-sm opacity-50">无匹配项</li>
1144
+ }
1145
+ </ul>
1146
+ </div>
1147
+ `,
1148
+ changeDetection: ChangeDetectionStrategy.OnPush
1149
+ }]
1150
+ }],
1151
+ ctorParameters: () => [],
1152
+ propDecorators: {
1153
+ trigger: [{
1154
+ type: i0.ViewChild,
1155
+ args: ["trigger", { isSignal: true }]
1156
+ }],
1157
+ popoverEl: [{
1158
+ type: i0.ViewChild,
1159
+ args: ["popoverEl", { isSignal: true }]
1160
+ }],
1161
+ filterInput: [{
1162
+ type: i0.ViewChild,
1163
+ args: ["filterInput", { isSignal: true }]
1164
+ }],
1165
+ options: [{
1166
+ type: i0.Input,
1167
+ args: [{
1168
+ isSignal: true,
1169
+ alias: "options",
1170
+ required: true
1171
+ }]
1172
+ }],
1173
+ selected: [{
1174
+ type: i0.Input,
1175
+ args: [{
1176
+ isSignal: true,
1177
+ alias: "selected",
1178
+ required: false
1179
+ }]
1180
+ }],
1181
+ placeholder: [{
1182
+ type: i0.Input,
1183
+ args: [{
1184
+ isSignal: true,
1185
+ alias: "placeholder",
1186
+ required: false
1187
+ }]
1188
+ }],
1189
+ minWidth: [{
1190
+ type: i0.Input,
1191
+ args: [{
1192
+ isSignal: true,
1193
+ alias: "minWidth",
1194
+ required: false
1195
+ }]
1196
+ }],
1197
+ selectChange: [{
1198
+ type: i0.Output,
1199
+ args: ["selectChange"]
1200
+ }]
1201
+ }
1202
+ });
1203
+ var OperatorSelectorComponent = class OperatorSelectorComponent {
1204
+ registry = getDefaultOperatorRegistry();
1205
+ operatorOptions = computed(() => {
1206
+ const metadata = this.fieldMetadata();
1207
+ return (metadata ? this.registry.getForField(metadata) : this.registry.getForType(this.fieldType())).map((op) => ({
1208
+ value: op.key,
1209
+ label: op.label
1210
+ }));
1211
+ }, ...ngDevMode ? [{ debugName: "operatorOptions" }] : /* istanbul ignore next */ []);
1212
+ fieldMetadata = input(void 0, ...ngDevMode ? [{ debugName: "fieldMetadata" }] : /* istanbul ignore next */ []);
1213
+ fieldType = input("string", ...ngDevMode ? [{ debugName: "fieldType" }] : /* istanbul ignore next */ []);
1214
+ selectedOperator = input("=", ...ngDevMode ? [{ debugName: "selectedOperator" }] : /* istanbul ignore next */ []);
1215
+ operatorChangeFn = input(void 0, ...ngDevMode ? [{ debugName: "operatorChangeFn" }] : /* istanbul ignore next */ []);
1216
+ operatorChange = output();
1217
+ onOperatorChange(op) {
1218
+ this.operatorChange.emit(op);
1219
+ this.operatorChangeFn()?.(op);
1220
+ }
1221
+ static ɵfac = i0.ɵɵngDeclareFactory({
1222
+ minVersion: "12.0.0",
1223
+ version: "22.2.3",
1224
+ ngImport: i0,
1225
+ type: OperatorSelectorComponent,
1226
+ deps: [],
1227
+ target: i0.ɵɵFactoryTarget.Component
1228
+ });
1229
+ static ɵcmp = i0.ɵɵngDeclareComponent({
1230
+ minVersion: "17.1.0",
1231
+ version: "22.2.3",
1232
+ type: OperatorSelectorComponent,
1233
+ isStandalone: true,
1234
+ selector: "rxdb-operator-selector",
1235
+ inputs: {
1236
+ fieldMetadata: {
1237
+ classPropertyName: "fieldMetadata",
1238
+ publicName: "fieldMetadata",
1239
+ isSignal: true,
1240
+ isRequired: false,
1241
+ transformFunction: null
1242
+ },
1243
+ fieldType: {
1244
+ classPropertyName: "fieldType",
1245
+ publicName: "fieldType",
1246
+ isSignal: true,
1247
+ isRequired: false,
1248
+ transformFunction: null
1249
+ },
1250
+ selectedOperator: {
1251
+ classPropertyName: "selectedOperator",
1252
+ publicName: "selectedOperator",
1253
+ isSignal: true,
1254
+ isRequired: false,
1255
+ transformFunction: null
1256
+ },
1257
+ operatorChangeFn: {
1258
+ classPropertyName: "operatorChangeFn",
1259
+ publicName: "operatorChangeFn",
1260
+ isSignal: true,
1261
+ isRequired: false,
1262
+ transformFunction: null
1263
+ }
1264
+ },
1265
+ outputs: { operatorChange: "operatorChange" },
1266
+ ngImport: i0,
1267
+ template: `
1268
+ <rxdb-popover-select
1269
+ [options]="operatorOptions()"
1270
+ [selected]="selectedOperator()"
1271
+ (selectChange)="onOperatorChange($event)"
1272
+ minWidth="8rem"
1273
+ placeholder="选择操作符"
1274
+ />
1275
+ `,
1276
+ isInline: true,
1277
+ dependencies: [{
1278
+ kind: "component",
1279
+ type: PopoverSelectComponent,
1280
+ selector: "rxdb-popover-select",
1281
+ inputs: [
1282
+ "options",
1283
+ "selected",
1284
+ "placeholder",
1285
+ "minWidth"
1286
+ ],
1287
+ outputs: ["selectChange"]
1288
+ }],
1289
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
1290
+ });
1291
+ };
1292
+ i0.ɵɵngDeclareClassMetadata({
1293
+ minVersion: "12.0.0",
1294
+ version: "22.2.3",
1295
+ ngImport: i0,
1296
+ type: OperatorSelectorComponent,
1297
+ decorators: [{
1298
+ type: Component,
1299
+ args: [{
1300
+ selector: "rxdb-operator-selector",
1301
+ standalone: true,
1302
+ imports: [PopoverSelectComponent],
1303
+ template: `
1304
+ <rxdb-popover-select
1305
+ [options]="operatorOptions()"
1306
+ [selected]="selectedOperator()"
1307
+ (selectChange)="onOperatorChange($event)"
1308
+ minWidth="8rem"
1309
+ placeholder="选择操作符"
1310
+ />
1311
+ `,
1312
+ changeDetection: ChangeDetectionStrategy.OnPush
1313
+ }]
1314
+ }],
1315
+ propDecorators: {
1316
+ fieldMetadata: [{
1317
+ type: i0.Input,
1318
+ args: [{
1319
+ isSignal: true,
1320
+ alias: "fieldMetadata",
1321
+ required: false
1322
+ }]
1323
+ }],
1324
+ fieldType: [{
1325
+ type: i0.Input,
1326
+ args: [{
1327
+ isSignal: true,
1328
+ alias: "fieldType",
1329
+ required: false
1330
+ }]
1331
+ }],
1332
+ selectedOperator: [{
1333
+ type: i0.Input,
1334
+ args: [{
1335
+ isSignal: true,
1336
+ alias: "selectedOperator",
1337
+ required: false
1338
+ }]
1339
+ }],
1340
+ operatorChangeFn: [{
1341
+ type: i0.Input,
1342
+ args: [{
1343
+ isSignal: true,
1344
+ alias: "operatorChangeFn",
1345
+ required: false
1346
+ }]
1347
+ }],
1348
+ operatorChange: [{
1349
+ type: i0.Output,
1350
+ args: ["operatorChange"]
1351
+ }]
1352
+ }
1353
+ });
1354
+ var ValueInputComponent = class ValueInputComponent {
1355
+ previousInputType = null;
1356
+ subqueryComponentRef;
1357
+ subquerySubscription;
1358
+ subqueryLoading = false;
1359
+ injector = inject(Injector);
1360
+ uuidError = computed(() => {
1361
+ if (this.inputType() !== "uuid") return "";
1362
+ const val = String(this.currentValue() ?? "").trim();
1363
+ return val && !UUID_RE.test(val) ? "请输入合法的 UUID 格式" : "";
1364
+ }, ...ngDevMode ? [{ debugName: "uuidError" }] : /* istanbul ignore next */ []);
1365
+ subqueryContainer;
1366
+ fieldType = input("string", ...ngDevMode ? [{ debugName: "fieldType" }] : /* istanbul ignore next */ []);
1367
+ operator = input("=", ...ngDevMode ? [{ debugName: "operator" }] : /* istanbul ignore next */ []);
1368
+ value = input("", ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
1369
+ enumOptions = input([], ...ngDevMode ? [{ debugName: "enumOptions" }] : /* istanbul ignore next */ []);
1370
+ relationFields = input([], ...ngDevMode ? [{ debugName: "relationFields" }] : /* istanbul ignore next */ []);
1371
+ where = input(...ngDevMode ? [void 0, { debugName: "where" }] : /* istanbul ignore next */ []);
1372
+ valueChange = output();
1373
+ whereChange = output();
1374
+ errorMessage = input("", ...ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []);
1375
+ hasError = computed(() => !!this.errorMessage(), ...ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []);
1376
+ valueChangeFn = input(void 0, ...ngDevMode ? [{ debugName: "valueChangeFn" }] : /* istanbul ignore next */ []);
1377
+ whereChangeFn = input(void 0, ...ngDevMode ? [{ debugName: "whereChangeFn" }] : /* istanbul ignore next */ []);
1378
+ currentValue = signal("", ...ngDevMode ? [{ debugName: "currentValue" }] : /* istanbul ignore next */ []);
1379
+ rangeMin = signal(null, ...ngDevMode ? [{ debugName: "rangeMin" }] : /* istanbul ignore next */ []);
1380
+ rangeMax = signal(null, ...ngDevMode ? [{ debugName: "rangeMax" }] : /* istanbul ignore next */ []);
1381
+ arrayInputValue = signal("", ...ngDevMode ? [{ debugName: "arrayInputValue" }] : /* istanbul ignore next */ []);
1382
+ currentDateStr = signal("", ...ngDevMode ? [{ debugName: "currentDateStr" }] : /* istanbul ignore next */ []);
1383
+ dateRangeStart = signal("", ...ngDevMode ? [{ debugName: "dateRangeStart" }] : /* istanbul ignore next */ []);
1384
+ dateRangeEnd = signal("", ...ngDevMode ? [{ debugName: "dateRangeEnd" }] : /* istanbul ignore next */ []);
1385
+ inputType = computed(() => getInputType(this.fieldType(), this.operator(), this.enumOptions()), ...ngDevMode ? [{ debugName: "inputType" }] : /* istanbul ignore next */ []);
1386
+ enumSelectOptions = computed(() => this.enumOptions().map((opt) => ({
1387
+ value: String(opt),
1388
+ label: String(opt)
1389
+ })), ...ngDevMode ? [{ debugName: "enumSelectOptions" }] : /* istanbul ignore next */ []);
1390
+ constructor() {
1391
+ effect(() => {
1392
+ const currentType = this.inputType();
1393
+ if (this.previousInputType !== null && this.previousInputType !== currentType) {
1394
+ const defaultVal = getDefaultValueForType(currentType);
1395
+ this.currentValue.set(defaultVal);
1396
+ this.valueChange.emit(defaultVal);
1397
+ this.valueChangeFn()?.(defaultVal);
1398
+ }
1399
+ this.previousInputType = currentType;
1400
+ });
1401
+ afterNextRender(() => {
1402
+ effect(() => {
1403
+ const isSubquery = this.inputType() === "subquery";
1404
+ const hasRelationFields = this.relationFields().length > 0;
1405
+ if (isSubquery && hasRelationFields) this.loadSubqueryComponent();
1406
+ else this.destroySubqueryComponent();
1407
+ }, { injector: this.injector });
1408
+ }, { injector: this.injector });
1409
+ }
1410
+ ngOnDestroy() {
1411
+ this.destroySubqueryComponent();
1412
+ }
1413
+ ngOnInit() {
1414
+ const value = this.value();
1415
+ if (this.inputType() === "range" && Array.isArray(value)) {
1416
+ if (this.fieldType() === "date") {
1417
+ this.dateRangeStart.set(String(value[0] ?? ""));
1418
+ this.dateRangeEnd.set(String(value[1] ?? ""));
1419
+ } else {
1420
+ this.rangeMin.set(value[0]);
1421
+ this.rangeMax.set(value[1]);
1422
+ }
1423
+ } else if (this.inputType() === "array" && Array.isArray(value)) this.arrayInputValue.set(value.join(", "));
1424
+ else if (this.inputType() === "enum-array") this.currentValue.set(Array.isArray(value) ? value : []);
1425
+ else if (this.inputType() === "date") this.currentDateStr.set(typeof value === "string" ? value : "");
1426
+ else if (this.inputType() === "uuid") this.currentValue.set(typeof value === "string" ? value : "");
1427
+ else this.currentValue.set(this.fieldType() === "number" && value === "" ? null : value);
1428
+ }
1429
+ onValueChange(value) {
1430
+ this.currentValue.set(value);
1431
+ this.valueChange.emit(value);
1432
+ this.valueChangeFn()?.(value);
1433
+ }
1434
+ onUuidChange(value) {
1435
+ this.currentValue.set(value);
1436
+ const trimmed = value.trim();
1437
+ this.valueChange.emit(trimmed.toLowerCase() || "");
1438
+ this.valueChangeFn()?.(trimmed.toLowerCase() || "");
1439
+ }
1440
+ onNumberInputChange(value) {
1441
+ const num = value === "" ? null : Number(value);
1442
+ this.currentValue.set(num);
1443
+ this.valueChange.emit(num);
1444
+ this.valueChangeFn()?.(num);
1445
+ }
1446
+ onEnumArrayChange(event) {
1447
+ const select = event.target;
1448
+ const selected = Array.from(select.selectedOptions).map((o) => o.value);
1449
+ this.currentValue.set(selected);
1450
+ this.valueChange.emit(selected);
1451
+ this.valueChangeFn()?.(selected);
1452
+ }
1453
+ isEnumSelected(opt) {
1454
+ const val = this.currentValue();
1455
+ return Array.isArray(val) && val.includes(opt);
1456
+ }
1457
+ onEnumSelect(value) {
1458
+ this.currentValue.set(value);
1459
+ this.valueChange.emit(value);
1460
+ this.valueChangeFn()?.(value);
1461
+ }
1462
+ onNativeDateChange(value) {
1463
+ this.currentDateStr.set(value);
1464
+ this.valueChange.emit(value || "");
1465
+ this.valueChangeFn()?.(value || "");
1466
+ }
1467
+ onDateRangeStartChange(value) {
1468
+ this.dateRangeStart.set(value);
1469
+ this.onDateRangePartChange();
1470
+ }
1471
+ onDateRangeEndChange(value) {
1472
+ this.dateRangeEnd.set(value);
1473
+ this.onDateRangePartChange();
1474
+ }
1475
+ onDateRangePartChange() {
1476
+ const start = this.dateRangeStart();
1477
+ const end = this.dateRangeEnd();
1478
+ if (start && end) {
1479
+ this.valueChange.emit([start, end]);
1480
+ this.valueChangeFn()?.([start, end]);
1481
+ } else {
1482
+ this.valueChange.emit([]);
1483
+ this.valueChangeFn()?.([]);
1484
+ }
1485
+ }
1486
+ onRangeMinChange(value) {
1487
+ this.rangeMin.set(value === "" ? null : Number(value));
1488
+ this.onRangeChange();
1489
+ }
1490
+ onRangeMaxChange(value) {
1491
+ this.rangeMax.set(value === "" ? null : Number(value));
1492
+ this.onRangeChange();
1493
+ }
1494
+ onRangeChange() {
1495
+ const min = this.rangeMin();
1496
+ const max = this.rangeMax();
1497
+ if (min != null && max != null) {
1498
+ this.valueChange.emit([min, max]);
1499
+ this.valueChangeFn()?.([min, max]);
1500
+ } else {
1501
+ this.valueChange.emit([]);
1502
+ this.valueChangeFn()?.([]);
1503
+ }
1504
+ }
1505
+ onArrayInputChange(value) {
1506
+ this.arrayInputValue.set(value);
1507
+ const result = parseCommaSeparatedInput(value, this.fieldType());
1508
+ this.valueChange.emit(result);
1509
+ this.valueChangeFn()?.(result);
1510
+ }
1511
+ handleWhereChange(where) {
1512
+ const typedWhere = where;
1513
+ this.whereChange.emit(typedWhere);
1514
+ this.whereChangeFn()?.(typedWhere);
1515
+ }
1516
+ async loadSubqueryComponent() {
1517
+ if (!this.subqueryContainer || this.subqueryComponentRef || this.subqueryLoading) return;
1518
+ this.subqueryLoading = true;
1519
+ try {
1520
+ const { SubqueryBuilderComponent } = await import("./aiao-rxdb-model-angular.mjs").then((n) => n.t);
1521
+ this.subqueryComponentRef = this.subqueryContainer.createComponent(SubqueryBuilderComponent);
1522
+ this.subqueryComponentRef.setInput("fields", this.relationFields());
1523
+ this.subqueryComponentRef.setInput("initialQuery", this.where());
1524
+ this.subquerySubscription = this.subqueryComponentRef.instance.queryChange.subscribe((query) => {
1525
+ this.handleWhereChange(query);
1526
+ });
1527
+ } catch (error) {
1528
+ console.error("[ValueInput] Failed to load SubqueryBuilderComponent:", error);
1529
+ } finally {
1530
+ this.subqueryLoading = false;
1531
+ }
1532
+ }
1533
+ destroySubqueryComponent() {
1534
+ this.subquerySubscription?.unsubscribe();
1535
+ this.subquerySubscription = void 0;
1536
+ if (this.subqueryComponentRef) {
1537
+ this.subqueryComponentRef.destroy();
1538
+ this.subqueryComponentRef = void 0;
1539
+ }
1540
+ }
1541
+ static ɵfac = i0.ɵɵngDeclareFactory({
1542
+ minVersion: "12.0.0",
1543
+ version: "22.2.3",
1544
+ ngImport: i0,
1545
+ type: ValueInputComponent,
1546
+ deps: [],
1547
+ target: i0.ɵɵFactoryTarget.Component
1548
+ });
1549
+ static ɵcmp = i0.ɵɵngDeclareComponent({
1550
+ minVersion: "17.0.0",
1551
+ version: "22.2.3",
1552
+ type: ValueInputComponent,
1553
+ isStandalone: true,
1554
+ selector: "rxdb-value-input",
1555
+ inputs: {
1556
+ fieldType: {
1557
+ classPropertyName: "fieldType",
1558
+ publicName: "fieldType",
1559
+ isSignal: true,
1560
+ isRequired: false,
1561
+ transformFunction: null
1562
+ },
1563
+ operator: {
1564
+ classPropertyName: "operator",
1565
+ publicName: "operator",
1566
+ isSignal: true,
1567
+ isRequired: false,
1568
+ transformFunction: null
1569
+ },
1570
+ value: {
1571
+ classPropertyName: "value",
1572
+ publicName: "value",
1573
+ isSignal: true,
1574
+ isRequired: false,
1575
+ transformFunction: null
1576
+ },
1577
+ enumOptions: {
1578
+ classPropertyName: "enumOptions",
1579
+ publicName: "enumOptions",
1580
+ isSignal: true,
1581
+ isRequired: false,
1582
+ transformFunction: null
1583
+ },
1584
+ relationFields: {
1585
+ classPropertyName: "relationFields",
1586
+ publicName: "relationFields",
1587
+ isSignal: true,
1588
+ isRequired: false,
1589
+ transformFunction: null
1590
+ },
1591
+ where: {
1592
+ classPropertyName: "where",
1593
+ publicName: "where",
1594
+ isSignal: true,
1595
+ isRequired: false,
1596
+ transformFunction: null
1597
+ },
1598
+ errorMessage: {
1599
+ classPropertyName: "errorMessage",
1600
+ publicName: "errorMessage",
1601
+ isSignal: true,
1602
+ isRequired: false,
1603
+ transformFunction: null
1604
+ },
1605
+ valueChangeFn: {
1606
+ classPropertyName: "valueChangeFn",
1607
+ publicName: "valueChangeFn",
1608
+ isSignal: true,
1609
+ isRequired: false,
1610
+ transformFunction: null
1611
+ },
1612
+ whereChangeFn: {
1613
+ classPropertyName: "whereChangeFn",
1614
+ publicName: "whereChangeFn",
1615
+ isSignal: true,
1616
+ isRequired: false,
1617
+ transformFunction: null
1618
+ }
1619
+ },
1620
+ outputs: {
1621
+ valueChange: "valueChange",
1622
+ whereChange: "whereChange"
1623
+ },
1624
+ viewQueries: [{
1625
+ propertyName: "subqueryContainer",
1626
+ first: true,
1627
+ predicate: ["subqueryContainer"],
1628
+ descendants: true,
1629
+ read: ViewContainerRef
1630
+ }],
1631
+ ngImport: i0,
1632
+ template: `
1633
+ @switch (inputType()) {
1634
+ @case ('none') {
1635
+ <span class="text-base-content/50 px-2 text-sm italic">无需输入值</span>
1636
+ }
1637
+ @case ('uuid') {
1638
+ <div
1639
+ [attr.data-tip]="uuidError() || errorMessage() || null"
1640
+ [class.tooltip-top]="!!uuidError() || hasError()"
1641
+ [class.tooltip]="!!uuidError() || hasError()"
1642
+ >
1643
+ <input
1644
+ class="input input-sm font-mono"
1645
+ [class.input-error]="hasError() || !!uuidError()"
1646
+ [value]="$any(currentValue())"
1647
+ (input)="onUuidChange($any($event.target).value)"
1648
+ placeholder="输入 UUID"
1649
+ style="min-width: 16rem"
1650
+ type="text"
1651
+ />
1652
+ </div>
1653
+ }
1654
+ @case ('subquery') {
1655
+ <div class="subquery-container w-full">
1656
+ @if (!relationFields() || relationFields().length === 0) {
1657
+ <span class="text-base-content/50 px-2 text-sm italic">存在/不存在(无子条件)</span>
1658
+ } @else {
1659
+ <div #subqueryContainer></div>
1660
+ }
1661
+ </div>
1662
+ }
1663
+ @case ('boolean') {
1664
+ <input
1665
+ class="toggle toggle-sm"
1666
+ [checked]="!!currentValue()"
1667
+ (change)="onValueChange($any($event.target).checked)"
1668
+ type="checkbox"
1669
+ />
1670
+ }
1671
+ @case ('enum') {
1672
+ <rxdb-popover-select
1673
+ [options]="enumSelectOptions()"
1674
+ [selected]="$any(currentValue())?.toString() ?? ''"
1675
+ (selectChange)="onEnumSelect($event)"
1676
+ minWidth="12rem"
1677
+ placeholder="选择值"
1678
+ />
1679
+ }
1680
+ @case ('enum-array') {
1681
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1682
+ <select
1683
+ class="select select-sm"
1684
+ [class.select-error]="hasError()"
1685
+ (change)="onEnumArrayChange($event)"
1686
+ multiple
1687
+ style="min-width: 12rem; min-height: 6rem"
1688
+ >
1689
+ @for (opt of enumOptions(); track opt) {
1690
+ <option [selected]="isEnumSelected(opt)" [value]="opt">{{ opt }}</option>
1691
+ }
1692
+ </select>
1693
+ </div>
1694
+ }
1695
+ @case ('range') {
1696
+ @if (fieldType() === 'date') {
1697
+ <div
1698
+ class="flex items-center gap-2"
1699
+ [attr.data-tip]="errorMessage() || null"
1700
+ [class.tooltip-top]="hasError()"
1701
+ [class.tooltip]="hasError()"
1702
+ >
1703
+ <input
1704
+ class="input input-sm"
1705
+ [class.input-error]="hasError()"
1706
+ [value]="dateRangeStart()"
1707
+ (change)="onDateRangeStartChange($any($event.target).value)"
1708
+ style="width: 9rem"
1709
+ type="date"
1710
+ />
1711
+ <span class="text-base-content/50">至</span>
1712
+ <input
1713
+ class="input input-sm"
1714
+ [class.input-error]="hasError()"
1715
+ [value]="dateRangeEnd()"
1716
+ (change)="onDateRangeEndChange($any($event.target).value)"
1717
+ style="width: 9rem"
1718
+ type="date"
1719
+ />
1720
+ </div>
1721
+ } @else {
1722
+ <div
1723
+ class="flex items-center gap-2"
1724
+ [attr.data-tip]="errorMessage() || null"
1725
+ [class.tooltip-top]="hasError()"
1726
+ [class.tooltip]="hasError()"
1727
+ >
1728
+ <input
1729
+ class="input input-sm"
1730
+ [class.input-error]="hasError()"
1731
+ [value]="$any(rangeMin()) ?? ''"
1732
+ (input)="onRangeMinChange($any($event.target).value)"
1733
+ placeholder="最小值"
1734
+ style="width: 7rem"
1735
+ type="number"
1736
+ />
1737
+ <span class="text-base-content/50">至</span>
1738
+ <input
1739
+ class="input input-sm"
1740
+ [class.input-error]="hasError()"
1741
+ [value]="$any(rangeMax()) ?? ''"
1742
+ (input)="onRangeMaxChange($any($event.target).value)"
1743
+ placeholder="最大值"
1744
+ style="width: 7rem"
1745
+ type="number"
1746
+ />
1747
+ </div>
1748
+ }
1749
+ }
1750
+ @case ('number') {
1751
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1752
+ <input
1753
+ class="input input-sm"
1754
+ [class.input-error]="hasError()"
1755
+ [value]="$any(currentValue()) ?? ''"
1756
+ (input)="onNumberInputChange($any($event.target).value)"
1757
+ placeholder="输入数值"
1758
+ type="number"
1759
+ />
1760
+ </div>
1761
+ }
1762
+ @case ('date') {
1763
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1764
+ <input
1765
+ class="input input-sm"
1766
+ [class.input-error]="hasError()"
1767
+ [value]="currentDateStr()"
1768
+ (change)="onNativeDateChange($any($event.target).value)"
1769
+ style="width: 10rem"
1770
+ type="date"
1771
+ />
1772
+ </div>
1773
+ }
1774
+ @case ('array') {
1775
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1776
+ <input
1777
+ class="input input-sm"
1778
+ [class.input-error]="hasError()"
1779
+ [value]="arrayInputValue()"
1780
+ (input)="onArrayInputChange($any($event.target).value)"
1781
+ placeholder="输入多个值,用逗号分隔"
1782
+ style="width: 100%; min-width: 12rem"
1783
+ type="text"
1784
+ />
1785
+ </div>
1786
+ }
1787
+ @default {
1788
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1789
+ <input
1790
+ class="input input-sm"
1791
+ [class.input-error]="hasError()"
1792
+ [value]="$any(currentValue()) ?? ''"
1793
+ (input)="onValueChange($any($event.target).value)"
1794
+ placeholder="输入值"
1795
+ style="width: 100%; min-width: 12rem"
1796
+ type="text"
1797
+ />
1798
+ </div>
1799
+ }
1800
+ }
1801
+ `,
1802
+ isInline: true,
1803
+ dependencies: [{
1804
+ kind: "ngmodule",
1805
+ type: CommonModule
1806
+ }, {
1807
+ kind: "component",
1808
+ type: PopoverSelectComponent,
1809
+ selector: "rxdb-popover-select",
1810
+ inputs: [
1811
+ "options",
1812
+ "selected",
1813
+ "placeholder",
1814
+ "minWidth"
1815
+ ],
1816
+ outputs: ["selectChange"]
1817
+ }],
1818
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
1819
+ });
1820
+ };
1821
+ i0.ɵɵngDeclareClassMetadata({
1822
+ minVersion: "12.0.0",
1823
+ version: "22.2.3",
1824
+ ngImport: i0,
1825
+ type: ValueInputComponent,
1826
+ decorators: [{
1827
+ type: Component,
1828
+ args: [{
1829
+ selector: "rxdb-value-input",
1830
+ standalone: true,
1831
+ imports: [CommonModule, PopoverSelectComponent],
1832
+ template: `
1833
+ @switch (inputType()) {
1834
+ @case ('none') {
1835
+ <span class="text-base-content/50 px-2 text-sm italic">无需输入值</span>
1836
+ }
1837
+ @case ('uuid') {
1838
+ <div
1839
+ [attr.data-tip]="uuidError() || errorMessage() || null"
1840
+ [class.tooltip-top]="!!uuidError() || hasError()"
1841
+ [class.tooltip]="!!uuidError() || hasError()"
1842
+ >
1843
+ <input
1844
+ class="input input-sm font-mono"
1845
+ [class.input-error]="hasError() || !!uuidError()"
1846
+ [value]="$any(currentValue())"
1847
+ (input)="onUuidChange($any($event.target).value)"
1848
+ placeholder="输入 UUID"
1849
+ style="min-width: 16rem"
1850
+ type="text"
1851
+ />
1852
+ </div>
1853
+ }
1854
+ @case ('subquery') {
1855
+ <div class="subquery-container w-full">
1856
+ @if (!relationFields() || relationFields().length === 0) {
1857
+ <span class="text-base-content/50 px-2 text-sm italic">存在/不存在(无子条件)</span>
1858
+ } @else {
1859
+ <div #subqueryContainer></div>
1860
+ }
1861
+ </div>
1862
+ }
1863
+ @case ('boolean') {
1864
+ <input
1865
+ class="toggle toggle-sm"
1866
+ [checked]="!!currentValue()"
1867
+ (change)="onValueChange($any($event.target).checked)"
1868
+ type="checkbox"
1869
+ />
1870
+ }
1871
+ @case ('enum') {
1872
+ <rxdb-popover-select
1873
+ [options]="enumSelectOptions()"
1874
+ [selected]="$any(currentValue())?.toString() ?? ''"
1875
+ (selectChange)="onEnumSelect($event)"
1876
+ minWidth="12rem"
1877
+ placeholder="选择值"
1878
+ />
1879
+ }
1880
+ @case ('enum-array') {
1881
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1882
+ <select
1883
+ class="select select-sm"
1884
+ [class.select-error]="hasError()"
1885
+ (change)="onEnumArrayChange($event)"
1886
+ multiple
1887
+ style="min-width: 12rem; min-height: 6rem"
1888
+ >
1889
+ @for (opt of enumOptions(); track opt) {
1890
+ <option [selected]="isEnumSelected(opt)" [value]="opt">{{ opt }}</option>
1891
+ }
1892
+ </select>
1893
+ </div>
1894
+ }
1895
+ @case ('range') {
1896
+ @if (fieldType() === 'date') {
1897
+ <div
1898
+ class="flex items-center gap-2"
1899
+ [attr.data-tip]="errorMessage() || null"
1900
+ [class.tooltip-top]="hasError()"
1901
+ [class.tooltip]="hasError()"
1902
+ >
1903
+ <input
1904
+ class="input input-sm"
1905
+ [class.input-error]="hasError()"
1906
+ [value]="dateRangeStart()"
1907
+ (change)="onDateRangeStartChange($any($event.target).value)"
1908
+ style="width: 9rem"
1909
+ type="date"
1910
+ />
1911
+ <span class="text-base-content/50">至</span>
1912
+ <input
1913
+ class="input input-sm"
1914
+ [class.input-error]="hasError()"
1915
+ [value]="dateRangeEnd()"
1916
+ (change)="onDateRangeEndChange($any($event.target).value)"
1917
+ style="width: 9rem"
1918
+ type="date"
1919
+ />
1920
+ </div>
1921
+ } @else {
1922
+ <div
1923
+ class="flex items-center gap-2"
1924
+ [attr.data-tip]="errorMessage() || null"
1925
+ [class.tooltip-top]="hasError()"
1926
+ [class.tooltip]="hasError()"
1927
+ >
1928
+ <input
1929
+ class="input input-sm"
1930
+ [class.input-error]="hasError()"
1931
+ [value]="$any(rangeMin()) ?? ''"
1932
+ (input)="onRangeMinChange($any($event.target).value)"
1933
+ placeholder="最小值"
1934
+ style="width: 7rem"
1935
+ type="number"
1936
+ />
1937
+ <span class="text-base-content/50">至</span>
1938
+ <input
1939
+ class="input input-sm"
1940
+ [class.input-error]="hasError()"
1941
+ [value]="$any(rangeMax()) ?? ''"
1942
+ (input)="onRangeMaxChange($any($event.target).value)"
1943
+ placeholder="最大值"
1944
+ style="width: 7rem"
1945
+ type="number"
1946
+ />
1947
+ </div>
1948
+ }
1949
+ }
1950
+ @case ('number') {
1951
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1952
+ <input
1953
+ class="input input-sm"
1954
+ [class.input-error]="hasError()"
1955
+ [value]="$any(currentValue()) ?? ''"
1956
+ (input)="onNumberInputChange($any($event.target).value)"
1957
+ placeholder="输入数值"
1958
+ type="number"
1959
+ />
1960
+ </div>
1961
+ }
1962
+ @case ('date') {
1963
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1964
+ <input
1965
+ class="input input-sm"
1966
+ [class.input-error]="hasError()"
1967
+ [value]="currentDateStr()"
1968
+ (change)="onNativeDateChange($any($event.target).value)"
1969
+ style="width: 10rem"
1970
+ type="date"
1971
+ />
1972
+ </div>
1973
+ }
1974
+ @case ('array') {
1975
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1976
+ <input
1977
+ class="input input-sm"
1978
+ [class.input-error]="hasError()"
1979
+ [value]="arrayInputValue()"
1980
+ (input)="onArrayInputChange($any($event.target).value)"
1981
+ placeholder="输入多个值,用逗号分隔"
1982
+ style="width: 100%; min-width: 12rem"
1983
+ type="text"
1984
+ />
1985
+ </div>
1986
+ }
1987
+ @default {
1988
+ <div [attr.data-tip]="errorMessage() || null" [class.tooltip-top]="hasError()" [class.tooltip]="hasError()">
1989
+ <input
1990
+ class="input input-sm"
1991
+ [class.input-error]="hasError()"
1992
+ [value]="$any(currentValue()) ?? ''"
1993
+ (input)="onValueChange($any($event.target).value)"
1994
+ placeholder="输入值"
1995
+ style="width: 100%; min-width: 12rem"
1996
+ type="text"
1997
+ />
1998
+ </div>
1999
+ }
2000
+ }
2001
+ `,
2002
+ changeDetection: ChangeDetectionStrategy.OnPush
2003
+ }]
2004
+ }],
2005
+ ctorParameters: () => [],
2006
+ propDecorators: {
2007
+ subqueryContainer: [{
2008
+ type: ViewChild,
2009
+ args: ["subqueryContainer", { read: ViewContainerRef }]
2010
+ }],
2011
+ fieldType: [{
2012
+ type: i0.Input,
2013
+ args: [{
2014
+ isSignal: true,
2015
+ alias: "fieldType",
2016
+ required: false
2017
+ }]
2018
+ }],
2019
+ operator: [{
2020
+ type: i0.Input,
2021
+ args: [{
2022
+ isSignal: true,
2023
+ alias: "operator",
2024
+ required: false
2025
+ }]
2026
+ }],
2027
+ value: [{
2028
+ type: i0.Input,
2029
+ args: [{
2030
+ isSignal: true,
2031
+ alias: "value",
2032
+ required: false
2033
+ }]
2034
+ }],
2035
+ enumOptions: [{
2036
+ type: i0.Input,
2037
+ args: [{
2038
+ isSignal: true,
2039
+ alias: "enumOptions",
2040
+ required: false
2041
+ }]
2042
+ }],
2043
+ relationFields: [{
2044
+ type: i0.Input,
2045
+ args: [{
2046
+ isSignal: true,
2047
+ alias: "relationFields",
2048
+ required: false
2049
+ }]
2050
+ }],
2051
+ where: [{
2052
+ type: i0.Input,
2053
+ args: [{
2054
+ isSignal: true,
2055
+ alias: "where",
2056
+ required: false
2057
+ }]
2058
+ }],
2059
+ valueChange: [{
2060
+ type: i0.Output,
2061
+ args: ["valueChange"]
2062
+ }],
2063
+ whereChange: [{
2064
+ type: i0.Output,
2065
+ args: ["whereChange"]
2066
+ }],
2067
+ errorMessage: [{
2068
+ type: i0.Input,
2069
+ args: [{
2070
+ isSignal: true,
2071
+ alias: "errorMessage",
2072
+ required: false
2073
+ }]
2074
+ }],
2075
+ valueChangeFn: [{
2076
+ type: i0.Input,
2077
+ args: [{
2078
+ isSignal: true,
2079
+ alias: "valueChangeFn",
2080
+ required: false
2081
+ }]
2082
+ }],
2083
+ whereChangeFn: [{
2084
+ type: i0.Input,
2085
+ args: [{
2086
+ isSignal: true,
2087
+ alias: "whereChangeFn",
2088
+ required: false
2089
+ }]
2090
+ }]
2091
+ }
2092
+ });
2093
+ const DEFAULT_QUERY_BUILDER_THEME = {
2094
+ fieldSelector: FieldSelectorComponent,
2095
+ operatorSelector: OperatorSelectorComponent,
2096
+ valueInput: ValueInputComponent
2097
+ };
2098
+ const QUERY_BUILDER_THEME = new InjectionToken("QUERY_BUILDER_THEME");
2099
+ function provideQueryBuilderTheme(theme) {
2100
+ return {
2101
+ provide: QUERY_BUILDER_THEME,
2102
+ useValue: theme
2103
+ };
2104
+ }
2105
+ var QueryRuleComponent = class QueryRuleComponent {
2106
+ existsOperators = /* @__PURE__ */ new Set(["exists", "notExists"]);
2107
+ noValueOperators = /* @__PURE__ */ new Set(["null", "notNull"]);
2108
+ theme = inject(QUERY_BUILDER_THEME, { optional: true }) ?? DEFAULT_QUERY_BUILDER_THEME;
2109
+ rule = input.required(...ngDevMode ? [{ debugName: "rule" }] : /* istanbul ignore next */ []);
2110
+ fields = input.required(...ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []);
2111
+ errors = input([], ...ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []);
2112
+ update = output();
2113
+ remove = output();
2114
+ fieldAsString = computed(() => this.rule().field, ...ngDevMode ? [{ debugName: "fieldAsString" }] : /* istanbul ignore next */ []);
2115
+ ruleErrors = computed(() => {
2116
+ const seen = /* @__PURE__ */ new Set();
2117
+ return this.errors().filter((e) => {
2118
+ if (e.field !== this.rule().field) return false;
2119
+ if (seen.has(e.message)) return false;
2120
+ seen.add(e.message);
2121
+ return true;
2122
+ });
2123
+ }, ...ngDevMode ? [{ debugName: "ruleErrors" }] : /* istanbul ignore next */ []);
2124
+ errorTooltip = computed(() => this.ruleErrors().map((e) => e.message).join(" | "), ...ngDevMode ? [{ debugName: "errorTooltip" }] : /* istanbul ignore next */ []);
2125
+ isExistsOperator = computed(() => this.existsOperators.has(this.rule().operator), ...ngDevMode ? [{ debugName: "isExistsOperator" }] : /* istanbul ignore next */ []);
2126
+ isNoValueOperator = computed(() => this.noValueOperators.has(this.rule().operator), ...ngDevMode ? [{ debugName: "isNoValueOperator" }] : /* istanbul ignore next */ []);
2127
+ showSubquery = signal(false, ...ngDevMode ? [{ debugName: "showSubquery" }] : /* istanbul ignore next */ []);
2128
+ subqueryVisible = computed(() => {
2129
+ const where = this.rule().where;
2130
+ if (where && where.rules && where.rules.length > 0) return true;
2131
+ return this.showSubquery();
2132
+ }, ...ngDevMode ? [{ debugName: "subqueryVisible" }] : /* istanbul ignore next */ []);
2133
+ currentField = computed(() => {
2134
+ const fieldName = this.rule().field;
2135
+ return this.fields().find((f) => f.name === fieldName);
2136
+ }, ...ngDevMode ? [{ debugName: "currentField" }] : /* istanbul ignore next */ []);
2137
+ currentFieldType = computed(() => this.currentField()?.type ?? "string", ...ngDevMode ? [{ debugName: "currentFieldType" }] : /* istanbul ignore next */ []);
2138
+ currentEnumOptions = computed(() => this.currentField()?.enum ?? [], ...ngDevMode ? [{ debugName: "currentEnumOptions" }] : /* istanbul ignore next */ []);
2139
+ currentRelationFields = computed(() => {
2140
+ return this.currentField()?.relationFields ?? [];
2141
+ }, ...ngDevMode ? [{ debugName: "currentRelationFields" }] : /* istanbul ignore next */ []);
2142
+ ruleWhere = computed(() => this.rule().where, ...ngDevMode ? [{ debugName: "ruleWhere" }] : /* istanbul ignore next */ []);
2143
+ fieldSelectorInputs = computed(() => ({
2144
+ fields: this.fields(),
2145
+ selectedField: this.fieldAsString(),
2146
+ fieldChangeFn: this.fieldChangeCb
2147
+ }), ...ngDevMode ? [{ debugName: "fieldSelectorInputs" }] : /* istanbul ignore next */ []);
2148
+ operatorSelectorInputs = computed(() => ({
2149
+ fieldMetadata: this.currentField(),
2150
+ fieldType: this.currentFieldType(),
2151
+ selectedOperator: this.rule().operator,
2152
+ operatorChangeFn: this.operatorChangeCb
2153
+ }), ...ngDevMode ? [{ debugName: "operatorSelectorInputs" }] : /* istanbul ignore next */ []);
2154
+ valueInputInputs = computed(() => ({
2155
+ fieldType: this.currentFieldType(),
2156
+ operator: this.rule().operator,
2157
+ value: this.rule().value,
2158
+ enumOptions: this.currentEnumOptions(),
2159
+ relationFields: this.currentRelationFields(),
2160
+ where: this.ruleWhere(),
2161
+ errorMessage: this.errorTooltip(),
2162
+ valueChangeFn: this.valueChangeCb,
2163
+ whereChangeFn: this.whereChangeCb
2164
+ }), ...ngDevMode ? [{ debugName: "valueInputInputs" }] : /* istanbul ignore next */ []);
2165
+ constructor() {
2166
+ effect(() => {
2167
+ const where = this.rule().where;
2168
+ if (!(where && where.rules && where.rules.length > 0)) untracked(() => this.showSubquery.set(false));
2169
+ });
2170
+ }
2171
+ onFieldChange(fieldName) {
2172
+ const field = this.fields().find((f) => f.name === fieldName);
2173
+ this.update.emit({
2174
+ id: this.rule().id,
2175
+ updates: {
2176
+ field: fieldName,
2177
+ operator: field?.isRelation ? "exists" : field?.type === "keyValue" ? "null" : "=",
2178
+ value: field?.type === "boolean" ? false : "",
2179
+ where: void 0
2180
+ }
2181
+ });
2182
+ }
2183
+ onAddSubcondition() {
2184
+ this.showSubquery.set(true);
2185
+ const firstField = this.currentRelationFields()[0];
2186
+ if (firstField) {
2187
+ const where = {
2188
+ id: "",
2189
+ combinator: "and",
2190
+ rules: [{
2191
+ id: "",
2192
+ field: firstField.name,
2193
+ operator: firstField.isRelation ? "exists" : firstField.type === "keyValue" ? "null" : "=",
2194
+ value: firstField.type === "boolean" ? false : ""
2195
+ }]
2196
+ };
2197
+ this.update.emit({
2198
+ id: this.rule().id,
2199
+ updates: { where }
2200
+ });
2201
+ }
2202
+ }
2203
+ onOperatorChange(operator) {
2204
+ const updates = { operator };
2205
+ if (this.existsOperators.has(operator) || this.noValueOperators.has(operator)) updates.value = null;
2206
+ if (!this.existsOperators.has(operator)) {
2207
+ updates.where = void 0;
2208
+ this.showSubquery.set(false);
2209
+ }
2210
+ this.update.emit({
2211
+ id: this.rule().id,
2212
+ updates
2213
+ });
2214
+ }
2215
+ onValueChange(value) {
2216
+ this.update.emit({
2217
+ id: this.rule().id,
2218
+ updates: { value }
2219
+ });
2220
+ }
2221
+ onWhereChange(where) {
2222
+ this.update.emit({
2223
+ id: this.rule().id,
2224
+ updates: { where }
2225
+ });
2226
+ }
2227
+ onRemove() {
2228
+ this.remove.emit();
2229
+ }
2230
+ fieldChangeCb = (field) => this.onFieldChange(field);
2231
+ operatorChangeCb = (op) => this.onOperatorChange(op);
2232
+ valueChangeCb = (value) => this.onValueChange(value);
2233
+ whereChangeCb = (where) => this.onWhereChange(where);
2234
+ static ɵfac = i0.ɵɵngDeclareFactory({
2235
+ minVersion: "12.0.0",
2236
+ version: "22.2.3",
2237
+ ngImport: i0,
2238
+ type: QueryRuleComponent,
2239
+ deps: [],
2240
+ target: i0.ɵɵFactoryTarget.Component
2241
+ });
2242
+ static ɵcmp = i0.ɵɵngDeclareComponent({
2243
+ minVersion: "17.0.0",
2244
+ version: "22.2.3",
2245
+ type: QueryRuleComponent,
2246
+ isStandalone: true,
2247
+ selector: "rxdb-query-rule",
2248
+ inputs: {
2249
+ rule: {
2250
+ classPropertyName: "rule",
2251
+ publicName: "rule",
2252
+ isSignal: true,
2253
+ isRequired: true,
2254
+ transformFunction: null
2255
+ },
2256
+ fields: {
2257
+ classPropertyName: "fields",
2258
+ publicName: "fields",
2259
+ isSignal: true,
2260
+ isRequired: true,
2261
+ transformFunction: null
2262
+ },
2263
+ errors: {
2264
+ classPropertyName: "errors",
2265
+ publicName: "errors",
2266
+ isSignal: true,
2267
+ isRequired: false,
2268
+ transformFunction: null
2269
+ }
2270
+ },
2271
+ outputs: {
2272
+ update: "update",
2273
+ remove: "remove"
2274
+ },
2275
+ ngImport: i0,
2276
+ template: `
2277
+ <div class="rxdb-query-rule group flex flex-col rounded p-1">
2278
+ <div class="flex flex-wrap items-center gap-2">
2279
+ <ng-container [ngComponentOutlet]="theme.fieldSelector" [ngComponentOutletInputs]="fieldSelectorInputs()" />
2280
+ <ng-container
2281
+ [ngComponentOutlet]="theme.operatorSelector"
2282
+ [ngComponentOutletInputs]="operatorSelectorInputs()"
2283
+ />
2284
+ @if (isExistsOperator()) {
2285
+ @if (!subqueryVisible()) {
2286
+ <button class="btn btn-ghost btn-xs" (click)="onAddSubcondition()" title="添加子条件" type="button">
2287
+ + 子条件(可选)
2288
+ </button>
2289
+ }
2290
+ } @else if (!isNoValueOperator()) {
2291
+ <ng-container [ngComponentOutlet]="theme.valueInput" [ngComponentOutletInputs]="valueInputInputs()" />
2292
+ }
2293
+ <button
2294
+ class="btn btn-ghost btn-xs btn-square text-base-content/30 hover:text-error ml-auto"
2295
+ (click)="onRemove()"
2296
+ title="删除条件"
2297
+ type="button"
2298
+ >
2299
+ ✕
2300
+ </button>
2301
+ </div>
2302
+ @if (isExistsOperator() && subqueryVisible()) {
2303
+ <ng-container [ngComponentOutlet]="theme.valueInput" [ngComponentOutletInputs]="valueInputInputs()" />
2304
+ }
2305
+ </div>
2306
+ `,
2307
+ isInline: true,
2308
+ dependencies: [{
2309
+ kind: "ngmodule",
2310
+ type: CommonModule
2311
+ }, {
2312
+ kind: "directive",
2313
+ type: i1.NgComponentOutlet,
2314
+ selector: "[ngComponentOutlet]",
2315
+ inputs: [
2316
+ "ngComponentOutlet",
2317
+ "ngComponentOutletInputs",
2318
+ "ngComponentOutletInjector",
2319
+ "ngComponentOutletEnvironmentInjector",
2320
+ "ngComponentOutletContent",
2321
+ "ngComponentOutletNgModule"
2322
+ ],
2323
+ exportAs: ["ngComponentOutlet"]
2324
+ }],
2325
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
2326
+ });
2327
+ };
2328
+ i0.ɵɵngDeclareClassMetadata({
2329
+ minVersion: "12.0.0",
2330
+ version: "22.2.3",
2331
+ ngImport: i0,
2332
+ type: QueryRuleComponent,
2333
+ decorators: [{
2334
+ type: Component,
2335
+ args: [{
2336
+ selector: "rxdb-query-rule",
2337
+ standalone: true,
2338
+ imports: [CommonModule, NgComponentOutlet],
2339
+ template: `
2340
+ <div class="rxdb-query-rule group flex flex-col rounded p-1">
2341
+ <div class="flex flex-wrap items-center gap-2">
2342
+ <ng-container [ngComponentOutlet]="theme.fieldSelector" [ngComponentOutletInputs]="fieldSelectorInputs()" />
2343
+ <ng-container
2344
+ [ngComponentOutlet]="theme.operatorSelector"
2345
+ [ngComponentOutletInputs]="operatorSelectorInputs()"
2346
+ />
2347
+ @if (isExistsOperator()) {
2348
+ @if (!subqueryVisible()) {
2349
+ <button class="btn btn-ghost btn-xs" (click)="onAddSubcondition()" title="添加子条件" type="button">
2350
+ + 子条件(可选)
2351
+ </button>
2352
+ }
2353
+ } @else if (!isNoValueOperator()) {
2354
+ <ng-container [ngComponentOutlet]="theme.valueInput" [ngComponentOutletInputs]="valueInputInputs()" />
2355
+ }
2356
+ <button
2357
+ class="btn btn-ghost btn-xs btn-square text-base-content/30 hover:text-error ml-auto"
2358
+ (click)="onRemove()"
2359
+ title="删除条件"
2360
+ type="button"
2361
+ >
2362
+ ✕
2363
+ </button>
2364
+ </div>
2365
+ @if (isExistsOperator() && subqueryVisible()) {
2366
+ <ng-container [ngComponentOutlet]="theme.valueInput" [ngComponentOutletInputs]="valueInputInputs()" />
2367
+ }
2368
+ </div>
2369
+ `,
2370
+ changeDetection: ChangeDetectionStrategy.OnPush
2371
+ }]
2372
+ }],
2373
+ ctorParameters: () => [],
2374
+ propDecorators: {
2375
+ rule: [{
2376
+ type: i0.Input,
2377
+ args: [{
2378
+ isSignal: true,
2379
+ alias: "rule",
2380
+ required: true
2381
+ }]
2382
+ }],
2383
+ fields: [{
2384
+ type: i0.Input,
2385
+ args: [{
2386
+ isSignal: true,
2387
+ alias: "fields",
2388
+ required: true
2389
+ }]
2390
+ }],
2391
+ errors: [{
2392
+ type: i0.Input,
2393
+ args: [{
2394
+ isSignal: true,
2395
+ alias: "errors",
2396
+ required: false
2397
+ }]
2398
+ }],
2399
+ update: [{
2400
+ type: i0.Output,
2401
+ args: ["update"]
2402
+ }],
2403
+ remove: [{
2404
+ type: i0.Output,
2405
+ args: ["remove"]
2406
+ }]
2407
+ }
2408
+ });
2409
+ var QueryDragDropHandler = class {
2410
+ moveItemFn;
2411
+ state = signal({
2412
+ draggedItemId: null,
2413
+ targetItemId: null,
2414
+ dropMode: null,
2415
+ isValidTarget: false
2416
+ }, ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
2417
+ constructor(moveItemFn) {
2418
+ this.moveItemFn = moveItemFn;
2419
+ }
2420
+ start(itemId) {
2421
+ this.state.set({
2422
+ draggedItemId: itemId,
2423
+ targetItemId: null,
2424
+ dropMode: null,
2425
+ isValidTarget: false
2426
+ });
2427
+ }
2428
+ over(targetItemId, dropMode, isValid, depth) {
2429
+ this.state.update((current) => {
2430
+ if (current.depth !== void 0 && depth < current.depth) return current;
2431
+ return {
2432
+ ...current,
2433
+ targetItemId,
2434
+ dropMode,
2435
+ isValidTarget: isValid,
2436
+ depth
2437
+ };
2438
+ });
2439
+ }
2440
+ leave() {
2441
+ this.state.update((s) => ({
2442
+ ...s,
2443
+ targetItemId: null,
2444
+ dropMode: null,
2445
+ isValidTarget: false,
2446
+ depth: void 0
2447
+ }));
2448
+ }
2449
+ drop(itemId, targetGroupId, targetIndex) {
2450
+ try {
2451
+ this.moveItemFn(itemId, targetGroupId, targetIndex);
2452
+ } catch {}
2453
+ this.reset();
2454
+ }
2455
+ end() {
2456
+ this.reset();
2457
+ }
2458
+ reset() {
2459
+ this.state.set({
2460
+ draggedItemId: null,
2461
+ targetItemId: null,
2462
+ dropMode: null,
2463
+ isValidTarget: false
2464
+ });
2465
+ }
2466
+ };
2467
+ function calculateDropMode(clientY, rect, isGroup) {
2468
+ const ratio = (clientY - rect.top) / rect.height;
2469
+ if (isGroup) {
2470
+ if (ratio < .25) return "before";
2471
+ if (ratio > .75) return "after";
2472
+ return "into";
2473
+ }
2474
+ return ratio < .5 ? "before" : "after";
2475
+ }
2476
+ var QueryGroupComponent = class QueryGroupComponent {
2477
+ group = input.required(...ngDevMode ? [{ debugName: "group" }] : /* istanbul ignore next */ []);
2478
+ fields = input.required(...ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []);
2479
+ errors = input([], ...ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []);
2480
+ depth = input(0, ...ngDevMode ? [{ debugName: "depth" }] : /* istanbul ignore next */ []);
2481
+ maxDepth = input(5, ...ngDevMode ? [{ debugName: "maxDepth" }] : /* istanbul ignore next */ []);
2482
+ dragDropHandler = input(...ngDevMode ? [void 0, { debugName: "dragDropHandler" }] : /* istanbul ignore next */ []);
2483
+ allowCollapse = input(true, ...ngDevMode ? [{ debugName: "allowCollapse" }] : /* istanbul ignore next */ []);
2484
+ addRule = output();
2485
+ addGroup = output();
2486
+ removeItem = output();
2487
+ updateRule = output();
2488
+ updateCombinator = output();
2489
+ collapsed = signal(false, ...ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []);
2490
+ canAddGroup = computed(() => this.depth() < this.maxDepth() - 1, ...ngDevMode ? [{ debugName: "canAddGroup" }] : /* istanbul ignore next */ []);
2491
+ nestingLimitHint = computed(() => `已达最大嵌套层级(${this.maxDepth()} 层)`, ...ngDevMode ? [{ debugName: "nestingLimitHint" }] : /* istanbul ignore next */ []);
2492
+ toggleCollapse() {
2493
+ this.collapsed.update((v) => !v);
2494
+ }
2495
+ toggleCombinator(combinator) {
2496
+ if (this.group().combinator !== combinator) this.updateCombinator.emit({
2497
+ id: this.group().id,
2498
+ combinator
2499
+ });
2500
+ }
2501
+ onAddRule() {
2502
+ const firstField = this.fields()[0];
2503
+ if (firstField) this.addRule.emit({
2504
+ parentId: this.group().id,
2505
+ rule: {
2506
+ field: firstField.name,
2507
+ operator: firstField.isRelation ? "exists" : firstField.type === "keyValue" ? "null" : "=",
2508
+ value: firstField.type === "boolean" ? false : ""
2509
+ }
2510
+ });
2511
+ }
2512
+ onAddGroup() {
2513
+ if (!this.canAddGroup()) return;
2514
+ this.addGroup.emit(this.group().id);
2515
+ }
2516
+ onRemove() {
2517
+ this.removeItem.emit(this.group().id);
2518
+ }
2519
+ onRuleUpdate(event) {
2520
+ this.updateRule.emit(event);
2521
+ }
2522
+ onRuleRemove(id) {
2523
+ this.removeItem.emit(id);
2524
+ }
2525
+ isRuleGroup(item) {
2526
+ return "combinator" in item && "rules" in item;
2527
+ }
2528
+ asRuleGroup(item) {
2529
+ return item;
2530
+ }
2531
+ asRule(item) {
2532
+ return item;
2533
+ }
2534
+ trackByItem(item) {
2535
+ return item.id;
2536
+ }
2537
+ isDragging(itemId) {
2538
+ return this.dragDropHandler()?.state().draggedItemId === itemId;
2539
+ }
2540
+ isDropTarget(itemId, mode) {
2541
+ const state = this.dragDropHandler()?.state();
2542
+ return !!state && state.targetItemId === itemId && state.dropMode === mode && state.isValidTarget;
2543
+ }
2544
+ isDropInvalid(itemId) {
2545
+ const state = this.dragDropHandler()?.state();
2546
+ return !!state && state.targetItemId === itemId && !state.isValidTarget && !!state.draggedItemId;
2547
+ }
2548
+ onItemDragStart(event, item) {
2549
+ const handler = this.dragDropHandler();
2550
+ if (!handler || !event.dataTransfer) return;
2551
+ event.stopPropagation();
2552
+ event.dataTransfer.effectAllowed = "move";
2553
+ event.dataTransfer.setData("text/plain", item.id);
2554
+ const row = event.target.closest(".rxdb-drag-item");
2555
+ if (row) {
2556
+ const rect = row.getBoundingClientRect();
2557
+ event.dataTransfer.setDragImage(row, event.clientX - rect.left, event.clientY - rect.top);
2558
+ }
2559
+ handler.start(item.id);
2560
+ }
2561
+ onItemDragOver(event, item, index, isGroup) {
2562
+ event.preventDefault();
2563
+ event.stopPropagation();
2564
+ const handler = this.dragDropHandler();
2565
+ if (!handler || !event.dataTransfer) return;
2566
+ const rect = event.currentTarget.getBoundingClientRect();
2567
+ const dropMode = calculateDropMode(event.clientY, rect, isGroup);
2568
+ const draggedId = handler.state().draggedItemId;
2569
+ const isValid = !!draggedId && draggedId !== item.id && (dropMode !== "into" || isGroup);
2570
+ event.dataTransfer.dropEffect = isValid ? "move" : "none";
2571
+ handler.over(item.id, dropMode, isValid, this.depth());
2572
+ }
2573
+ onItemDragLeave(event) {
2574
+ const target = event.currentTarget;
2575
+ const related = event.relatedTarget;
2576
+ if (!related || !target.contains(related)) this.dragDropHandler()?.leave();
2577
+ }
2578
+ onItemDrop(event, targetItem, targetIndex, isGroup) {
2579
+ event.preventDefault();
2580
+ event.stopPropagation();
2581
+ const handler = this.dragDropHandler();
2582
+ const state = handler?.state();
2583
+ if (!handler || !state?.draggedItemId || !state.isValidTarget || !state.dropMode) {
2584
+ handler?.end();
2585
+ return;
2586
+ }
2587
+ const dropMode = state.dropMode;
2588
+ let finalTargetGroupId;
2589
+ let finalTargetIndex;
2590
+ if (dropMode === "into" && isGroup) {
2591
+ const subGroup = targetItem;
2592
+ finalTargetGroupId = subGroup.id;
2593
+ finalTargetIndex = subGroup.rules.some((r) => r.id === state.draggedItemId) ? subGroup.rules.length - 1 : subGroup.rules.length;
2594
+ } else {
2595
+ finalTargetGroupId = this.group().id;
2596
+ const draggedIndex = this.group().rules.findIndex((r) => r.id === state.draggedItemId);
2597
+ const isInSameGroup = draggedIndex !== -1;
2598
+ let adjustedIndex = targetIndex;
2599
+ if (isInSameGroup && draggedIndex < targetIndex) adjustedIndex--;
2600
+ finalTargetIndex = dropMode === "before" ? adjustedIndex : adjustedIndex + 1;
2601
+ }
2602
+ handler.drop(state.draggedItemId, finalTargetGroupId, finalTargetIndex);
2603
+ }
2604
+ onItemDragEnd() {
2605
+ this.dragDropHandler()?.end();
2606
+ }
2607
+ static ɵfac = i0.ɵɵngDeclareFactory({
2608
+ minVersion: "12.0.0",
2609
+ version: "22.2.3",
2610
+ ngImport: i0,
2611
+ type: QueryGroupComponent,
2612
+ deps: [],
2613
+ target: i0.ɵɵFactoryTarget.Component
2614
+ });
2615
+ static ɵcmp = i0.ɵɵngDeclareComponent({
2616
+ minVersion: "17.0.0",
2617
+ version: "22.2.3",
2618
+ type: QueryGroupComponent,
2619
+ isStandalone: true,
2620
+ selector: "rxdb-query-group",
2621
+ inputs: {
2622
+ group: {
2623
+ classPropertyName: "group",
2624
+ publicName: "group",
2625
+ isSignal: true,
2626
+ isRequired: true,
2627
+ transformFunction: null
2628
+ },
2629
+ fields: {
2630
+ classPropertyName: "fields",
2631
+ publicName: "fields",
2632
+ isSignal: true,
2633
+ isRequired: true,
2634
+ transformFunction: null
2635
+ },
2636
+ errors: {
2637
+ classPropertyName: "errors",
2638
+ publicName: "errors",
2639
+ isSignal: true,
2640
+ isRequired: false,
2641
+ transformFunction: null
2642
+ },
2643
+ depth: {
2644
+ classPropertyName: "depth",
2645
+ publicName: "depth",
2646
+ isSignal: true,
2647
+ isRequired: false,
2648
+ transformFunction: null
2649
+ },
2650
+ maxDepth: {
2651
+ classPropertyName: "maxDepth",
2652
+ publicName: "maxDepth",
2653
+ isSignal: true,
2654
+ isRequired: false,
2655
+ transformFunction: null
2656
+ },
2657
+ dragDropHandler: {
2658
+ classPropertyName: "dragDropHandler",
2659
+ publicName: "dragDropHandler",
2660
+ isSignal: true,
2661
+ isRequired: false,
2662
+ transformFunction: null
2663
+ },
2664
+ allowCollapse: {
2665
+ classPropertyName: "allowCollapse",
2666
+ publicName: "allowCollapse",
2667
+ isSignal: true,
2668
+ isRequired: false,
2669
+ transformFunction: null
2670
+ }
2671
+ },
2672
+ outputs: {
2673
+ addRule: "addRule",
2674
+ addGroup: "addGroup",
2675
+ removeItem: "removeItem",
2676
+ updateRule: "updateRule",
2677
+ updateCombinator: "updateCombinator"
2678
+ },
2679
+ ngImport: i0,
2680
+ template: `
2681
+ <div class="rxdb-query-group border-base-300 my-1 rounded-lg border p-2" [class.bg-base-200]="depth() % 2 === 1">
2682
+ <!-- 组合器 + 操作按钮(同一行) -->
2683
+ <div class="flex items-center gap-2" [class.mb-2]="!collapsed()">
2684
+ <div class="join">
2685
+ <button
2686
+ class="btn btn-xs join-item"
2687
+ [class.btn-ghost]="group().combinator !== 'and'"
2688
+ [class.btn-primary]="group().combinator === 'and'"
2689
+ (click)="toggleCombinator('and')"
2690
+ type="button"
2691
+ >
2692
+ AND
2693
+ </button>
2694
+ <button
2695
+ class="btn btn-xs join-item"
2696
+ [class.btn-ghost]="group().combinator !== 'or'"
2697
+ [class.btn-primary]="group().combinator === 'or'"
2698
+ (click)="toggleCombinator('or')"
2699
+ type="button"
2700
+ >
2701
+ OR
2702
+ </button>
2703
+ </div>
2704
+
2705
+ @if (allowCollapse()) {
2706
+ <button
2707
+ class="btn btn-ghost btn-xs btn-square"
2708
+ [title]="collapsed() ? '展开' : '收起'"
2709
+ (click)="toggleCollapse()"
2710
+ type="button"
2711
+ >
2712
+ <svg
2713
+ class="h-3 w-3 transition-transform duration-200"
2714
+ [class.-rotate-90]="collapsed()"
2715
+ fill="none"
2716
+ stroke="currentColor"
2717
+ stroke-linecap="round"
2718
+ stroke-linejoin="round"
2719
+ stroke-width="2.5"
2720
+ viewBox="0 0 24 24"
2721
+ xmlns="http://www.w3.org/2000/svg"
2722
+ >
2723
+ <polyline points="6 9 12 15 18 9" />
2724
+ </svg>
2725
+ </button>
2726
+ }
2727
+
2728
+ <div class="flex-1"></div>
2729
+
2730
+ @if (!collapsed()) {
2731
+ <button class="btn btn-ghost btn-xs" (click)="onAddRule()" type="button">+ 条件</button>
2732
+
2733
+ <!--
2734
+ ⚠️ 达到上限时保留按钮并禁用,而不是整个条件渲染掉。
2735
+ 此前用条件渲染,按钮到达上限就直接消失、页面上零解释文案,
2736
+ 用户不知道是「不支持」还是「用错了」(US-210 AC#3 要求「可达上限;超过提示」)。
2737
+ -->
2738
+ <button
2739
+ class="btn btn-ghost btn-xs"
2740
+ [attr.aria-disabled]="!canAddGroup()"
2741
+ [attr.title]="canAddGroup() ? null : nestingLimitHint()"
2742
+ [disabled]="!canAddGroup()"
2743
+ (click)="onAddGroup()"
2744
+ type="button"
2745
+ >
2746
+ + 分组
2747
+ </button>
2748
+ @if (!canAddGroup()) {
2749
+ <span class="text-base-content/50 text-xs" role="status">{{ nestingLimitHint() }}</span>
2750
+ }
2751
+
2752
+ @if (depth() > 0) {
2753
+ <button
2754
+ class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-error"
2755
+ (click)="onRemove()"
2756
+ title="删除组"
2757
+ type="button"
2758
+ >
2759
+ ✕
2760
+ </button>
2761
+ }
2762
+ }
2763
+ </div>
2764
+
2765
+ <!-- 规则列表 -->
2766
+ @if (!collapsed()) {
2767
+ <div class="space-y-1">
2768
+ @for (item of group().rules; track trackByItem(item); let idx = $index) {
2769
+ @if (isRuleGroup(item)) {
2770
+ <div
2771
+ class="rxdb-drag-item"
2772
+ [class.dragging]="isDragging(item.id)"
2773
+ [class.drop-invalid]="isDropInvalid(item.id)"
2774
+ [class.drop-target-after]="isDropTarget(item.id, 'after')"
2775
+ [class.drop-target-before]="isDropTarget(item.id, 'before')"
2776
+ [class.drop-target-into]="isDropTarget(item.id, 'into')"
2777
+ (dragend)="onItemDragEnd()"
2778
+ (dragleave)="onItemDragLeave($event)"
2779
+ (dragover)="onItemDragOver($event, item, idx, true)"
2780
+ (drop)="onItemDrop($event, item, idx, true)"
2781
+ >
2782
+ <div class="flex items-start gap-1">
2783
+ @if (dragDropHandler()) {
2784
+ <span
2785
+ class="btn btn-ghost btn-xs btn-square text-base-content/30 hover:text-base-content/60 mt-1 cursor-grab"
2786
+ (dragstart)="onItemDragStart($event, item)"
2787
+ draggable="true"
2788
+ >⠿</span
2789
+ >
2790
+ }
2791
+ <div class="min-w-0 flex-1">
2792
+ <rxdb-query-group
2793
+ [allowCollapse]="allowCollapse()"
2794
+ [depth]="depth() + 1"
2795
+ [dragDropHandler]="dragDropHandler()"
2796
+ [errors]="errors()"
2797
+ [fields]="fields()"
2798
+ [group]="asRuleGroup(item)"
2799
+ [maxDepth]="maxDepth()"
2800
+ (addGroup)="addGroup.emit($event)"
2801
+ (addRule)="addRule.emit($event)"
2802
+ (removeItem)="removeItem.emit($event)"
2803
+ (updateCombinator)="updateCombinator.emit($event)"
2804
+ (updateRule)="updateRule.emit($event)"
2805
+ />
2806
+ </div>
2807
+ </div>
2808
+ </div>
2809
+ } @else {
2810
+ <div
2811
+ class="rxdb-drag-item"
2812
+ [class.dragging]="isDragging(item.id)"
2813
+ [class.drop-invalid]="isDropInvalid(item.id)"
2814
+ [class.drop-target-after]="isDropTarget(item.id, 'after')"
2815
+ [class.drop-target-before]="isDropTarget(item.id, 'before')"
2816
+ (dragend)="onItemDragEnd()"
2817
+ (dragleave)="onItemDragLeave($event)"
2818
+ (dragover)="onItemDragOver($event, item, idx, false)"
2819
+ (drop)="onItemDrop($event, item, idx, false)"
2820
+ >
2821
+ <div class="flex items-center gap-1">
2822
+ @if (dragDropHandler()) {
2823
+ <span
2824
+ class="btn btn-ghost btn-xs btn-square text-base-content/30 hover:text-base-content/60 cursor-grab"
2825
+ (dragstart)="onItemDragStart($event, item)"
2826
+ draggable="true"
2827
+ >⠿</span
2828
+ >
2829
+ }
2830
+ <div class="min-w-0 flex-1">
2831
+ <rxdb-query-rule
2832
+ [errors]="errors()"
2833
+ [fields]="fields()"
2834
+ [rule]="asRule(item)"
2835
+ (remove)="onRuleRemove(asRule(item).id)"
2836
+ (update)="onRuleUpdate($event)"
2837
+ />
2838
+ </div>
2839
+ </div>
2840
+ </div>
2841
+ }
2842
+ }
2843
+ </div>
2844
+
2845
+ <!-- 空状态 -->
2846
+ @if (group().rules.length === 0) {
2847
+ <div class="text-base-content/50 py-2 text-center text-sm select-none">暂无条件</div>
2848
+ }
2849
+ }
2850
+ </div>
2851
+ `,
2852
+ isInline: true,
2853
+ styles: [".rxdb-drag-item{position:relative}.rxdb-drag-item.dragging{opacity:.4}.rxdb-drag-item.drop-target-before:before{content:\"\";position:absolute;top:-2px;left:0;right:0;height:3px;background:oklch(.6 .2 250);border-radius:2px;z-index:1}.rxdb-drag-item.drop-target-after:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:3px;background:oklch(.6 .2 250);border-radius:2px;z-index:1}.rxdb-drag-item.drop-target-into{outline:2px dashed oklch(.6 .2 250);outline-offset:-2px;border-radius:.5rem;background:oklch(.6 .2 250 / 10%)}.rxdb-drag-item.drop-invalid{outline:2px dashed oklch(.6 .2 30);outline-offset:-2px;border-radius:.5rem}\n"],
2854
+ dependencies: [
2855
+ {
2856
+ kind: "component",
2857
+ type: QueryGroupComponent,
2858
+ selector: "rxdb-query-group",
2859
+ inputs: [
2860
+ "group",
2861
+ "fields",
2862
+ "errors",
2863
+ "depth",
2864
+ "maxDepth",
2865
+ "dragDropHandler",
2866
+ "allowCollapse"
2867
+ ],
2868
+ outputs: [
2869
+ "addRule",
2870
+ "addGroup",
2871
+ "removeItem",
2872
+ "updateRule",
2873
+ "updateCombinator"
2874
+ ]
2875
+ },
2876
+ {
2877
+ kind: "ngmodule",
2878
+ type: CommonModule
2879
+ },
2880
+ {
2881
+ kind: "component",
2882
+ type: QueryRuleComponent,
2883
+ selector: "rxdb-query-rule",
2884
+ inputs: [
2885
+ "rule",
2886
+ "fields",
2887
+ "errors"
2888
+ ],
2889
+ outputs: ["update", "remove"]
2890
+ }
2891
+ ],
2892
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
2893
+ });
2894
+ };
2895
+ i0.ɵɵngDeclareClassMetadata({
2896
+ minVersion: "12.0.0",
2897
+ version: "22.2.3",
2898
+ ngImport: i0,
2899
+ type: QueryGroupComponent,
2900
+ decorators: [{
2901
+ type: Component,
2902
+ args: [{
2903
+ selector: "rxdb-query-group",
2904
+ standalone: true,
2905
+ imports: [CommonModule, QueryRuleComponent],
2906
+ template: `
2907
+ <div class="rxdb-query-group border-base-300 my-1 rounded-lg border p-2" [class.bg-base-200]="depth() % 2 === 1">
2908
+ <!-- 组合器 + 操作按钮(同一行) -->
2909
+ <div class="flex items-center gap-2" [class.mb-2]="!collapsed()">
2910
+ <div class="join">
2911
+ <button
2912
+ class="btn btn-xs join-item"
2913
+ [class.btn-ghost]="group().combinator !== 'and'"
2914
+ [class.btn-primary]="group().combinator === 'and'"
2915
+ (click)="toggleCombinator('and')"
2916
+ type="button"
2917
+ >
2918
+ AND
2919
+ </button>
2920
+ <button
2921
+ class="btn btn-xs join-item"
2922
+ [class.btn-ghost]="group().combinator !== 'or'"
2923
+ [class.btn-primary]="group().combinator === 'or'"
2924
+ (click)="toggleCombinator('or')"
2925
+ type="button"
2926
+ >
2927
+ OR
2928
+ </button>
2929
+ </div>
2930
+
2931
+ @if (allowCollapse()) {
2932
+ <button
2933
+ class="btn btn-ghost btn-xs btn-square"
2934
+ [title]="collapsed() ? '展开' : '收起'"
2935
+ (click)="toggleCollapse()"
2936
+ type="button"
2937
+ >
2938
+ <svg
2939
+ class="h-3 w-3 transition-transform duration-200"
2940
+ [class.-rotate-90]="collapsed()"
2941
+ fill="none"
2942
+ stroke="currentColor"
2943
+ stroke-linecap="round"
2944
+ stroke-linejoin="round"
2945
+ stroke-width="2.5"
2946
+ viewBox="0 0 24 24"
2947
+ xmlns="http://www.w3.org/2000/svg"
2948
+ >
2949
+ <polyline points="6 9 12 15 18 9" />
2950
+ </svg>
2951
+ </button>
2952
+ }
2953
+
2954
+ <div class="flex-1"></div>
2955
+
2956
+ @if (!collapsed()) {
2957
+ <button class="btn btn-ghost btn-xs" (click)="onAddRule()" type="button">+ 条件</button>
2958
+
2959
+ <!--
2960
+ ⚠️ 达到上限时保留按钮并禁用,而不是整个条件渲染掉。
2961
+ 此前用条件渲染,按钮到达上限就直接消失、页面上零解释文案,
2962
+ 用户不知道是「不支持」还是「用错了」(US-210 AC#3 要求「可达上限;超过提示」)。
2963
+ -->
2964
+ <button
2965
+ class="btn btn-ghost btn-xs"
2966
+ [attr.aria-disabled]="!canAddGroup()"
2967
+ [attr.title]="canAddGroup() ? null : nestingLimitHint()"
2968
+ [disabled]="!canAddGroup()"
2969
+ (click)="onAddGroup()"
2970
+ type="button"
2971
+ >
2972
+ + 分组
2973
+ </button>
2974
+ @if (!canAddGroup()) {
2975
+ <span class="text-base-content/50 text-xs" role="status">{{ nestingLimitHint() }}</span>
2976
+ }
2977
+
2978
+ @if (depth() > 0) {
2979
+ <button
2980
+ class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-error"
2981
+ (click)="onRemove()"
2982
+ title="删除组"
2983
+ type="button"
2984
+ >
2985
+ ✕
2986
+ </button>
2987
+ }
2988
+ }
2989
+ </div>
2990
+
2991
+ <!-- 规则列表 -->
2992
+ @if (!collapsed()) {
2993
+ <div class="space-y-1">
2994
+ @for (item of group().rules; track trackByItem(item); let idx = $index) {
2995
+ @if (isRuleGroup(item)) {
2996
+ <div
2997
+ class="rxdb-drag-item"
2998
+ [class.dragging]="isDragging(item.id)"
2999
+ [class.drop-invalid]="isDropInvalid(item.id)"
3000
+ [class.drop-target-after]="isDropTarget(item.id, 'after')"
3001
+ [class.drop-target-before]="isDropTarget(item.id, 'before')"
3002
+ [class.drop-target-into]="isDropTarget(item.id, 'into')"
3003
+ (dragend)="onItemDragEnd()"
3004
+ (dragleave)="onItemDragLeave($event)"
3005
+ (dragover)="onItemDragOver($event, item, idx, true)"
3006
+ (drop)="onItemDrop($event, item, idx, true)"
3007
+ >
3008
+ <div class="flex items-start gap-1">
3009
+ @if (dragDropHandler()) {
3010
+ <span
3011
+ class="btn btn-ghost btn-xs btn-square text-base-content/30 hover:text-base-content/60 mt-1 cursor-grab"
3012
+ (dragstart)="onItemDragStart($event, item)"
3013
+ draggable="true"
3014
+ >⠿</span
3015
+ >
3016
+ }
3017
+ <div class="min-w-0 flex-1">
3018
+ <rxdb-query-group
3019
+ [allowCollapse]="allowCollapse()"
3020
+ [depth]="depth() + 1"
3021
+ [dragDropHandler]="dragDropHandler()"
3022
+ [errors]="errors()"
3023
+ [fields]="fields()"
3024
+ [group]="asRuleGroup(item)"
3025
+ [maxDepth]="maxDepth()"
3026
+ (addGroup)="addGroup.emit($event)"
3027
+ (addRule)="addRule.emit($event)"
3028
+ (removeItem)="removeItem.emit($event)"
3029
+ (updateCombinator)="updateCombinator.emit($event)"
3030
+ (updateRule)="updateRule.emit($event)"
3031
+ />
3032
+ </div>
3033
+ </div>
3034
+ </div>
3035
+ } @else {
3036
+ <div
3037
+ class="rxdb-drag-item"
3038
+ [class.dragging]="isDragging(item.id)"
3039
+ [class.drop-invalid]="isDropInvalid(item.id)"
3040
+ [class.drop-target-after]="isDropTarget(item.id, 'after')"
3041
+ [class.drop-target-before]="isDropTarget(item.id, 'before')"
3042
+ (dragend)="onItemDragEnd()"
3043
+ (dragleave)="onItemDragLeave($event)"
3044
+ (dragover)="onItemDragOver($event, item, idx, false)"
3045
+ (drop)="onItemDrop($event, item, idx, false)"
3046
+ >
3047
+ <div class="flex items-center gap-1">
3048
+ @if (dragDropHandler()) {
3049
+ <span
3050
+ class="btn btn-ghost btn-xs btn-square text-base-content/30 hover:text-base-content/60 cursor-grab"
3051
+ (dragstart)="onItemDragStart($event, item)"
3052
+ draggable="true"
3053
+ >⠿</span
3054
+ >
3055
+ }
3056
+ <div class="min-w-0 flex-1">
3057
+ <rxdb-query-rule
3058
+ [errors]="errors()"
3059
+ [fields]="fields()"
3060
+ [rule]="asRule(item)"
3061
+ (remove)="onRuleRemove(asRule(item).id)"
3062
+ (update)="onRuleUpdate($event)"
3063
+ />
3064
+ </div>
3065
+ </div>
3066
+ </div>
3067
+ }
3068
+ }
3069
+ </div>
3070
+
3071
+ <!-- 空状态 -->
3072
+ @if (group().rules.length === 0) {
3073
+ <div class="text-base-content/50 py-2 text-center text-sm select-none">暂无条件</div>
3074
+ }
3075
+ }
3076
+ </div>
3077
+ `,
3078
+ changeDetection: ChangeDetectionStrategy.OnPush,
3079
+ styles: [".rxdb-drag-item{position:relative}.rxdb-drag-item.dragging{opacity:.4}.rxdb-drag-item.drop-target-before:before{content:\"\";position:absolute;top:-2px;left:0;right:0;height:3px;background:oklch(.6 .2 250);border-radius:2px;z-index:1}.rxdb-drag-item.drop-target-after:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:3px;background:oklch(.6 .2 250);border-radius:2px;z-index:1}.rxdb-drag-item.drop-target-into{outline:2px dashed oklch(.6 .2 250);outline-offset:-2px;border-radius:.5rem;background:oklch(.6 .2 250 / 10%)}.rxdb-drag-item.drop-invalid{outline:2px dashed oklch(.6 .2 30);outline-offset:-2px;border-radius:.5rem}\n"]
3080
+ }]
3081
+ }],
3082
+ propDecorators: {
3083
+ group: [{
3084
+ type: i0.Input,
3085
+ args: [{
3086
+ isSignal: true,
3087
+ alias: "group",
3088
+ required: true
3089
+ }]
3090
+ }],
3091
+ fields: [{
3092
+ type: i0.Input,
3093
+ args: [{
3094
+ isSignal: true,
3095
+ alias: "fields",
3096
+ required: true
3097
+ }]
3098
+ }],
3099
+ errors: [{
3100
+ type: i0.Input,
3101
+ args: [{
3102
+ isSignal: true,
3103
+ alias: "errors",
3104
+ required: false
3105
+ }]
3106
+ }],
3107
+ depth: [{
3108
+ type: i0.Input,
3109
+ args: [{
3110
+ isSignal: true,
3111
+ alias: "depth",
3112
+ required: false
3113
+ }]
3114
+ }],
3115
+ maxDepth: [{
3116
+ type: i0.Input,
3117
+ args: [{
3118
+ isSignal: true,
3119
+ alias: "maxDepth",
3120
+ required: false
3121
+ }]
3122
+ }],
3123
+ dragDropHandler: [{
3124
+ type: i0.Input,
3125
+ args: [{
3126
+ isSignal: true,
3127
+ alias: "dragDropHandler",
3128
+ required: false
3129
+ }]
3130
+ }],
3131
+ allowCollapse: [{
3132
+ type: i0.Input,
3133
+ args: [{
3134
+ isSignal: true,
3135
+ alias: "allowCollapse",
3136
+ required: false
3137
+ }]
3138
+ }],
3139
+ addRule: [{
3140
+ type: i0.Output,
3141
+ args: ["addRule"]
3142
+ }],
3143
+ addGroup: [{
3144
+ type: i0.Output,
3145
+ args: ["addGroup"]
3146
+ }],
3147
+ removeItem: [{
3148
+ type: i0.Output,
3149
+ args: ["removeItem"]
3150
+ }],
3151
+ updateRule: [{
3152
+ type: i0.Output,
3153
+ args: ["updateRule"]
3154
+ }],
3155
+ updateCombinator: [{
3156
+ type: i0.Output,
3157
+ args: ["updateCombinator"]
3158
+ }]
3159
+ }
3160
+ });
3161
+ export { QUERY_BUILDER_THEME as a, ValueInputComponent as c, FieldSelectorComponent as d, TreeItemDirective as f, QueryRuleComponent as i, OperatorSelectorComponent as l, QueryGroupComponent as n, provideQueryBuilderTheme as o, TreeSelectComponent as p, calculateDropMode as r, DEFAULT_QUERY_BUILDER_THEME as s, QueryDragDropHandler as t, PopoverSelectComponent as u };
3162
+
3163
+ //# sourceMappingURL=aiao-rxdb-model-angular-query-group.component-DphJnDon.mjs.map