overview-components 1.1.181 → 1.1.182

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.
@@ -34,7 +34,7 @@ export interface ColumnFilterServer {
34
34
  }
35
35
  export type ColumnCustomFilter = FromSchema<typeof litDataGridTanstackColumnCustomFilterSchema>;
36
36
  export type TColumnVisibility = Record<string, boolean>;
37
- export type ColumnType = 'string' | 'number' | 'date' | 'range' | 'select' | 'multiselect' | 'dateTime' | 'actions' | 'dateTimeRange' | 'dateRange' | 'numberRange' | 'currency' | 'thumbnail';
37
+ export type ColumnType = 'string' | 'number' | 'date' | 'range' | 'select' | 'multiselect' | 'includeExclude' | 'dateTime' | 'actions' | 'dateTimeRange' | 'dateRange' | 'numberRange' | 'currency' | 'thumbnail';
38
38
  export type Density = 'compact' | 'standard' | 'comfort';
39
39
  export type DateFormats = null | 'dd.MM.yyyy' | 'd.M.yyyy' | 'dd/MM/yyyy' | 'MM/dd/yyyy' | 'yyyy-MM-dd';
40
40
  export interface ColumnCustomExtension {
@@ -34,7 +34,7 @@ import './components-settings/data-grid-settings.js';
34
34
  // utils
35
35
  import { formatDate } from '../utils/date.js';
36
36
  import { formatCurrency, formatDecimal } from '../utils/formatNumber.js';
37
- import { dateFilterFn, multiselectFilterFn, dateRangeFilterFn } from '../utils/custom-filters.js';
37
+ import { dateFilterFn, multiselectFilterFn, dateRangeFilterFn, includeExcludeFilterFn, } from '../utils/custom-filters.js';
38
38
  import { setLocale, getLocale, LOCALE_LANGS } from '../utils/localization.js';
39
39
  import { getOperatorsByColumnType } from '../utils/getOperatorByType.js';
40
40
  import { tooltip, overflowTooltip } from '../shared/simple-tooltip.js';
@@ -568,6 +568,7 @@ export class LitDataGridTanstack extends LitElement {
568
568
  numberRange: filterFns.inNumberRange,
569
569
  select: filterFns.weakEquals,
570
570
  multiselect: multiselectFilterFn,
571
+ includeExclude: includeExcludeFilterFn,
571
572
  number: filterFns.weakEquals,
572
573
  currency: filterFns.weakEquals,
573
574
  dateRange: dateRangeFilterFn,
@@ -1097,7 +1098,9 @@ export class LitDataGridTanstack extends LitElement {
1097
1098
  // can actually be used. The faceted model is expensive because
1098
1099
  // FilterInputs requests unique values for every filterable column on
1099
1100
  // mount; the grouped/expanded models only matter when grouping is on.
1100
- const hasSelectColumn = (this.columns || []).some((c) => c.type === 'select' || c.type === 'multiselect');
1101
+ const hasSelectColumn = (this.columns || []).some((c) => c.type === 'select' ||
1102
+ c.type === 'multiselect' ||
1103
+ c.type === 'includeExclude');
1101
1104
  const hasGroupableColumn = (this.initialGroups?.length ?? 0) > 0 ||
1102
1105
  ((this.enableGrouping ?? true) &&
1103
1106
  (this.columns || []).some((c) => c.enableGrouping ?? true));
@@ -1142,6 +1145,7 @@ export class LitDataGridTanstack extends LitElement {
1142
1145
  dateRangeFilterFn: dateRangeFilterFn,
1143
1146
  dateFilterFn: dateFilterFn,
1144
1147
  multiselectFilterFn: multiselectFilterFn,
1148
+ includeExcludeFilterFn: includeExcludeFilterFn,
1145
1149
  },
1146
1150
  // debugTable: true,
1147
1151
  enableRowSelection: this.enableRowSelection,
@@ -8,7 +8,7 @@ export declare const litDataGridTanstackColumnArraySchema: {
8
8
  };
9
9
  readonly type: {
10
10
  readonly type: "string";
11
- readonly enum: readonly ["string", "number", "date", "range", "select", "multiselect", "dateTime", "actions", "dateTimeRange", "dateRange", "numberRange", "currency", "thumbnail"];
11
+ readonly enum: readonly ["string", "number", "date", "range", "select", "multiselect", "includeExclude", "dateTime", "actions", "dateTimeRange", "dateRange", "numberRange", "currency", "thumbnail"];
12
12
  };
13
13
  readonly headerName: {
14
14
  readonly type: "string";
@@ -6,7 +6,7 @@ export declare const litDataGridTanstackColumnSchema: {
6
6
  };
7
7
  readonly type: {
8
8
  readonly type: "string";
9
- readonly enum: readonly ["string", "number", "date", "range", "select", "multiselect", "dateTime", "actions", "dateTimeRange", "dateRange", "numberRange", "currency", "thumbnail"];
9
+ readonly enum: readonly ["string", "number", "date", "range", "select", "multiselect", "includeExclude", "dateTime", "actions", "dateTimeRange", "dateRange", "numberRange", "currency", "thumbnail"];
10
10
  };
11
11
  readonly headerName: {
12
12
  readonly type: "string";
@@ -12,6 +12,7 @@ export const litDataGridTanstackColumnSchema = {
12
12
  'range',
13
13
  'select',
14
14
  'multiselect',
15
+ 'includeExclude',
15
16
  'dateTime',
16
17
  'actions',
17
18
  'dateTimeRange',
@@ -17,7 +17,7 @@ export declare const litDataGridTanstackSchema: {
17
17
  };
18
18
  readonly type: {
19
19
  readonly type: "string";
20
- readonly enum: readonly ["string", "number", "date", "range", "select", "multiselect", "dateTime", "actions", "dateTimeRange", "dateRange", "numberRange", "currency", "thumbnail"];
20
+ readonly enum: readonly ["string", "number", "date", "range", "select", "multiselect", "includeExclude", "dateTime", "actions", "dateTimeRange", "dateRange", "numberRange", "currency", "thumbnail"];
21
21
  };
22
22
  readonly headerName: {
23
23
  readonly type: "string";
@@ -2,6 +2,7 @@ import { LitElement } from 'lit';
2
2
  import './lit-icon.js';
3
3
  import './lit-icon-button.js';
4
4
  import './lit-select.js';
5
+ import './lit-include-exclude-select.js';
5
6
  import './lit-date-picker.js';
6
7
  import './lit-data-grid-operators-popover.js';
7
8
  import './lit-input.js';
@@ -28,6 +29,10 @@ export declare class FilterInputs extends LitElement {
28
29
  checkNumberOfCharacters(value: string, cf: Function): void;
29
30
  willUpdate(changedProperties: any): void;
30
31
  handleSetFilter(value: any): void;
32
+ handleSetIncludeExcludeFilter(value: {
33
+ in: string[];
34
+ nin: string[];
35
+ }): void;
31
36
  private handleSetOperator;
32
37
  handleClearFilter(): void;
33
38
  private handleDateChange;
@@ -39,6 +44,7 @@ export declare class FilterInputs extends LitElement {
39
44
  focus(): void;
40
45
  renderNumberRangeInput(): import("lit-html").TemplateResult<1>;
41
46
  renderSelectInput(multiselect?: boolean): import("lit-html").TemplateResult<1>;
47
+ renderIncludeExcludeInput(): import("lit-html").TemplateResult<1>;
42
48
  renderNumberInput(): import("lit-html").TemplateResult<1>;
43
49
  renderDateInput(): import("lit-html").TemplateResult<1>;
44
50
  renderDateRangeInput(): import("lit-html").TemplateResult<1>;
@@ -12,6 +12,7 @@ import { property, state } from 'lit/decorators.js';
12
12
  import './lit-icon.js';
13
13
  import './lit-icon-button.js';
14
14
  import './lit-select.js';
15
+ import './lit-include-exclude-select.js';
15
16
  import './lit-date-picker.js';
16
17
  import './lit-data-grid-operators-popover.js';
17
18
  import './lit-input.js';
@@ -69,7 +70,9 @@ export class FilterInputs extends LitElement {
69
70
  }
70
71
  willUpdate(changedProperties) {
71
72
  if (changedProperties.has('column')) {
72
- if (this.filterVariant === 'select' || this.filterVariant === 'multiselect') {
73
+ if (this.filterVariant === 'select' ||
74
+ this.filterVariant === 'multiselect' ||
75
+ this.filterVariant === 'includeExclude') {
73
76
  if (this.column?.columnDef?.meta.valueOptions || this.server) {
74
77
  this.valueOptions = this.column?.columnDef?.meta.valueOptions || [];
75
78
  }
@@ -103,6 +106,21 @@ export class FilterInputs extends LitElement {
103
106
  this.column?.setFilterValue();
104
107
  }
105
108
  }
109
+ handleSetIncludeExcludeFilter(value) {
110
+ const isEmpty = (value?.in?.length ?? 0) === 0 && (value?.nin?.length ?? 0) === 0;
111
+ this.value = isEmpty ? null : value;
112
+ if (isEmpty) {
113
+ // {in:[],nin:[]} is not lodash-empty, so clear explicitly to drop the
114
+ // active-filter dot (column.getIsFiltered() -> false).
115
+ this.column?.setFilterValue();
116
+ }
117
+ else if (this.server) {
118
+ this.column?.setFilterValue({ value, operator: 'includeExclude' });
119
+ }
120
+ else {
121
+ this.column?.setFilterValue(value);
122
+ }
123
+ }
106
124
  handleClearFilter() {
107
125
  if (this.server) {
108
126
  this.operator = this.filterOperators[0].value;
@@ -192,6 +210,11 @@ export class FilterInputs extends LitElement {
192
210
  if (selects) {
193
211
  selects.forEach((select) => elements.push(select));
194
212
  }
213
+ // Check for lit-include-exclude-select components
214
+ const includeExcludes = this.shadowRoot?.querySelectorAll('lit-include-exclude-select');
215
+ if (includeExcludes) {
216
+ includeExcludes.forEach((el) => elements.push(el));
217
+ }
195
218
  // Check for lit-date-picker components
196
219
  const datePickers = this.shadowRoot?.querySelectorAll('lit-date-picker');
197
220
  if (datePickers) {
@@ -200,7 +223,7 @@ export class FilterInputs extends LitElement {
200
223
  return elements;
201
224
  }
202
225
  focus() {
203
- const firstFocusable = this.shadowRoot?.querySelector('lit-input, lit-select, lit-date-picker');
226
+ const firstFocusable = this.shadowRoot?.querySelector('lit-input, lit-select, lit-include-exclude-select, lit-date-picker');
204
227
  firstFocusable?.focus();
205
228
  }
206
229
  renderNumberRangeInput() {
@@ -266,6 +289,20 @@ export class FilterInputs extends LitElement {
266
289
  ></lit-select>
267
290
  `;
268
291
  }
292
+ renderIncludeExcludeInput() {
293
+ const current = this.value && typeof this.value === 'object' && !Array.isArray(this.value)
294
+ ? { in: this.value.in ?? [], nin: this.value.nin ?? [] }
295
+ : { in: [], nin: [] };
296
+ return html `
297
+ <lit-include-exclude-select
298
+ id=${this.column.id}
299
+ .value="${current}"
300
+ .options="${this.valueOptions}"
301
+ .onChange="${(v) => this.handleSetIncludeExcludeFilter(v)}"
302
+ @keydown=${(e) => this.handleKeyDown(e)}
303
+ ></lit-include-exclude-select>
304
+ `;
305
+ }
269
306
  renderNumberInput() {
270
307
  return html `
271
308
  <lit-input
@@ -363,6 +400,9 @@ export class FilterInputs extends LitElement {
363
400
  else if (this.filterVariant === 'multiselect') {
364
401
  return this.renderSelectInput(true);
365
402
  }
403
+ else if (this.filterVariant === 'includeExclude') {
404
+ return this.renderIncludeExcludeInput();
405
+ }
366
406
  else if (this.filterVariant === 'number') {
367
407
  return this.renderNumberInput();
368
408
  }
@@ -6,6 +6,7 @@ export declare class LitCheckbox extends LitElement {
6
6
  disabled: boolean;
7
7
  checked: boolean;
8
8
  indeterminate: boolean;
9
+ cross: boolean;
9
10
  onChange: (e: Event) => void;
10
11
  value: string;
11
12
  label: string;
@@ -15,6 +15,7 @@ export class LitCheckbox extends LitElement {
15
15
  this.disabled = false;
16
16
  this.checked = false;
17
17
  this.indeterminate = false;
18
+ this.cross = false;
18
19
  this.onChange = () => { };
19
20
  this.value = '';
20
21
  this.label = '';
@@ -68,6 +69,7 @@ export class LitCheckbox extends LitElement {
68
69
  id="${this.id || `checkbox-${Math.random().toString(36).substring(2, 11)}`}"
69
70
  class=${classMap({
70
71
  indeterminate: this.indeterminate,
72
+ cross: this.cross,
71
73
  marginLeft: this.label !== '',
72
74
  })}
73
75
  type="checkbox"
@@ -123,11 +125,24 @@ LitCheckbox.styles = css `
123
125
  font-size: 16px;
124
126
  }
125
127
 
128
+ /* Cross state: same filled-box design as :checked, but a cross glyph
129
+ (used by the tri-state include/exclude filter to mark exclusion). */
130
+ input[type='checkbox'].cross {
131
+ background-color: var(--color-error-main, #ca2d0a);
132
+ border-color: var(--color-error-main, #ca2d0a);
133
+ }
134
+
135
+ input[type='checkbox'].cross::after {
136
+ content: '✕';
137
+ color: var(--background-paper, #fff);
138
+ font-size: 13px;
139
+ }
140
+
126
141
  /* Custom checkmark */
127
142
  input[type='checkbox']::after {
128
143
  content: '✓';
129
144
  color: var(--background-paper, #111827);
130
- font-size: 16px;
145
+ font-size: 13px;
131
146
  visible: none;
132
147
  }
133
148
 
@@ -166,6 +181,9 @@ __decorate([
166
181
  __decorate([
167
182
  property({ type: Boolean })
168
183
  ], LitCheckbox.prototype, "indeterminate", void 0);
184
+ __decorate([
185
+ property({ type: Boolean })
186
+ ], LitCheckbox.prototype, "cross", void 0);
169
187
  __decorate([
170
188
  property({ type: Function })
171
189
  ], LitCheckbox.prototype, "onChange", void 0);
@@ -0,0 +1,59 @@
1
+ import { LitElement } from 'lit';
2
+ import './simple-popper.js';
3
+ import './lit-icon.js';
4
+ import './lit-icon-button.js';
5
+ import './lit-checkbox.js';
6
+ import './lit-menu-item.js';
7
+ import './lit-menu.js';
8
+ export type IncludeExcludeValue = {
9
+ in: string[];
10
+ nin: string[];
11
+ };
12
+ export type IncludeExcludeState = 'in' | 'nin' | 'default';
13
+ export type IncludeExcludeSelectProps = {
14
+ id: string;
15
+ value?: IncludeExcludeValue;
16
+ options: {
17
+ value: string;
18
+ label: string;
19
+ }[];
20
+ onChange: (value: IncludeExcludeValue) => void;
21
+ disabled?: boolean;
22
+ placeholder?: string;
23
+ };
24
+ export declare class LitIncludeExcludeSelect extends LitElement {
25
+ id: string;
26
+ value: IncludeExcludeValue;
27
+ options: {
28
+ value: string;
29
+ label: string;
30
+ }[];
31
+ onChange: (value: IncludeExcludeValue) => void;
32
+ disabled: boolean;
33
+ placeholder: string;
34
+ private isOpen;
35
+ private searchQuery;
36
+ static styles: import("lit").CSSResult;
37
+ connectedCallback(): void;
38
+ disconnectedCallback(): void;
39
+ private handleOutsideClick;
40
+ private getState;
41
+ private cycleOption;
42
+ private clearValue;
43
+ private isEmptyValue;
44
+ private toggleCustomPopover;
45
+ private closePopover;
46
+ private handleInput;
47
+ private getFilteredOptions;
48
+ focus(): void;
49
+ private handleKeyDown;
50
+ private labelOf;
51
+ private displayValue;
52
+ private menuTemplate;
53
+ render(): import("lit-html").TemplateResult<1>;
54
+ }
55
+ declare global {
56
+ interface HTMLElementTagNameMap {
57
+ 'lit-include-exclude-select': LitIncludeExcludeSelect;
58
+ }
59
+ }
@@ -0,0 +1,293 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { LitElement, html, css, nothing } from 'lit';
8
+ import { msg } from '@lit/localize';
9
+ import { classMap } from 'lit/directives/class-map.js';
10
+ import { property, state } from 'lit/decorators.js';
11
+ import './simple-popper.js';
12
+ import './lit-icon.js';
13
+ import './lit-icon-button.js';
14
+ import './lit-checkbox.js';
15
+ import './lit-menu-item.js';
16
+ import './lit-menu.js';
17
+ // Tri-state multiselect ("includeExclude") filter. Each option cycles through
18
+ // three states on click: default -> in (include) -> nin (exclude) -> default.
19
+ // Modeled on lit-select: input box + simple-popper + lit-menu, with the menu
20
+ // rendered only while open to keep the closed steady-state cheap.
21
+ export class LitIncludeExcludeSelect extends LitElement {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.id = '';
25
+ this.value = { in: [], nin: [] };
26
+ this.options = [];
27
+ this.onChange = () => { };
28
+ this.disabled = false;
29
+ this.placeholder = '';
30
+ this.isOpen = false;
31
+ this.searchQuery = null;
32
+ this.handleOutsideClick = (event) => {
33
+ const path = event.composedPath();
34
+ if (!path.includes(this) &&
35
+ !path.some((element) => element instanceof HTMLElement && element.tagName === 'SIMPLE-POPPER')) {
36
+ this.closePopover();
37
+ }
38
+ };
39
+ }
40
+ connectedCallback() {
41
+ super.connectedCallback();
42
+ document.addEventListener('click', this.handleOutsideClick);
43
+ }
44
+ disconnectedCallback() {
45
+ super.disconnectedCallback();
46
+ document.removeEventListener('click', this.handleOutsideClick);
47
+ this.closePopover();
48
+ }
49
+ getState(optionValue) {
50
+ if (this.value?.in?.includes(optionValue))
51
+ return 'in';
52
+ if (this.value?.nin?.includes(optionValue))
53
+ return 'nin';
54
+ return 'default';
55
+ }
56
+ // default -> in -> nin -> default
57
+ cycleOption(optionValue) {
58
+ const current = this.getState(optionValue);
59
+ const nextIn = (this.value?.in ?? []).filter((v) => v !== optionValue);
60
+ const nextNin = (this.value?.nin ?? []).filter((v) => v !== optionValue);
61
+ if (current === 'default') {
62
+ nextIn.push(optionValue);
63
+ }
64
+ else if (current === 'in') {
65
+ nextNin.push(optionValue);
66
+ }
67
+ // current === 'nin' -> default: leave the value out of both lists.
68
+ const newValue = { in: nextIn, nin: nextNin };
69
+ this.value = newValue;
70
+ this.onChange(newValue);
71
+ }
72
+ clearValue(e) {
73
+ e.stopPropagation();
74
+ const cleared = { in: [], nin: [] };
75
+ this.value = cleared;
76
+ this.onChange(cleared);
77
+ this.searchQuery = null;
78
+ }
79
+ isEmptyValue() {
80
+ return (this.value?.in?.length ?? 0) === 0 && (this.value?.nin?.length ?? 0) === 0;
81
+ }
82
+ toggleCustomPopover(event) {
83
+ event?.preventDefault();
84
+ this.isOpen = !this.isOpen;
85
+ }
86
+ closePopover() {
87
+ this.isOpen = false;
88
+ this.searchQuery = null;
89
+ }
90
+ handleInput(event) {
91
+ this.searchQuery = event.target.value;
92
+ this.isOpen = true;
93
+ }
94
+ getFilteredOptions() {
95
+ if (!this.searchQuery) {
96
+ return this.options;
97
+ }
98
+ const query = this.searchQuery.toLowerCase();
99
+ return this.options.filter((option) => option.label?.toLowerCase().includes(query));
100
+ }
101
+ focus() {
102
+ this.shadowRoot?.querySelector('input')?.focus();
103
+ }
104
+ handleKeyDown(event) {
105
+ if (event.key === 'Tab' || event.key === 'Escape') {
106
+ this.closePopover();
107
+ }
108
+ }
109
+ labelOf(value) {
110
+ return this.options.find((option) => option.value == value)?.label ?? value;
111
+ }
112
+ displayValue() {
113
+ // Single comma-separated list of every selected option; excluded ones get
114
+ // a leading "!" (e.g. "Active, !Pending").
115
+ const include = (this.value?.in ?? []).map((v) => this.labelOf(v));
116
+ const exclude = (this.value?.nin ?? []).map((v) => `!${this.labelOf(v)}`);
117
+ return [...include, ...exclude].join(', ');
118
+ }
119
+ menuTemplate(selectOptions) {
120
+ return html `
121
+ <lit-menu tabindex="0" id="menu">
122
+ ${selectOptions?.length === 0
123
+ ? html `<lit-menu-item .disabled=${true}>${msg('Nenalezeno')}</lit-menu-item>`
124
+ : selectOptions?.map((option, index) => {
125
+ const state = this.getState(option.value);
126
+ return html `
127
+ <lit-menu-item
128
+ id="${this.id}-${index}"
129
+ .onClick="${() => this.cycleOption(option.value)}"
130
+ >
131
+ <span class="option-row">
132
+ <lit-checkbox
133
+ class="cursor"
134
+ .checked=${state === 'in'}
135
+ .cross=${state === 'nin'}
136
+ ></lit-checkbox>
137
+ ${option.label}
138
+ </span>
139
+ </lit-menu-item>
140
+ `;
141
+ })}
142
+ </lit-menu>
143
+ `;
144
+ }
145
+ render() {
146
+ const selectOptions = this.getFilteredOptions();
147
+ const dropdownIconClasses = {
148
+ 'dropdown-icon': true,
149
+ 'dropdown-icon--rotate': this.isOpen,
150
+ };
151
+ const display = this.isEmptyValue() ? '' : this.displayValue();
152
+ return html `
153
+ <div class="custom-icon-wrapper" slot="reference">
154
+ <input
155
+ id="${this.id}"
156
+ type="text"
157
+ placeholder="${this.placeholder}"
158
+ .value="${this.searchQuery ?? display ?? ''}"
159
+ @input=${this.handleInput}
160
+ @click="${(e) => this.toggleCustomPopover(e)}"
161
+ @keydown=${this.handleKeyDown}
162
+ .disabled="${this.disabled}"
163
+ />
164
+ ${!this.isEmptyValue()
165
+ ? html `
166
+ <div class="custom-icon" @click="${this.clearValue}">
167
+ <lit-icon-button
168
+ icon="close"
169
+ size="small"
170
+ variant="text"
171
+ color="secondary"
172
+ ></lit-icon-button>
173
+ </div>
174
+ `
175
+ : ''}
176
+ <div class="${classMap(dropdownIconClasses)}">
177
+ <lit-icon
178
+ class="${this.disabled ? 'dropdown-icon-disabled' : null}"
179
+ icon="chevrondown"
180
+ size="17px"
181
+ ></lit-icon>
182
+ </div>
183
+ </div>
184
+ <simple-popper
185
+ .showing=${this.isOpen}
186
+ placement="bottom-start"
187
+ @close="${this.closePopover}"
188
+ manualOpening=${true}
189
+ maxWidthAsTarget=${true}
190
+ @keydown=${this.handleKeyDown}
191
+ >
192
+ ${this.isOpen ? this.menuTemplate(selectOptions) : nothing}
193
+ </simple-popper>
194
+ `;
195
+ }
196
+ }
197
+ LitIncludeExcludeSelect.styles = css `
198
+ .custom-icon-wrapper {
199
+ position: relative;
200
+ width: 100%;
201
+ display: inline-block;
202
+ color: var(--text-primary, #111827);
203
+ }
204
+
205
+ input {
206
+ padding: 0.5rem;
207
+ border: 0.0625rem solid var(--color-divider, #d0d3db);
208
+ border-radius: 0.25rem;
209
+ min-width: 70px;
210
+ width: -webkit-fill-available;
211
+ width: -moz-available;
212
+ width: fill-available;
213
+ background-color: var(--background-paper, #fff);
214
+ color: var(--text-primary, #111827);
215
+ }
216
+
217
+ input:hover {
218
+ border: 0.0625rem solid var(--color-secondary-main, #111827);
219
+ cursor: pointer;
220
+ }
221
+
222
+ input:focus {
223
+ outline: none;
224
+ border: 0.0625rem solid var(--color-secondary-dark, #010204);
225
+ }
226
+
227
+ input:disabled {
228
+ cursor: inherit;
229
+ }
230
+
231
+ .custom-icon-wrapper .custom-icon {
232
+ display: inline-block;
233
+ position: absolute;
234
+ right: 27px;
235
+ top: 50%;
236
+ transform: translateY(-50%);
237
+ cursor: pointer;
238
+ pointer-events: auto;
239
+ }
240
+
241
+ .custom-icon-wrapper .dropdown-icon {
242
+ position: absolute;
243
+ right: 10px;
244
+ top: 50%;
245
+ transform: translateY(-50%);
246
+ pointer-events: none;
247
+ background-color: var(--background-paper, #fff);
248
+ }
249
+
250
+ .custom-icon-wrapper .dropdown-icon--rotate {
251
+ transform: translateY(-55%) rotate(180deg);
252
+ }
253
+
254
+ .dropdown-icon-disabled {
255
+ background: #f8f8f8;
256
+ color: #5d6371;
257
+ }
258
+
259
+ .option-row {
260
+ display: flex;
261
+ align-items: center;
262
+ gap: 0.25rem;
263
+ width: 100%;
264
+ }
265
+ `;
266
+ __decorate([
267
+ property({ type: String })
268
+ ], LitIncludeExcludeSelect.prototype, "id", void 0);
269
+ __decorate([
270
+ property({ type: Object })
271
+ ], LitIncludeExcludeSelect.prototype, "value", void 0);
272
+ __decorate([
273
+ property({ type: Array })
274
+ ], LitIncludeExcludeSelect.prototype, "options", void 0);
275
+ __decorate([
276
+ property({ type: Function })
277
+ ], LitIncludeExcludeSelect.prototype, "onChange", void 0);
278
+ __decorate([
279
+ property({ type: Boolean })
280
+ ], LitIncludeExcludeSelect.prototype, "disabled", void 0);
281
+ __decorate([
282
+ property({ type: String })
283
+ ], LitIncludeExcludeSelect.prototype, "placeholder", void 0);
284
+ __decorate([
285
+ property({ reflect: true, type: Boolean }),
286
+ state()
287
+ ], LitIncludeExcludeSelect.prototype, "isOpen", void 0);
288
+ __decorate([
289
+ state()
290
+ ], LitIncludeExcludeSelect.prototype, "searchQuery", void 0);
291
+ if (!window.customElements.get('lit-include-exclude-select')) {
292
+ window.customElements.define('lit-include-exclude-select', LitIncludeExcludeSelect);
293
+ }
@@ -4,3 +4,7 @@ export declare const dateRangeFilterFn: (row: any, columnId: string, filterValue
4
4
  }) => boolean;
5
5
  export declare function dateFilterFn(row: any, columnId: string, filterValue: string): boolean;
6
6
  export declare const multiselectFilterFn: (row: any, columnId: string, filterValue: any[]) => boolean;
7
+ export declare const includeExcludeFilterFn: (row: any, columnId: string, filterValue: {
8
+ in?: string[];
9
+ nin?: string[];
10
+ }) => boolean;
@@ -39,3 +39,23 @@ export const multiselectFilterFn = (row, columnId, filterValue) => {
39
39
  const rowValue = row.getValue(columnId);
40
40
  return !!rowValue && filterValue.includes(rowValue);
41
41
  };
42
+ // Tri-state ("includeExclude") column filter: each option can be included (`in`),
43
+ // excluded (`nin`) or left in the default/unset state. Semantics: exclude wins,
44
+ // then `in` acts as a whitelist when non-empty.
45
+ export const includeExcludeFilterFn = (row, columnId, filterValue) => {
46
+ const include = filterValue?.in ?? [];
47
+ const exclude = filterValue?.nin ?? [];
48
+ // Defensive: TanStack normally won't call us when the filter is cleared, but
49
+ // an empty selection imposes no constraint.
50
+ if (include.length === 0 && exclude.length === 0)
51
+ return true;
52
+ const rowValue = row.getValue(columnId);
53
+ // Option values are stringified for display, so compare as strings to keep
54
+ // numeric/boolean cell values matching their option.
55
+ const rowKey = rowValue == null ? null : String(rowValue);
56
+ if (rowKey !== null && exclude.includes(rowKey))
57
+ return false; // exclude wins
58
+ if (include.length > 0)
59
+ return rowKey !== null && include.includes(rowKey); // whitelist
60
+ return true; // only excludes specified, and this row wasn't excluded
61
+ };
@@ -1,4 +1,4 @@
1
- export type Operator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'contains' | 'doesNotContain' | 'isEmpty' | 'isNotEmpty' | 'in' | 'nin' | 'fromTo' | 'startsWith' | 'endsWith' | 'is' | 'not' | 'isAnyOfValue';
1
+ export type Operator = '=' | '!=' | '>' | '>=' | '<' | '<=' | 'contains' | 'doesNotContain' | 'isEmpty' | 'isNotEmpty' | 'in' | 'nin' | 'fromTo' | 'startsWith' | 'endsWith' | 'is' | 'not' | 'isAnyOfValue' | 'includeExclude';
2
2
  export type FieldType = 'string' | 'select' | 'number' | 'date' | 'dateTime' | 'multiselect';
3
3
  export declare function getOperatorsByColumnType(type: FieldType): {
4
4
  value: Operator;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overview-components",
3
- "version": "1.1.181",
3
+ "version": "1.1.182",
4
4
  "description": "A reusable design Lit components for case overview section.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {