overview-components 1.1.159 → 1.1.161

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +82 -0
  3. package/dist/icons.js +692 -0
  4. package/dist/index.js +19890 -7
  5. package/dist/shared/lit-icon.d.ts.map +1 -1
  6. package/package.json +58 -21
  7. package/dist/assets/generated/locales/de.js +0 -269
  8. package/dist/assets/generated/locales/de.js.map +0 -1
  9. package/dist/assets/generated/locales/en.js +0 -269
  10. package/dist/assets/generated/locales/en.js.map +0 -1
  11. package/dist/assets/generated/locales/fr.js +0 -269
  12. package/dist/assets/generated/locales/fr.js.map +0 -1
  13. package/dist/assets/generated/locales/hr.js +0 -269
  14. package/dist/assets/generated/locales/hr.js.map +0 -1
  15. package/dist/assets/generated/locales/it.js +0 -269
  16. package/dist/assets/generated/locales/it.js.map +0 -1
  17. package/dist/assets/generated/locales/pl.js +0 -269
  18. package/dist/assets/generated/locales/pl.js.map +0 -1
  19. package/dist/assets/generated/locales/ro.js +0 -269
  20. package/dist/assets/generated/locales/ro.js.map +0 -1
  21. package/dist/assets/generated/locales/sk.js +0 -269
  22. package/dist/assets/generated/locales/sk.js.map +0 -1
  23. package/dist/assets/generated/locales/sr.js +0 -269
  24. package/dist/assets/generated/locales/sr.js.map +0 -1
  25. package/dist/assets/icons/iconGlyphs.js +0 -691
  26. package/dist/assets/icons/iconGlyphs.js.map +0 -1
  27. package/dist/assets/illustration/aichatbot-illustration.js +0 -144
  28. package/dist/assets/illustration/aichatbot-illustration.js.map +0 -1
  29. package/dist/assets/illustration/delete-illustration.js +0 -88
  30. package/dist/assets/illustration/delete-illustration.js.map +0 -1
  31. package/dist/assets/illustration/no-content.js +0 -159
  32. package/dist/assets/illustration/no-content.js.map +0 -1
  33. package/dist/assets/illustration/no-preview.js +0 -125
  34. package/dist/assets/illustration/no-preview.js.map +0 -1
  35. package/dist/assets/illustration/not-found.js +0 -98
  36. package/dist/assets/illustration/not-found.js.map +0 -1
  37. package/dist/assets/illustration/settings-illustration.js +0 -168
  38. package/dist/assets/illustration/settings-illustration.js.map +0 -1
  39. package/dist/components/components-settings/attachments-tab-settings.js +0 -318
  40. package/dist/components/components-settings/attachments-tab-settings.js.map +0 -1
  41. package/dist/components/components-settings/data-grid-settings.js +0 -553
  42. package/dist/components/components-settings/data-grid-settings.js.map +0 -1
  43. package/dist/components/components-settings/section-tab-settings.js +0 -719
  44. package/dist/components/components-settings/section-tab-settings.js.map +0 -1
  45. package/dist/components/components-settings/tabs-overview-settings.js +0 -421
  46. package/dist/components/components-settings/tabs-overview-settings.js.map +0 -1
  47. package/dist/components/index.js +0 -30
  48. package/dist/components/index.js.map +0 -1
  49. package/dist/components/lit-ai-filter-assistant.js +0 -443
  50. package/dist/components/lit-ai-filter-assistant.js.map +0 -1
  51. package/dist/components/lit-attachments-tab.js +0 -2044
  52. package/dist/components/lit-attachments-tab.js.map +0 -1
  53. package/dist/components/lit-badge.js +0 -124
  54. package/dist/components/lit-badge.js.map +0 -1
  55. package/dist/components/lit-case-variables-tab.js +0 -3718
  56. package/dist/components/lit-case-variables-tab.js.map +0 -1
  57. package/dist/components/lit-chart.js +0 -727
  58. package/dist/components/lit-chart.js.map +0 -1
  59. package/dist/components/lit-data-grid-tanstack.js +0 -2550
  60. package/dist/components/lit-data-grid-tanstack.js.map +0 -1
  61. package/dist/components/lit-filter-builder.js +0 -701
  62. package/dist/components/lit-filter-builder.js.map +0 -1
  63. package/dist/components/lit-filter-modal.js +0 -349
  64. package/dist/components/lit-filter-modal.js.map +0 -1
  65. package/dist/components/lit-multiselect-item.js +0 -707
  66. package/dist/components/lit-multiselect-item.js.map +0 -1
  67. package/dist/components/lit-section-tab.js +0 -268
  68. package/dist/components/lit-section-tab.js.map +0 -1
  69. package/dist/components/lit-tabs-overview.js +0 -356
  70. package/dist/components/lit-tabs-overview.js.map +0 -1
  71. package/dist/components/modals/lit-confirm-modal.js +0 -121
  72. package/dist/components/modals/lit-confirm-modal.js.map +0 -1
  73. package/dist/components/modals/lit-delete-modal.js +0 -131
  74. package/dist/components/modals/lit-delete-modal.js.map +0 -1
  75. package/dist/components/react-wrappers/ai-filter-assistant.js +0 -9
  76. package/dist/components/react-wrappers/ai-filter-assistant.js.map +0 -1
  77. package/dist/components/react-wrappers/attachments-tab.js +0 -9
  78. package/dist/components/react-wrappers/attachments-tab.js.map +0 -1
  79. package/dist/components/react-wrappers/badge.js +0 -9
  80. package/dist/components/react-wrappers/badge.js.map +0 -1
  81. package/dist/components/react-wrappers/button.js +0 -9
  82. package/dist/components/react-wrappers/button.js.map +0 -1
  83. package/dist/components/react-wrappers/calendar.js +0 -9
  84. package/dist/components/react-wrappers/calendar.js.map +0 -1
  85. package/dist/components/react-wrappers/case-variables-tab.js +0 -9
  86. package/dist/components/react-wrappers/case-variables-tab.js.map +0 -1
  87. package/dist/components/react-wrappers/chart.js +0 -9
  88. package/dist/components/react-wrappers/chart.js.map +0 -1
  89. package/dist/components/react-wrappers/data-grid-tanstack.js +0 -9
  90. package/dist/components/react-wrappers/data-grid-tanstack.js.map +0 -1
  91. package/dist/components/react-wrappers/filter-builder.js +0 -12
  92. package/dist/components/react-wrappers/filter-builder.js.map +0 -1
  93. package/dist/components/react-wrappers/filter-modal.js +0 -9
  94. package/dist/components/react-wrappers/filter-modal.js.map +0 -1
  95. package/dist/components/react-wrappers/index.js +0 -27
  96. package/dist/components/react-wrappers/index.js.map +0 -1
  97. package/dist/components/react-wrappers/progress-bar.js +0 -9
  98. package/dist/components/react-wrappers/progress-bar.js.map +0 -1
  99. package/dist/components/react-wrappers/section-tab.js +0 -9
  100. package/dist/components/react-wrappers/section-tab.js.map +0 -1
  101. package/dist/components/react-wrappers/tabs-overview.js +0 -9
  102. package/dist/components/react-wrappers/tabs-overview.js.map +0 -1
  103. package/dist/data/translations.js +0 -2768
  104. package/dist/data/translations.js.map +0 -1
  105. package/dist/index.js.map +0 -1
  106. package/dist/schemas/index.js +0 -18
  107. package/dist/schemas/index.js.map +0 -1
  108. package/dist/schemas/lit-attachments-tab-document.schema.js +0 -39
  109. package/dist/schemas/lit-attachments-tab-document.schema.js.map +0 -1
  110. package/dist/schemas/lit-attachments-tab-settings-value.schema.js +0 -30
  111. package/dist/schemas/lit-attachments-tab-settings-value.schema.js.map +0 -1
  112. package/dist/schemas/lit-attachments-tab.schema.js +0 -68
  113. package/dist/schemas/lit-attachments-tab.schema.js.map +0 -1
  114. package/dist/schemas/lit-case-variables-tab-cell.schema.js +0 -225
  115. package/dist/schemas/lit-case-variables-tab-cell.schema.js.map +0 -1
  116. package/dist/schemas/lit-case-variables-tab-rows.schema.js +0 -6
  117. package/dist/schemas/lit-case-variables-tab-rows.schema.js.map +0 -1
  118. package/dist/schemas/lit-case-variables-tab.schema.js +0 -27
  119. package/dist/schemas/lit-case-variables-tab.schema.js.map +0 -1
  120. package/dist/schemas/lit-data-grid-tanstack-column-array.schema.js +0 -6
  121. package/dist/schemas/lit-data-grid-tanstack-column-array.schema.js.map +0 -1
  122. package/dist/schemas/lit-data-grid-tanstack-column-custom-filter-array.schema.js +0 -6
  123. package/dist/schemas/lit-data-grid-tanstack-column-custom-filter-array.schema.js.map +0 -1
  124. package/dist/schemas/lit-data-grid-tanstack-column-custom-filter.schema.js +0 -11
  125. package/dist/schemas/lit-data-grid-tanstack-column-custom-filter.schema.js.map +0 -1
  126. package/dist/schemas/lit-data-grid-tanstack-column.schema.js +0 -79
  127. package/dist/schemas/lit-data-grid-tanstack-column.schema.js.map +0 -1
  128. package/dist/schemas/lit-data-grid-tanstack.schema.js +0 -108
  129. package/dist/schemas/lit-data-grid-tanstack.schema.js.map +0 -1
  130. package/dist/schemas/lit-filter-builder.schema.js +0 -61
  131. package/dist/schemas/lit-filter-builder.schema.js.map +0 -1
  132. package/dist/schemas/lit-section-tab-schema.js +0 -37
  133. package/dist/schemas/lit-section-tab-schema.js.map +0 -1
  134. package/dist/schemas/lit-tabs-overview-tab-array.schema.js +0 -6
  135. package/dist/schemas/lit-tabs-overview-tab-array.schema.js.map +0 -1
  136. package/dist/schemas/lit-tabs-overview-tab.schema.js +0 -32
  137. package/dist/schemas/lit-tabs-overview-tab.schema.js.map +0 -1
  138. package/dist/schemas/lit-tabs-overview.schema.js +0 -29
  139. package/dist/schemas/lit-tabs-overview.schema.js.map +0 -1
  140. package/dist/scripts/translate-locales.js +0 -241
  141. package/dist/scripts/translate-locales.js.map +0 -1
  142. package/dist/shared/filter-inputs.js +0 -429
  143. package/dist/shared/filter-inputs.js.map +0 -1
  144. package/dist/shared/index.js +0 -40
  145. package/dist/shared/index.js.map +0 -1
  146. package/dist/shared/lit-button.js +0 -159
  147. package/dist/shared/lit-button.js.map +0 -1
  148. package/dist/shared/lit-calendar.js +0 -485
  149. package/dist/shared/lit-calendar.js.map +0 -1
  150. package/dist/shared/lit-case-variables-tab-cell.js +0 -226
  151. package/dist/shared/lit-case-variables-tab-cell.js.map +0 -1
  152. package/dist/shared/lit-checkbox.js +0 -184
  153. package/dist/shared/lit-checkbox.js.map +0 -1
  154. package/dist/shared/lit-custom-popper.js +0 -116
  155. package/dist/shared/lit-custom-popper.js.map +0 -1
  156. package/dist/shared/lit-data-grid-action-buttons-popover.js +0 -295
  157. package/dist/shared/lit-data-grid-action-buttons-popover.js.map +0 -1
  158. package/dist/shared/lit-data-grid-density-popover.js +0 -84
  159. package/dist/shared/lit-data-grid-density-popover.js.map +0 -1
  160. package/dist/shared/lit-data-grid-export-popover.js +0 -68
  161. package/dist/shared/lit-data-grid-export-popover.js.map +0 -1
  162. package/dist/shared/lit-data-grid-operators-popover.js +0 -114
  163. package/dist/shared/lit-data-grid-operators-popover.js.map +0 -1
  164. package/dist/shared/lit-data-grid-row-actions.js +0 -87
  165. package/dist/shared/lit-data-grid-row-actions.js.map +0 -1
  166. package/dist/shared/lit-date-picker.js +0 -608
  167. package/dist/shared/lit-date-picker.js.map +0 -1
  168. package/dist/shared/lit-document-thumbnail.js +0 -383
  169. package/dist/shared/lit-document-thumbnail.js.map +0 -1
  170. package/dist/shared/lit-filter-input.js +0 -115
  171. package/dist/shared/lit-filter-input.js.map +0 -1
  172. package/dist/shared/lit-icon-button.js +0 -165
  173. package/dist/shared/lit-icon-button.js.map +0 -1
  174. package/dist/shared/lit-icon.js +0 -338
  175. package/dist/shared/lit-icon.js.map +0 -1
  176. package/dist/shared/lit-input.js +0 -282
  177. package/dist/shared/lit-input.js.map +0 -1
  178. package/dist/shared/lit-label.js +0 -103
  179. package/dist/shared/lit-label.js.map +0 -1
  180. package/dist/shared/lit-loader.js +0 -68
  181. package/dist/shared/lit-loader.js.map +0 -1
  182. package/dist/shared/lit-loading-bar.js +0 -91
  183. package/dist/shared/lit-loading-bar.js.map +0 -1
  184. package/dist/shared/lit-menu-item.js +0 -98
  185. package/dist/shared/lit-menu-item.js.map +0 -1
  186. package/dist/shared/lit-menu.js +0 -29
  187. package/dist/shared/lit-menu.js.map +0 -1
  188. package/dist/shared/lit-modal-body.js +0 -24
  189. package/dist/shared/lit-modal-body.js.map +0 -1
  190. package/dist/shared/lit-modal-footer.js +0 -21
  191. package/dist/shared/lit-modal-footer.js.map +0 -1
  192. package/dist/shared/lit-modal-header.js +0 -34
  193. package/dist/shared/lit-modal-header.js.map +0 -1
  194. package/dist/shared/lit-modal.js +0 -168
  195. package/dist/shared/lit-modal.js.map +0 -1
  196. package/dist/shared/lit-overflow-tooltip.js +0 -114
  197. package/dist/shared/lit-overflow-tooltip.js.map +0 -1
  198. package/dist/shared/lit-pill.js +0 -87
  199. package/dist/shared/lit-pill.js.map +0 -1
  200. package/dist/shared/lit-progress-bar.js +0 -130
  201. package/dist/shared/lit-progress-bar.js.map +0 -1
  202. package/dist/shared/lit-responsive-button.js +0 -106
  203. package/dist/shared/lit-responsive-button.js.map +0 -1
  204. package/dist/shared/lit-select-field.js +0 -457
  205. package/dist/shared/lit-select-field.js.map +0 -1
  206. package/dist/shared/lit-select.js +0 -668
  207. package/dist/shared/lit-select.js.map +0 -1
  208. package/dist/shared/lit-settings.js +0 -76
  209. package/dist/shared/lit-settings.js.map +0 -1
  210. package/dist/shared/lit-text-field.js +0 -252
  211. package/dist/shared/lit-text-field.js.map +0 -1
  212. package/dist/shared/lit-toggle.js +0 -240
  213. package/dist/shared/lit-toggle.js.map +0 -1
  214. package/dist/shared/lit-tooltip.js +0 -165
  215. package/dist/shared/lit-tooltip.js.map +0 -1
  216. package/dist/shared/simple-popper.js +0 -285
  217. package/dist/shared/simple-popper.js.map +0 -1
  218. package/dist/shared/simple-tooltip.js +0 -249
  219. package/dist/shared/simple-tooltip.js.map +0 -1
  220. package/dist/shared/styles/button-shared-styles.js +0 -494
  221. package/dist/shared/styles/button-shared-styles.js.map +0 -1
  222. package/dist/styles.js +0 -169
  223. package/dist/styles.js.map +0 -1
  224. package/dist/utils/custom-filters.js +0 -42
  225. package/dist/utils/custom-filters.js.map +0 -1
  226. package/dist/utils/date.js +0 -21
  227. package/dist/utils/date.js.map +0 -1
  228. package/dist/utils/file-type-utils.js +0 -55
  229. package/dist/utils/file-type-utils.js.map +0 -1
  230. package/dist/utils/formatNumber.js +0 -62
  231. package/dist/utils/formatNumber.js.map +0 -1
  232. package/dist/utils/getOperatorByType.js +0 -70
  233. package/dist/utils/getOperatorByType.js.map +0 -1
  234. package/dist/utils/getOverviewValue.js +0 -175
  235. package/dist/utils/getOverviewValue.js.map +0 -1
  236. package/dist/utils/localization.js +0 -433
  237. package/dist/utils/localization.js.map +0 -1
  238. package/dist/utils/pdf-thumbnail-generator.js +0 -91
  239. package/dist/utils/pdf-thumbnail-generator.js.map +0 -1
  240. package/dist/utils/utils.js +0 -94
  241. package/dist/utils/utils.js.map +0 -1
  242. package/dist/vite.svg +0 -1
@@ -1,3718 +0,0 @@
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 } from 'lit';
8
- import { unsafeHTML } from 'lit/directives/unsafe-html.js';
9
- import { styleMap } from 'lit/directives/style-map.js';
10
- import { property, state } from 'lit/decorators.js';
11
- import Sortable from 'sortablejs';
12
- import { repeat } from 'lit/directives/repeat.js';
13
- import { Pane } from 'tweakpane';
14
- // components
15
- import '../shared/lit-icon.js';
16
- import '../shared/lit-button.js';
17
- import '../shared/lit-menu.js';
18
- import '../shared/lit-menu-item.js';
19
- import '../shared/lit-checkbox.js';
20
- import '../shared/lit-input.js';
21
- import '../shared/simple-popper.js';
22
- import '../assets/illustration/not-found.js';
23
- import { tooltip } from '../shared/simple-tooltip.js';
24
- import '../shared/lit-tooltip.js';
25
- import '../shared/lit-progress-bar.js';
26
- import '../shared/lit-modal.js';
27
- import '../shared/lit-modal-header.js';
28
- import '../shared/lit-modal-body.js';
29
- import '../shared/lit-modal-footer.js';
30
- import '../shared/lit-select-field.js';
31
- import '../shared/lit-select.js';
32
- import '../shared/lit-label.js';
33
- import '../shared/lit-icon-button.js';
34
- import '../shared/lit-toggle.js';
35
- import '../shared/lit-text-field.js';
36
- import './lit-tabs-overview.js';
37
- // utils
38
- import { formatDate } from '../utils/date.js';
39
- import { formatCurrency, formatDecimal } from '../utils/formatNumber.js';
40
- import { setLocale, getLocale, LOCALE_LANGS } from '../utils/localization.js';
41
- import { msg } from '@lit/localize';
42
- import { isEqual } from 'lodash';
43
- import { getOperatorsByColumnType, getDefaultOperator, } from '../utils/getOperatorByType.js';
44
- export class LitCaseVariablesTab extends LitElement {
45
- constructor() {
46
- super(...arguments);
47
- this.rows = [];
48
- this.variables = [];
49
- this.hideTabWhen = false;
50
- this.userLang = 'cs';
51
- this.allowedLang = ['cs'];
52
- this.dateFormat = null;
53
- this.isLoading = false;
54
- this.enableSettings = false;
55
- this.gridVariables = false;
56
- this.onSettingsChanged = (rows) => { };
57
- this.hostURL = '';
58
- this.currentBreakpoint = this.getBreakpoint();
59
- this.isOpen = false;
60
- this.filterText = '';
61
- this.isMobile = window.innerWidth <= 600;
62
- this.expertModeModalOpen = false;
63
- this.expertModeCell = null;
64
- this.expertModeConfig = {
65
- enabled: false,
66
- leftType: 'column',
67
- leftSource: '',
68
- leftValue: '',
69
- leftAggregation: 'sum',
70
- operation: 'none',
71
- rightType: 'variable',
72
- rightSource: '',
73
- rightValue: '',
74
- rightAggregation: 'sum',
75
- headerName: '',
76
- headerNameMutations: {},
77
- pmEnabled: false,
78
- pmLeftType: 'column',
79
- pmLeftSource: '',
80
- pmLeftValue: '',
81
- pmLeftAggregation: 'sum',
82
- pmOperation: 'none',
83
- pmRightType: 'variable',
84
- pmRightSource: '',
85
- pmRightValue: '',
86
- pmRightAggregation: 'sum',
87
- };
88
- this._resolvedExpertValues = new Map();
89
- this._resizeListener = this.handleResize.bind(this);
90
- this.sortableInstances = [];
91
- this.settingsPanes = new Map();
92
- this.operatorInputs = new Map(); // Track conditional formatting operator inputs
93
- this.conditionValueInputs = new Map(); // Track conditional formatting value inputs
94
- this.hideOperatorInputs = new Map(); // Track hide condition operator inputs
95
- this.hideValueInputs = new Map(); // Track hide condition value inputs
96
- this.sortableGroupId = `group-${Math.random().toString(36).substring(2, 9)}`;
97
- this.activeSettingsCell = null;
98
- this.settingsPopperOpen = false;
99
- this.conditionSubFolders = new Map();
100
- this.language = 'cs';
101
- this.BREAKPOINTS = ['xs', 'sm', 'md', 'lg', 'xl'];
102
- this.closeSettingsPopper = () => {
103
- // Clean up any existing tweakpanes before closing
104
- this.destroySettingsPanes();
105
- this.settingsPopperOpen = false;
106
- this.activeSettingsCell = null;
107
- };
108
- this.handleSettingsChanged = (newRows) => {
109
- this.onSettingsChanged?.(newRows);
110
- };
111
- this.addCustomEmptyCell = () => {
112
- // Generate a unique field name for the custom cell
113
- const customCellCount = this.rows.filter((cell) => cell.field.startsWith('custom_cell_')).length;
114
- const fieldName = `custom_cell_${customCellCount + 1}`;
115
- // Create a new empty custom cell (without tooltip, headerName, or value)
116
- const newCell = {
117
- field: fieldName,
118
- type: 'string',
119
- size: { xs: 4, sm: 2, md: 1, lg: 1, xl: 1 },
120
- };
121
- const newRows = [...this.rows, newCell];
122
- this.rows = newRows;
123
- this.handleSettingsChanged(newRows);
124
- // Automatically open settings for the new cell to allow user to customize it
125
- setTimeout(() => {
126
- this.toggleSettingsPane(newCell);
127
- }, 100);
128
- };
129
- this.getHeaderActions = (cell) => {
130
- return html `<div class="header-buttons">
131
- ${this.enableSettings
132
- ? html `
133
- <div style="position: relative;">
134
- <lit-icon
135
- icon="cog"
136
- size="1rem"
137
- style="cursor: pointer"
138
- @click=${() => this.toggleSettingsPane(cell)}
139
- id="settings-trigger-${cell.field}"
140
- ></lit-icon>
141
-
142
- ${this.activeSettingsCell?.field === cell.field
143
- ? html `
144
- <simple-popper
145
- .showing=${this.settingsPopperOpen}
146
- .placement=${'bottom'}
147
- .manualOpening=${true}
148
- .maxWidthAsTarget=${false}
149
- .onClose=${this.closeSettingsPopper}
150
- >
151
- ${this.renderSettingsContent()}
152
- </simple-popper>
153
- `
154
- : ''}
155
- </div>
156
-
157
- <div class="drag-handle">
158
- <lit-icon icon="hamburger" size="1rem"></lit-icon>
159
- </div>
160
- `
161
- : null}
162
- </div>`;
163
- };
164
- this.getHeaderLabel = (cell) => {
165
- const tooltipText = cell?.[`tooltip_${(this.userLang)}`] || cell?.tooltip;
166
- return html ` <div
167
- style="${cell?.headerStyle ? styleMap(cell?.headerStyle) : ''}"
168
- class="process-data-heading"
169
- >
170
- ${cell?.[`headerName_${(this.userLang)}`] ||
171
- cell?.headerName ||
172
- ''}
173
- ${tooltipText
174
- ? html `
175
- <lit-icon
176
- ${tooltip(tooltipText, 'right', 100)}
177
- size="12px"
178
- icon="informative"
179
- ></lit-icon>
180
- `
181
- : ''}
182
- </div>`;
183
- };
184
- this.getHeader = (cell) => {
185
- return html ` <div class="header-cell">
186
- ${this.getHeaderLabel(cell)} ${this.getHeaderActions(cell)}
187
- </div>`;
188
- };
189
- this.handleButtonClick = (cell) => {
190
- // First try the original buttonFn if it exists and is a function
191
- if (cell.buttonFn && typeof cell.buttonFn === 'function') {
192
- cell.buttonFn();
193
- return;
194
- }
195
- // Otherwise use the href functionality like links
196
- const url = this.getLinkUrl(cell);
197
- if (url) {
198
- window.open(url, '_blank');
199
- }
200
- };
201
- this.getCellButton = (cell) => {
202
- return html `
203
- <div style="display: flex; justify-content: space-between">
204
- <lit-button
205
- style="flex-grow: 1; margin-right: 0.5rem"
206
- .fullWidth="${cell.buttonFullWidth}"
207
- variant="${cell.buttonVariant || 'contained'}"
208
- color="${cell.buttonColor || 'primary'}"
209
- label="${cell?.headerName}"
210
- @click=${() => this.handleButtonClick(cell)}
211
- ></lit-button>
212
- ${this.getHeaderActions(cell)}
213
- </div>
214
- `;
215
- };
216
- this.getCellLink = (cell) => {
217
- const linkUrl = this.getLinkUrl(cell);
218
- const displayText = cell?.value || linkUrl || '';
219
- return html `<div>${this.getHeader(cell)}</div>
220
- <a class="link" href=${linkUrl} target="_blank">${displayText} </a> `;
221
- };
222
- this.getCellValue = (cell) => {
223
- const langValue = cell?.[`value_${(this.userLang)}`];
224
- const rawValue = (langValue !== null && langValue !== undefined && langValue !== '') ? langValue : (cell?.value ?? '');
225
- const value = this.formatDisplayValue(rawValue);
226
- return html `<div>${this.getHeader(cell)}</div>
227
- <div
228
- style="${styleMap(this.computeValueStyles(cell))}"
229
- class="process-data-value"
230
- >
231
- ${unsafeHTML(value)}
232
- </div> `;
233
- };
234
- this.getCellDate = (cell) => {
235
- return html `<div>${this.getHeader(cell)}</div>
236
- <div
237
- style="${styleMap(this.computeValueStyles(cell))}"
238
- class="process-data-value"
239
- >
240
- ${cell?.value
241
- ? formatDate(cell.value, this.getLocaleLang(), !!this.dateFormat, this.dateFormat)
242
- : ''}
243
- </div> `;
244
- };
245
- this.getCellCurrency = (cell) => {
246
- const resolvedCurrency = this.resolveCurrencyType(cell);
247
- return html `<div>${this.getHeader(cell)}</div>
248
- <div
249
- style="${styleMap(this.computeValueStyles(cell))}"
250
- class="process-data-value"
251
- >
252
- ${cell?.value != null
253
- ? formatCurrency(cell.value, {
254
- locale: this.getLocaleLang(),
255
- currency: resolvedCurrency,
256
- numberOfDecimal: typeof cell.numberOfDecimal === 'number' ? cell.numberOfDecimal : 2,
257
- })
258
- : ''}
259
- </div> `;
260
- };
261
- this.getCellNumber = (cell) => {
262
- return html `<div>${this.getHeader(cell)}</div>
263
- <div
264
- style="${styleMap(this.computeValueStyles(cell))}"
265
- class="process-data-value"
266
- >
267
- ${cell?.value != null
268
- ? formatDecimal(cell.value, {
269
- locale: this.getLocaleLang(),
270
- numberOfDecimal: typeof cell.numberOfDecimal === 'number'
271
- ? cell.numberOfDecimal
272
- : undefined,
273
- })
274
- : ''}
275
- </div> `;
276
- };
277
- this.getCellProgress = (cell) => {
278
- const numericValue = this.parseProgressValue(cell?.value);
279
- const progressMax = this.resolveProgressMax(cell);
280
- const percentage = progressMax > 0 ? (numericValue / progressMax) * 100 : 0;
281
- const progressColor = this.computeProgressColor(cell);
282
- return html `<div>${this.getHeader(cell)}</div>
283
- <div
284
- style="${styleMap(this.computeValueStyles(cell))}"
285
- class="process-data-value"
286
- >
287
- <lit-progress-bar
288
- .progress="${percentage}"
289
- .color="${progressColor}"
290
- label=""
291
- style="width: 100%;"
292
- ></lit-progress-bar>
293
- </div> `;
294
- };
295
- this.getCellCheckbox = (cell) => {
296
- const checked = this.parseBooleanValue(cell?.value);
297
- return html `<div>${this.getHeader(cell)}</div>
298
- <div class="process-data-value">
299
- <lit-checkbox
300
- .checked=${checked}
301
- .disabled=${true}
302
- ></lit-checkbox>
303
- </div> `;
304
- };
305
- this.getInlineCellValue = (cell) => {
306
- const langVal = cell?.[`value_${(this.userLang)}`];
307
- let rawValue = (langVal !== null && langVal !== undefined && langVal !== '') ? langVal : (cell?.value ?? '');
308
- let value = this.formatDisplayValue(rawValue);
309
- // Format value based on cell.type
310
- switch (cell.type) {
311
- case 'currency':
312
- value = cell?.value != null
313
- ? formatCurrency(cell.value, {
314
- locale: this.getLocaleLang(),
315
- currency: this.resolveCurrencyType(cell),
316
- numberOfDecimal: typeof cell.numberOfDecimal === 'number' ? cell.numberOfDecimal : 2,
317
- })
318
- : '';
319
- break;
320
- case 'number':
321
- value = cell?.value != null
322
- ? formatDecimal(cell.value, {
323
- locale: this.getLocaleLang(),
324
- numberOfDecimal: typeof cell.numberOfDecimal === 'number'
325
- ? cell.numberOfDecimal
326
- : undefined,
327
- })
328
- : '';
329
- break;
330
- case 'date':
331
- value = cell?.value
332
- ? formatDate(cell.value, this.getLocaleLang(), !!this.dateFormat, this.dateFormat)
333
- : '';
334
- break;
335
- case 'progress':
336
- // For progress, show percentage value based on progressMax variable
337
- const progressNumeric = this.parseProgressValue(cell?.value);
338
- const progressMaxVal = this.resolveProgressMax(cell);
339
- const progressPercent = progressMaxVal > 0 ? (progressNumeric / progressMaxVal) * 100 : 0;
340
- value = `${parseFloat(progressPercent.toFixed(2))}%`;
341
- break;
342
- case 'checkbox':
343
- value = this.parseBooleanValue(cell?.value) ? msg('Ano') : msg('Ne');
344
- break;
345
- default:
346
- value = this.formatDisplayValue(rawValue);
347
- }
348
- return html `
349
- <div style="display: flex;align-items: center; justify-content: end; gap: 0.5rem;">
350
- <div
351
- style="${styleMap({
352
- ...this.computeCellStyles(cell),
353
- width: 'fit-content',
354
- display: 'flex',
355
- alignItems: 'center',
356
- justifyContent: 'end',
357
- gap: '0.5rem',
358
- padding: '0.375rem',
359
- })}"
360
- >
361
- <div
362
- style="display: flex;align-items: center; justify-content: end; gap: 0.5rem; flex-direction: row"
363
- >
364
- <div>${this.getHeaderLabel(cell)}</div>
365
- <div
366
- style="${styleMap(this.computeValueStyles(cell))}"
367
- class="process-data-value"
368
- >
369
- ${value}
370
- </div>
371
- <div>${this.getHeaderActions(cell)}</div>
372
- </div>
373
- </div>
374
- </div>
375
- `;
376
- };
377
- }
378
- normalizeConditions(formatting) {
379
- if (!formatting)
380
- return [];
381
- // Backward compat: single object → wrap in array
382
- const arr = Array.isArray(formatting) ? formatting : [formatting];
383
- // Ensure every condition property has a concrete type (tweakpane throws
384
- // "No matching controller" when a bound value is undefined/null/unsupported)
385
- const defaults = {
386
- enabled: true,
387
- variable: '',
388
- operator: '',
389
- value: '',
390
- cellVariant: 'default',
391
- valueVariant: 'default',
392
- progressColor: 'primary',
393
- };
394
- return arr.map((c) => ({
395
- ...defaults,
396
- ...c,
397
- // Force value to string — it may arrive as number, array, object, null
398
- value: c.value != null ? String(c.value) : '',
399
- enabled: typeof c.enabled === 'boolean' ? c.enabled : true,
400
- }));
401
- }
402
- createDefaultCondition(cell) {
403
- return {
404
- enabled: true,
405
- variable: cell.field,
406
- operator: this.getDefaultOperatorForCell(cell),
407
- value: '',
408
- cellVariant: 'default',
409
- valueVariant: 'default',
410
- progressColor: 'primary',
411
- };
412
- }
413
- rebuildConditionFolders(parentFolder, config, cell) {
414
- // Dispose old sub-folders for this cell
415
- const key = cell.field;
416
- const oldFolders = this.conditionSubFolders.get(key) || [];
417
- for (const f of oldFolders) {
418
- try {
419
- f.dispose();
420
- }
421
- catch (_e) { /* already disposed */ }
422
- }
423
- this.conditionSubFolders.set(key, []);
424
- // Clean up tracked inputs for this cell's conditions
425
- for (const [k] of this.operatorInputs) {
426
- if (k.startsWith(`${key}_condition_`)) {
427
- this.operatorInputs.delete(k);
428
- }
429
- }
430
- for (const [k] of this.conditionValueInputs) {
431
- if (k.startsWith(`${key}_condition_`)) {
432
- this.conditionValueInputs.delete(k);
433
- }
434
- }
435
- const conditions = config.conditions;
436
- const folders = [];
437
- conditions.forEach((conditionObj, idx) => {
438
- const subFolder = parentFolder.addFolder({
439
- title: `${msg('Podmínka')} ${idx + 1}`,
440
- expanded: idx === conditions.length - 1,
441
- });
442
- folders.push(subFolder);
443
- this.buildSingleConditionFolder(subFolder, conditionObj, config, cell, idx);
444
- // Delete condition button
445
- const deleteBtn = subFolder.addButton({ title: msg('Smazat podmínku') });
446
- deleteBtn.on('click', () => {
447
- this.removeConditionFromCell(cell, idx);
448
- config.conditions = this.normalizeConditions(this.rows.find((c) => c.field === cell.field)?.conditionalFormatting);
449
- this.rebuildConditionFolders(parentFolder, config, cell);
450
- });
451
- });
452
- this.conditionSubFolders.set(key, folders);
453
- }
454
- buildSingleConditionFolder(folder, conditionObj, _config, cell, conditionIndex) {
455
- // Guard: ensure all bound properties have concrete types for tweakpane
456
- if (typeof conditionObj.enabled !== 'boolean')
457
- conditionObj.enabled = true;
458
- if (typeof conditionObj.variable !== 'string')
459
- conditionObj.variable = cell.field || '';
460
- if (typeof conditionObj.operator !== 'string')
461
- conditionObj.operator = '';
462
- if (conditionObj.value == null || typeof conditionObj.value !== 'string')
463
- conditionObj.value = conditionObj.value != null ? String(conditionObj.value) : '';
464
- if (typeof conditionObj.cellVariant !== 'string')
465
- conditionObj.cellVariant = 'default';
466
- if (typeof conditionObj.valueVariant !== 'string')
467
- conditionObj.valueVariant = 'default';
468
- if (typeof conditionObj.progressColor !== 'string')
469
- conditionObj.progressColor = 'primary';
470
- // Enable toggle
471
- const enabledInput = folder.addBinding(conditionObj, 'enabled', {
472
- label: msg('Povolit'),
473
- });
474
- enabledInput.on('change', (ev) => {
475
- this.setConditionalFormatting(cell, conditionIndex, 'enabled', ev.value);
476
- });
477
- // Variable selector
478
- const variableOptions = this.getAllAvailableVariables();
479
- if (variableOptions.length > 0) {
480
- if (!conditionObj.variable) {
481
- conditionObj.variable = cell.field;
482
- this.setConditionalFormatting(cell, conditionIndex, 'variable', cell.field);
483
- }
484
- const variableInput = folder.addBinding(conditionObj, 'variable', {
485
- view: 'list',
486
- label: msg('Proměnná'),
487
- options: variableOptions,
488
- });
489
- variableInput.on('change', (ev) => {
490
- this.setConditionalFormatting(cell, conditionIndex, 'variable', ev.value);
491
- this.updateConditionalFormattingOperators(folder, conditionObj, cell, conditionIndex, ev.value);
492
- });
493
- }
494
- // Operator
495
- const variableType = this.getVariableTypeByField(conditionObj.variable);
496
- const tempCell = { ...cell, field: conditionObj.variable, type: variableType };
497
- const operatorOptions = this.getOperatorOptionsForCell(tempCell);
498
- const operatorInput = folder.addBinding(conditionObj, 'operator', {
499
- view: 'list',
500
- label: msg('Operátor'),
501
- options: operatorOptions,
502
- });
503
- operatorInput.on('change', (ev) => {
504
- this.setConditionalFormatting(cell, conditionIndex, 'operator', ev.value);
505
- });
506
- const operatorKey = `${cell.field}_condition_${conditionIndex}_operator`;
507
- this.operatorInputs.set(operatorKey, operatorInput);
508
- // Value input
509
- this.addConditionalFormattingValueInput(folder, conditionObj, cell, conditionIndex, conditionObj.variable);
510
- // Cell Variant
511
- const cellVariantOptions = [
512
- { text: msg('Výchozí'), value: 'default' },
513
- { text: msg('Primární'), value: 'primary' },
514
- { text: msg('Úspěch'), value: 'success' },
515
- { text: msg('Varování'), value: 'warning' },
516
- { text: msg('Chyba'), value: 'error' },
517
- { text: msg('Informace'), value: 'info' },
518
- ];
519
- const cellVariantInput = folder.addBinding(conditionObj, 'cellVariant', {
520
- view: 'list',
521
- label: msg('Styl buňky'),
522
- options: cellVariantOptions,
523
- });
524
- cellVariantInput.on('change', (ev) => {
525
- this.setConditionalFormatting(cell, conditionIndex, 'cellVariant', ev.value);
526
- });
527
- this.colorizeVariantSelect(cellVariantInput, cellVariantOptions, conditionObj, 'cellVariant');
528
- // Value Variant
529
- const valueVariantOptions = [
530
- { text: msg('Výchozí'), value: 'default' },
531
- { text: msg('Primární'), value: 'primary' },
532
- { text: msg('Úspěch'), value: 'success' },
533
- { text: msg('Varování'), value: 'warning' },
534
- { text: msg('Chyba'), value: 'error' },
535
- { text: msg('Informace'), value: 'info' },
536
- ];
537
- const valueVariantInput = folder.addBinding(conditionObj, 'valueVariant', {
538
- view: 'list',
539
- label: msg('Barva hodnoty'),
540
- options: valueVariantOptions,
541
- });
542
- valueVariantInput.on('change', (ev) => {
543
- this.setConditionalFormatting(cell, conditionIndex, 'valueVariant', ev.value);
544
- });
545
- this.colorizeVariantSelect(valueVariantInput, valueVariantOptions, conditionObj, 'valueVariant');
546
- // Progress Color (only for progress type)
547
- if (cell.type === 'progress') {
548
- const progressColorOptions = [
549
- { text: msg('Primární'), value: 'primary' },
550
- { text: msg('Úspěch'), value: 'success' },
551
- { text: msg('Varování'), value: 'warning' },
552
- { text: msg('Chyba'), value: 'error' },
553
- { text: msg('Informace'), value: 'info' },
554
- ];
555
- const progressColorInput = folder.addBinding(conditionObj, 'progressColor', {
556
- view: 'list',
557
- label: msg('Barva Progressu'),
558
- options: progressColorOptions,
559
- });
560
- progressColorInput.on('change', (ev) => {
561
- this.setConditionalFormatting(cell, conditionIndex, 'progressColor', ev.value);
562
- });
563
- this.colorizeVariantSelect(progressColorInput, progressColorOptions, conditionObj, 'progressColor');
564
- }
565
- }
566
- createCellSettingsConfig(cell) {
567
- // If cell type is 'select' or 'multiselect', set cellType to 'string' for settings display
568
- let cellType = cell.type || 'string';
569
- if (cellType === 'select' || cellType === 'multiselect') {
570
- cellType = 'string';
571
- }
572
- return {
573
- size: cell.size?.lg ?? 1,
574
- mobileSize: cell.size?.xs ?? 4,
575
- cellVariant: cell.cellVariant || 'default',
576
- cellCustomStyles: cell.cellCustomStyles || false,
577
- valueVariant: cell.valueVariant || 'default',
578
- valueCustomStyles: cell.valueCustomStyles || false,
579
- fontWeight: cell.valueStyle?.fontWeight === 'bold',
580
- cellType,
581
- // Cell content properties
582
- headerName: cell.headerName || '',
583
- cellValue: cell.value || '',
584
- // Cell type specific properties
585
- href: cell.href || '',
586
- linkType: cell.linkType || 'custom',
587
- dynamicLink: cell.dynamicLink || '',
588
- numberOfDecimal: cell.numberOfDecimal ?? 2,
589
- progressColor: cell.progressColor || 'primary',
590
- progressMax: cell.progressMax ?? 100,
591
- buttonVariant: cell.buttonVariant || 'contained',
592
- buttonColor: cell.buttonColor || 'primary',
593
- buttonFullWidth: cell.buttonFullWidth ?? false,
594
- currencyType: cell.currencyType || 'CZK',
595
- customCSS: JSON.stringify(cell.cellStyle || {}),
596
- // Conditional formatting - array of conditions (backward compat: wrap single object)
597
- conditions: this.normalizeConditions(cell.conditionalFormatting),
598
- // Conditional hide cell settings
599
- hideConditionEnabled: !!cell.hideCondition?.enabled,
600
- hideConditionVariable: cell.hideCondition?.variable ?? '',
601
- hideConditionOperator: cell.hideCondition?.operator ?? '',
602
- hideConditionValue: cell.hideCondition?.value ?? '',
603
- };
604
- }
605
- getOperatorOptionsForCell(cell) {
606
- // Map Cell type to FieldType for operators
607
- let variableType = 'string'; // default type
608
- if (cell.type) {
609
- switch (cell.type) {
610
- case 'select':
611
- case 'checkbox':
612
- variableType = 'select';
613
- break;
614
- case 'multiselect':
615
- variableType = 'multiselect';
616
- break;
617
- case 'number':
618
- case 'currency':
619
- case 'progress':
620
- variableType = 'number';
621
- break;
622
- case 'date':
623
- variableType = 'date';
624
- break;
625
- case 'string':
626
- case 'button':
627
- case 'link':
628
- default:
629
- variableType = 'string';
630
- break;
631
- }
632
- }
633
- return getOperatorsByColumnType(variableType).map((op) => ({
634
- text: op.label,
635
- value: op.value,
636
- }));
637
- }
638
- getDefaultOperatorForCell(cell) {
639
- let variableType = 'string'; // default type
640
- // Map Cell type to FieldType for operators
641
- if (cell.type) {
642
- switch (cell.type) {
643
- case 'select':
644
- case 'checkbox':
645
- variableType = 'select';
646
- break;
647
- case 'multiselect':
648
- variableType = 'multiselect';
649
- break;
650
- case 'number':
651
- case 'currency':
652
- case 'progress':
653
- variableType = 'number';
654
- break;
655
- case 'date':
656
- variableType = 'date';
657
- break;
658
- case 'string':
659
- case 'button':
660
- case 'link':
661
- default:
662
- variableType = 'string';
663
- break;
664
- }
665
- }
666
- return getDefaultOperator(variableType);
667
- }
668
- getVariableTypeByField(fieldName) {
669
- // First check variables array
670
- const cellInVariables = this.variables.find((variable) => variable.field === fieldName);
671
- if (cellInVariables?.type) {
672
- return cellInVariables.type;
673
- }
674
- // Then check in rows array
675
- const cellInRows = this.rows.find((cell) => cell.field === fieldName);
676
- if (cellInRows?.type) {
677
- return cellInRows.type;
678
- }
679
- // Default to string if not found
680
- return 'string';
681
- }
682
- getValueOptionsByField(fieldName) {
683
- // Firs check in variables array
684
- const cellInVariables = this.variables.find((variable) => variable.field === fieldName);
685
- if (cellInVariables?.valueOptions) {
686
- return cellInVariables.valueOptions;
687
- }
688
- // Than check in rows
689
- const cellInRows = this.rows.find((cell) => cell.field === fieldName);
690
- if (cellInRows?.valueOptions) {
691
- return cellInRows.valueOptions;
692
- }
693
- return undefined;
694
- }
695
- updateConditionalFormattingOperators(folder, conditionObj, cell, conditionIndex, variableName) {
696
- const variableType = this.getVariableTypeByField(variableName);
697
- const tempCell = { ...cell, field: variableName, type: variableType };
698
- const newOperatorOptions = this.getOperatorOptionsForCell(tempCell);
699
- const operatorKey = `${cell.field}_condition_${conditionIndex}_operator`;
700
- const existingOperatorInput = this.operatorInputs.get(operatorKey);
701
- let insertIndex = -1;
702
- if (existingOperatorInput) {
703
- try {
704
- insertIndex = folder.children.indexOf(existingOperatorInput);
705
- existingOperatorInput.dispose();
706
- this.operatorInputs.delete(operatorKey);
707
- }
708
- catch (error) {
709
- console.warn('Could not dispose existing operator binding:', error);
710
- }
711
- }
712
- const defaultOperator = this.getDefaultOperatorForCell(tempCell);
713
- conditionObj.operator = defaultOperator;
714
- this.setConditionalFormatting(cell, conditionIndex, 'operator', defaultOperator);
715
- const operatorInput = folder.addBinding(conditionObj, 'operator', {
716
- view: 'list',
717
- label: msg('Operátor'),
718
- options: newOperatorOptions,
719
- index: insertIndex >= 0 ? insertIndex : undefined,
720
- });
721
- operatorInput.on('change', (ev) => {
722
- this.setConditionalFormatting(cell, conditionIndex, 'operator', ev.value);
723
- });
724
- this.operatorInputs.set(operatorKey, operatorInput);
725
- this.addConditionalFormattingValueInput(folder, conditionObj, cell, conditionIndex, variableName);
726
- }
727
- updateHideConditionOperators(folder, config, cell, variableName) {
728
- // Get the type of the selected variable to ensure correct operators
729
- const variableType = this.getVariableTypeByField(variableName);
730
- // Create a temporary cell object with the new variable and its type
731
- const tempCell = { ...cell, field: variableName, type: variableType };
732
- const newOperatorOptions = this.getOperatorOptionsForCell(tempCell);
733
- // Find existing operator binding and its position
734
- const operatorKey = `${cell.field}_hide_operator`;
735
- const existingOperatorInput = this.hideOperatorInputs.get(operatorKey);
736
- let insertIndex = -1;
737
- if (existingOperatorInput) {
738
- try {
739
- // Find the index of the existing operator input
740
- insertIndex = folder.children.indexOf(existingOperatorInput);
741
- existingOperatorInput.dispose();
742
- this.hideOperatorInputs.delete(operatorKey);
743
- }
744
- catch (error) {
745
- console.warn('Could not dispose existing hide operator binding:', error);
746
- }
747
- }
748
- // Reset operator to default for new variable type
749
- const defaultOperator = this.getDefaultOperatorForCell(tempCell);
750
- config.hideConditionOperator = defaultOperator;
751
- this.setHideCondition(cell, 'operator', defaultOperator);
752
- // Add new operator binding with updated options
753
- const hideOperatorInput = folder.addBinding(config, 'hideConditionOperator', {
754
- view: 'list',
755
- label: msg('Operátor'),
756
- options: newOperatorOptions,
757
- index: insertIndex >= 0 ? insertIndex : undefined, // Insert at same position if possible
758
- });
759
- hideOperatorInput.on('change', (ev) => {
760
- this.setHideCondition(cell, 'operator', ev.value);
761
- });
762
- // Store the new operator input for future disposal
763
- this.hideOperatorInputs.set(operatorKey, hideOperatorInput);
764
- // Update the value input to match the new variable type
765
- this.addHideConditionValueInput(folder, config, cell, variableName);
766
- }
767
- addHideConditionValueInput(folder, config, cell, variableName) {
768
- // Find and dispose existing value input if it exists
769
- const valueKey = `${cell.field}_hide_value`;
770
- const existingValueInput = this.hideValueInputs.get(valueKey);
771
- let insertIndex = -1;
772
- if (existingValueInput) {
773
- try {
774
- // Find the index of the existing value input
775
- insertIndex = folder.children.indexOf(existingValueInput);
776
- existingValueInput.dispose();
777
- this.hideValueInputs.delete(valueKey);
778
- }
779
- catch (error) {
780
- console.warn('Could not dispose existing hide value binding:', error);
781
- }
782
- }
783
- // Get the type and valueOptions of the selected variable
784
- const variableType = this.getVariableTypeByField(variableName);
785
- const valueOptions = this.getValueOptionsByField(variableName);
786
- let hideValueInput;
787
- // If variable is type 'select' or 'multiselect' and has valueOptions, create a dropdown
788
- if ((variableType === 'select' || variableType === 'multiselect') &&
789
- valueOptions &&
790
- valueOptions.length > 0) {
791
- // Convert valueOptions from {label, value} to {text, value} for Tweakpane
792
- const tweakpaneOptions = valueOptions.map((opt) => ({
793
- text: opt.label,
794
- value: opt.value,
795
- }));
796
- hideValueInput = folder.addBinding(config, 'hideConditionValue', {
797
- view: 'list',
798
- label: msg('Hodnota'),
799
- options: tweakpaneOptions,
800
- index: insertIndex >= 0 ? insertIndex : undefined,
801
- });
802
- }
803
- else {
804
- // Otherwise, create a text input
805
- hideValueInput = folder.addBinding(config, 'hideConditionValue', {
806
- label: msg('Hodnota'),
807
- index: insertIndex >= 0 ? insertIndex : undefined,
808
- });
809
- }
810
- hideValueInput.on('change', (ev) => {
811
- this.setHideCondition(cell, 'value', ev.value);
812
- });
813
- // Store the new value input for future disposal
814
- this.hideValueInputs.set(valueKey, hideValueInput);
815
- }
816
- addConditionalFormattingValueInput(folder, conditionObj, cell, conditionIndex, variableName) {
817
- const valueKey = `${cell.field}_condition_${conditionIndex}_value`;
818
- const existingValueInput = this.conditionValueInputs.get(valueKey);
819
- let insertIndex = -1;
820
- if (existingValueInput) {
821
- try {
822
- insertIndex = folder.children.indexOf(existingValueInput);
823
- existingValueInput.dispose();
824
- this.conditionValueInputs.delete(valueKey);
825
- }
826
- catch (error) {
827
- console.warn('Could not dispose existing condition value binding:', error);
828
- }
829
- }
830
- const variableType = this.getVariableTypeByField(variableName);
831
- const valueOptions = this.getValueOptionsByField(variableName);
832
- // Ensure value is a string before binding (tweakpane requires a concrete type)
833
- if (conditionObj.value == null || typeof conditionObj.value !== 'string') {
834
- conditionObj.value = conditionObj.value != null ? String(conditionObj.value) : '';
835
- }
836
- let conditionValueInput;
837
- if ((variableType === 'select' || variableType === 'multiselect') &&
838
- valueOptions &&
839
- valueOptions.length > 0) {
840
- const tweakpaneOptions = valueOptions.map((opt) => ({
841
- text: opt.label,
842
- value: opt.value,
843
- }));
844
- conditionValueInput = folder.addBinding(conditionObj, 'value', {
845
- view: 'list',
846
- label: msg('Hodnota'),
847
- options: tweakpaneOptions,
848
- index: insertIndex >= 0 ? insertIndex : undefined,
849
- });
850
- }
851
- else {
852
- conditionValueInput = folder.addBinding(conditionObj, 'value', {
853
- label: msg('Hodnota'),
854
- index: insertIndex >= 0 ? insertIndex : undefined,
855
- });
856
- }
857
- conditionValueInput.on('change', (ev) => {
858
- this.setConditionalFormatting(cell, conditionIndex, 'value', ev.value);
859
- });
860
- this.conditionValueInputs.set(valueKey, conditionValueInput);
861
- }
862
- getBreakpoint() {
863
- const width = window.innerWidth;
864
- if (width >= 1920)
865
- return 'xl';
866
- if (width >= 1280)
867
- return 'lg';
868
- if (width >= 960)
869
- return 'md';
870
- if (width >= 600)
871
- return 'sm';
872
- return 'xs';
873
- }
874
- // Function to handle resize events
875
- handleResize() {
876
- const newBreakpoint = this.getBreakpoint();
877
- if (newBreakpoint !== this.currentBreakpoint) {
878
- this.currentBreakpoint = newBreakpoint; // Update property
879
- }
880
- this.isMobile = window.innerWidth <= 600;
881
- }
882
- connectedCallback() {
883
- super.connectedCallback();
884
- window.addEventListener('resize', this._resizeListener); // Add listener
885
- this.isMobile = window.innerWidth <= 600;
886
- if (!this.dateFormat) {
887
- const userSettings = localStorage.getItem('userSettings');
888
- const storedFormat = userSettings
889
- ? JSON.parse(userSettings)?.state?.dateFormat
890
- : undefined;
891
- this.dateFormat = storedFormat;
892
- }
893
- }
894
- disconnectedCallback() {
895
- this.destroySortables();
896
- this.destroySettingsPanes();
897
- window.removeEventListener('resize', this._resizeListener); // Remove listener
898
- super.disconnectedCallback();
899
- }
900
- firstUpdated() {
901
- if (this.enableSettings) {
902
- this.initSortable();
903
- }
904
- }
905
- setFontWeight(cell) {
906
- this.rows = this.rows.map((c) => {
907
- if (c.field === cell.field) {
908
- const isBold = c.valueStyle?.fontWeight === 'bold';
909
- return {
910
- ...c,
911
- valueCustomStyles: true,
912
- valueVariant: 'default',
913
- valueStyle: {
914
- ...(c.valueStyle || {}),
915
- fontWeight: isBold ? '500' : 'bold',
916
- },
917
- };
918
- }
919
- return c;
920
- });
921
- this.handleSettingsChanged(this.rows);
922
- }
923
- getAllAvailableVariables() {
924
- const variables = [];
925
- // Add variables from current rows (including all cells)
926
- this.rows.forEach((cell) => {
927
- variables.push({
928
- text: `${cell.headerName || cell.field} (${cell.field})`,
929
- value: cell.field,
930
- });
931
- });
932
- // Add variables from variables array (that are not in current rows)
933
- const existingFields = this.rows.map((cell) => cell.field);
934
- this.variables.forEach((variable) => {
935
- if (!existingFields.includes(variable.field)) {
936
- const name = variable.headerName || variable.field;
937
- variables.push({
938
- text: `${name} (${variable.field})`,
939
- value: variable.field,
940
- });
941
- }
942
- });
943
- return variables.sort((a, b) => a.text.localeCompare(b.text));
944
- }
945
- setConditionalFormatting(cell, conditionIndex, property, value) {
946
- this.rows = this.rows.map((c) => {
947
- if (c.field === cell.field) {
948
- const updatedCell = { ...c };
949
- // Normalize to array
950
- const conditions = this.normalizeConditions(updatedCell.conditionalFormatting).map((cond) => ({ ...cond }));
951
- // Ensure index exists
952
- if (!conditions[conditionIndex]) {
953
- conditions[conditionIndex] = this.createDefaultCondition(cell);
954
- }
955
- conditions[conditionIndex][property] = value;
956
- updatedCell.conditionalFormatting = conditions;
957
- return updatedCell;
958
- }
959
- return c;
960
- });
961
- this.handleSettingsChanged(this.rows);
962
- }
963
- addConditionToCell(cell) {
964
- this.rows = this.rows.map((c) => {
965
- if (c.field === cell.field) {
966
- const updatedCell = { ...c };
967
- const conditions = this.normalizeConditions(updatedCell.conditionalFormatting).map((cond) => ({ ...cond }));
968
- conditions.push(this.createDefaultCondition(cell));
969
- updatedCell.conditionalFormatting = conditions;
970
- return updatedCell;
971
- }
972
- return c;
973
- });
974
- this.handleSettingsChanged(this.rows);
975
- }
976
- removeConditionFromCell(cell, conditionIndex) {
977
- this.rows = this.rows.map((c) => {
978
- if (c.field === cell.field) {
979
- const updatedCell = { ...c };
980
- const conditions = this.normalizeConditions(updatedCell.conditionalFormatting).map((cond) => ({ ...cond }));
981
- conditions.splice(conditionIndex, 1);
982
- updatedCell.conditionalFormatting = conditions;
983
- return updatedCell;
984
- }
985
- return c;
986
- });
987
- this.handleSettingsChanged(this.rows);
988
- }
989
- setCellStyle(style, cell) {
990
- this.rows = this.rows.map((c) => {
991
- if (c.field === cell.field) {
992
- return { ...c, cellStyle: style };
993
- }
994
- return c;
995
- });
996
- this.handleSettingsChanged(this.rows);
997
- }
998
- setCellVariant(variant, cell) {
999
- this.rows = this.rows.map((c) => {
1000
- if (c.field === cell.field) {
1001
- const updatedCell = { ...c, cellVariant: variant };
1002
- // If setting a semantic variant, disable custom styles
1003
- if (variant !== 'default') {
1004
- updatedCell.cellCustomStyles = false;
1005
- }
1006
- return updatedCell;
1007
- }
1008
- return c;
1009
- });
1010
- this.handleSettingsChanged(this.rows);
1011
- }
1012
- setValueVariant(variant, cell) {
1013
- this.rows = this.rows.map((c) => {
1014
- if (c.field === cell.field) {
1015
- const updatedCell = { ...c, valueVariant: variant };
1016
- // If setting a semantic variant, disable custom styles
1017
- if (variant !== 'default') {
1018
- updatedCell.valueCustomStyles = false;
1019
- }
1020
- return updatedCell;
1021
- }
1022
- return c;
1023
- });
1024
- this.handleSettingsChanged(this.rows);
1025
- }
1026
- enableCellCustomStyles(cell) {
1027
- this.rows = this.rows.map((c) => {
1028
- if (c.field === cell.field) {
1029
- return {
1030
- ...c,
1031
- cellCustomStyles: true,
1032
- cellVariant: 'default',
1033
- };
1034
- }
1035
- return c;
1036
- });
1037
- this.handleSettingsChanged(this.rows);
1038
- }
1039
- enableValueCustomStyles(cell) {
1040
- this.rows = this.rows.map((c) => {
1041
- if (c.field === cell.field) {
1042
- return {
1043
- ...c,
1044
- valueCustomStyles: true,
1045
- valueVariant: 'default',
1046
- };
1047
- }
1048
- return c;
1049
- });
1050
- this.handleSettingsChanged(this.rows);
1051
- }
1052
- destroySortables() {
1053
- this.sortableInstances.forEach((instance) => {
1054
- instance.destroy();
1055
- });
1056
- this.sortableInstances = [];
1057
- }
1058
- destroySettingsPanes() {
1059
- this.settingsPanes.forEach((pane, field) => {
1060
- try {
1061
- if (pane) {
1062
- pane.dispose();
1063
- }
1064
- }
1065
- catch (error) {
1066
- // Silently catch disposal errors (usually means already disposed)
1067
- if (error.type !== 'alreadydisposed') {
1068
- console.error(`Error disposing pane for ${field}:`, error);
1069
- }
1070
- }
1071
- });
1072
- this.settingsPanes.clear();
1073
- // Remove any floating tooltip containers (legacy cleanup)
1074
- document.querySelectorAll('.tweakpane-floating-tooltip').forEach((el) => {
1075
- el.remove();
1076
- });
1077
- // Clean up any leftover tweakpane containers in document
1078
- document.querySelectorAll('[id^="tweakpane-"]').forEach((el) => {
1079
- const parentContainer = el.closest('.tweakpane-container');
1080
- if (parentContainer) {
1081
- parentContainer.remove();
1082
- }
1083
- });
1084
- }
1085
- toggleSettingsPane(cell) {
1086
- // If clicking the same cell, toggle the popper
1087
- if (this.activeSettingsCell?.field === cell.field) {
1088
- this.settingsPopperOpen = !this.settingsPopperOpen;
1089
- if (!this.settingsPopperOpen) {
1090
- this.activeSettingsCell = null;
1091
- }
1092
- }
1093
- else {
1094
- // Different cell, show popper for this cell
1095
- this.activeSettingsCell = cell;
1096
- this.settingsPopperOpen = true;
1097
- }
1098
- }
1099
- getVariantColor(variant) {
1100
- const entry = LitCaseVariablesTab.VARIANT_CSS_VARS[variant];
1101
- if (!entry)
1102
- return '#9ca3af';
1103
- const styles = getComputedStyle(this);
1104
- return styles.getPropertyValue(entry.var).trim() || entry.fallback;
1105
- }
1106
- colorizeVariantSelect(binding, options, config, configKey) {
1107
- const el = binding.element;
1108
- if (!el)
1109
- return;
1110
- const selectEl = el.querySelector('select');
1111
- if (!selectEl)
1112
- return;
1113
- // Hide the native select
1114
- const valueContainer = el.querySelector('.tp-lblv_v');
1115
- if (!valueContainer)
1116
- return;
1117
- selectEl.style.display = 'none';
1118
- // Also hide the dropdown arrow
1119
- const arrow = valueContainer.querySelector('.tp-lstv_m');
1120
- if (arrow)
1121
- arrow.style.display = 'none';
1122
- // Create swatches container
1123
- const swatchContainer = document.createElement('div');
1124
- swatchContainer.style.cssText = 'display:flex;gap:4px;flex-wrap:wrap;align-items:center;';
1125
- const borderVarMap = {
1126
- default: 'var(--text-secondary, #9ca3af)',
1127
- primary: 'var(--color-primary-main, #76b703)',
1128
- secondary: 'var(--color-secondary-main, #6b7280)',
1129
- success: 'var(--color-success-main, #22c55e)',
1130
- warning: 'var(--color-warning-main, #f59e0b)',
1131
- error: 'var(--color-error-main, #ef4444)',
1132
- info: 'var(--color-info-main, #3b82f6)',
1133
- custom: 'var(--color-primary-main, #8b5cf6)',
1134
- ai: 'var(--color-primary-main, #a855f7)',
1135
- };
1136
- options.forEach((opt) => {
1137
- const color = this.getVariantColor(opt.value);
1138
- const borderColor = borderVarMap[opt.value] || 'var(--text-secondary, #9ca3af)';
1139
- const isCustom = opt.value === 'custom';
1140
- // Wrapper for positioning the tooltip
1141
- const wrapper = document.createElement('div');
1142
- wrapper.style.cssText = 'position:relative;display:inline-flex;';
1143
- const swatch = document.createElement('div');
1144
- swatch.dataset.value = opt.value;
1145
- if (isCustom) {
1146
- swatch.style.cssText = `
1147
- width:16px;height:16px;border-radius:50%;cursor:pointer;
1148
- background:conic-gradient(#ef4444,#f59e0b,#22c55e,#3b82f6,#8b5cf6,#ef4444);
1149
- border:2px dashed var(--text-secondary, #9ca3af);transition:box-shadow .15s;flex-shrink:0;
1150
- `;
1151
- }
1152
- else {
1153
- swatch.style.cssText = `
1154
- width:16px;height:16px;border-radius:50%;background:${color};cursor:pointer;
1155
- border:2px solid ${borderColor};transition:box-shadow .15s;flex-shrink:0;
1156
- `;
1157
- }
1158
- // Tooltip using SimpleTooltip component
1159
- const tip = document.createElement('simple-tooltip');
1160
- tip.textContent = opt.text;
1161
- tip.placement = 'top';
1162
- tip.offset = 6;
1163
- tip.openDelayMs = 200;
1164
- tip.target = swatch;
1165
- wrapper.appendChild(tip);
1166
- if (config[configKey] === opt.value) {
1167
- swatch.style.boxShadow = `0 0 0 2px #fff, 0 0 0 4px ${borderColor}`;
1168
- }
1169
- swatch.addEventListener('click', () => {
1170
- config[configKey] = opt.value;
1171
- binding.refresh();
1172
- swatchContainer.querySelectorAll('[data-value]').forEach((s) => {
1173
- s.style.boxShadow = 'none';
1174
- });
1175
- swatch.style.boxShadow = `0 0 0 2px #fff, 0 0 0 4px ${borderColor}`;
1176
- });
1177
- wrapper.appendChild(swatch);
1178
- swatchContainer.appendChild(wrapper);
1179
- });
1180
- valueContainer.appendChild(swatchContainer);
1181
- }
1182
- // Sanitize field name for use in CSS selectors
1183
- sanitizeFieldName(field) {
1184
- return field.replace(/[^a-zA-Z0-9-_]/g, '-');
1185
- }
1186
- renderSettingsContent() {
1187
- if (!this.activeSettingsCell)
1188
- return null;
1189
- const cell = this.activeSettingsCell;
1190
- return html `
1191
- <div class="tweakpane-container" @click=${(e) => e.stopPropagation()}>
1192
- <div id="tweakpane-${this.sanitizeFieldName(cell.field)}"></div>
1193
- </div>
1194
- `;
1195
- }
1196
- updated(changedProperties) {
1197
- super.updated(changedProperties);
1198
- if (changedProperties.has('userLang')) {
1199
- const localeLang = this.getLocaleLang();
1200
- if (getLocale() !== localeLang) {
1201
- setLocale(localeLang).then(() => this.requestUpdate());
1202
- }
1203
- }
1204
- // Initialize tweakpane when activeSettingsCell changes and popper is open
1205
- if (changedProperties.has('activeSettingsCell') ||
1206
- changedProperties.has('settingsPopperOpen')) {
1207
- if (this.activeSettingsCell && this.settingsPopperOpen) {
1208
- // Use requestAnimationFrame + small delay to ensure DOM is ready (container is in shadowRoot)
1209
- requestAnimationFrame(() => {
1210
- this.initializeTweakpane(this.activeSettingsCell, 0);
1211
- });
1212
- }
1213
- }
1214
- }
1215
- initializeTweakpaneWithRetry(cell, attempt = 1) {
1216
- // Clean up any existing pane for this cell first
1217
- const existingPane = this.settingsPanes.get(cell.field);
1218
- if (existingPane) {
1219
- try {
1220
- existingPane.dispose();
1221
- }
1222
- catch (error) {
1223
- // Silently catch disposal errors (usually means already disposed)
1224
- if (error.type !== 'alreadydisposed') {
1225
- console.error(`Error disposing existing pane for ${cell.field}:`, error);
1226
- }
1227
- }
1228
- this.settingsPanes.delete(cell.field);
1229
- }
1230
- // Increased retries and delay for slower PCs
1231
- const maxAttempts = 20;
1232
- const retryDelay = 100; // 100ms per retry = 2000ms total
1233
- const selector = `#tweakpane-${this.sanitizeFieldName(cell.field)}`;
1234
- const container = document.querySelector(selector) || this.shadowRoot?.querySelector(selector);
1235
- if (container) {
1236
- this.initializeTweakpane(cell, 0);
1237
- }
1238
- else if (attempt < maxAttempts) {
1239
- setTimeout(() => {
1240
- // Check if we're still supposed to be showing this cell
1241
- if (this.activeSettingsCell?.field === cell.field && this.settingsPopperOpen) {
1242
- this.initializeTweakpaneWithRetry(cell, attempt + 1);
1243
- }
1244
- }, retryDelay);
1245
- }
1246
- else {
1247
- console.warn(`Failed to find tweakpane container for ${cell.field} after ${maxAttempts} attempts (${maxAttempts * retryDelay}ms)`);
1248
- }
1249
- }
1250
- initializeTweakpane(cell, retryCount = 0) {
1251
- // Get the container from shadow DOM (always available in fixed overlay)
1252
- const selector = `#tweakpane-${this.sanitizeFieldName(cell.field)}`;
1253
- const container = document.querySelector(selector) || this.shadowRoot?.querySelector(selector);
1254
- if (!container) {
1255
- // Increased retries for slower PCs
1256
- const maxRetries = 5;
1257
- const retryDelay = 150;
1258
- if (retryCount < maxRetries) {
1259
- console.warn(`Tweakpane container not found, retrying... (${retryCount + 1}/${maxRetries})`);
1260
- setTimeout(() => {
1261
- this.initializeTweakpane(cell, retryCount + 1);
1262
- }, retryDelay);
1263
- return;
1264
- }
1265
- else {
1266
- console.error(`Tweakpane container not found: #tweakpane-${this.sanitizeFieldName(cell.field)}`);
1267
- return;
1268
- }
1269
- }
1270
- // Clear any existing pane for this cell
1271
- const existingPane = this.settingsPanes.get(cell.field);
1272
- if (existingPane) {
1273
- try {
1274
- existingPane.dispose();
1275
- }
1276
- catch (error) {
1277
- // Silently catch disposal errors (usually means already disposed)
1278
- if (error.type !== 'alreadydisposed') {
1279
- console.error(`Error disposing existing pane for ${cell.field}:`, error);
1280
- }
1281
- }
1282
- this.settingsPanes.delete(cell.field);
1283
- }
1284
- // Create Tweakpane in the container
1285
- const pane = new Pane({
1286
- container: container,
1287
- title: `${cell.headerName || cell.field}`,
1288
- });
1289
- // Apply app theme CSS variables to Tweakpane
1290
- const styles = getComputedStyle(this);
1291
- const bgPaper = styles.getPropertyValue('--background-paper').trim() || '#fff';
1292
- const bgDefault = styles.getPropertyValue('--background-default').trim() || '#fff';
1293
- const textPrimary = styles.getPropertyValue('--text-primary').trim() || '#111827';
1294
- const textSecondary = styles.getPropertyValue('--text-secondary').trim() || '#5d6371';
1295
- const colorDivider = styles.getPropertyValue('--color-divider').trim() || '#e5e7eb';
1296
- const containerEl = container;
1297
- containerEl.style.setProperty('--tp-base-background-color', bgPaper);
1298
- containerEl.style.setProperty('--tp-base-shadow-color', 'rgba(0,0,0,0)');
1299
- containerEl.style.setProperty('--tp-button-background-color', bgDefault);
1300
- containerEl.style.setProperty('--tp-button-foreground-color', textPrimary);
1301
- containerEl.style.setProperty('--tp-container-background-color', colorDivider);
1302
- containerEl.style.setProperty('--tp-container-background-color-hover', colorDivider);
1303
- containerEl.style.setProperty('--tp-container-foreground-color', textPrimary);
1304
- containerEl.style.setProperty('--tp-input-background-color', bgDefault);
1305
- containerEl.style.setProperty('--tp-input-foreground-color', textPrimary);
1306
- containerEl.style.setProperty('--tp-label-foreground-color', textSecondary);
1307
- containerEl.style.setProperty('--tp-monitor-background-color', colorDivider);
1308
- containerEl.style.setProperty('--tp-monitor-foreground-color', textSecondary);
1309
- containerEl.style.setProperty('--tp-groove-foreground-color', colorDivider);
1310
- const config = this.createCellSettingsConfig(cell);
1311
- // Add size controls for desktop and mobile (only if not in grid variables mode)
1312
- if (!this.gridVariables) {
1313
- // Desktop size input
1314
- const desktopSizeInput = pane.addBinding(config, 'size', {
1315
- view: 'list',
1316
- label: msg('Velikost desktop'),
1317
- options: [
1318
- { text: msg('Skrytý'), value: 0 },
1319
- { text: msg('1 sloupec'), value: 1 },
1320
- { text: msg('2 sloupce'), value: 2 },
1321
- { text: msg('3 sloupce'), value: 3 },
1322
- { text: msg('4 sloupce'), value: 4 },
1323
- ],
1324
- });
1325
- desktopSizeInput.on('change', (ev) => {
1326
- this.setCellSize(ev.value, cell, 'lg');
1327
- });
1328
- // Mobile size input
1329
- const mobileSizeInput = pane.addBinding(config, 'mobileSize', {
1330
- view: 'list',
1331
- label: msg('Velikost mobil'),
1332
- options: [
1333
- { text: msg('Skrytý'), value: 0 },
1334
- { text: msg('1 sloupec'), value: 1 },
1335
- { text: msg('2 sloupce'), value: 2 },
1336
- { text: msg('3 sloupce'), value: 3 },
1337
- { text: msg('4 sloupce'), value: 4 },
1338
- ],
1339
- });
1340
- mobileSizeInput.on('change', (ev) => {
1341
- this.setCellSize(ev.value, cell, 'xs');
1342
- });
1343
- }
1344
- // Add cell type control - at the top
1345
- const cellTypeInput = pane.addBinding(config, 'cellType', {
1346
- view: 'list',
1347
- label: msg('Typ buňky'),
1348
- options: [
1349
- { text: msg('Text'), value: 'string' },
1350
- { text: msg('Tlačítko'), value: 'button' },
1351
- { text: msg('Odkaz'), value: 'link' },
1352
- { text: msg('Datum'), value: 'date' },
1353
- { text: msg('Měna'), value: 'currency' },
1354
- { text: msg('Progress'), value: 'progress' },
1355
- { text: msg('Číslo'), value: 'number' },
1356
- { text: msg('Checkbox'), value: 'checkbox' },
1357
- ],
1358
- });
1359
- cellTypeInput.on('change', (ev) => {
1360
- this.setCellType(cell, ev.value);
1361
- });
1362
- // Add header name and value controls (only for custom cells)
1363
- const isCustomCell = cell.field.startsWith('custom_cell_');
1364
- if (isCustomCell) {
1365
- const headerNameInput = pane.addBinding(config, 'headerName', {
1366
- label: msg('Název buňky'),
1367
- });
1368
- headerNameInput.on('change', (ev) => {
1369
- this.setCellHeaderName(cell, ev.value);
1370
- });
1371
- // Add cell value control for custom cells
1372
- const cellValueInput = pane.addBinding(config, 'cellValue', {
1373
- label: msg('Hodnota buňky'),
1374
- multiline: cell.type === 'string',
1375
- });
1376
- cellValueInput.on('change', (ev) => {
1377
- this.setCellValue(cell, ev.value);
1378
- });
1379
- }
1380
- // Add cell variant control - common for all types
1381
- const cellVariantOptions = [
1382
- { text: msg('Výchozí'), value: 'default' },
1383
- { text: msg('Primární'), value: 'primary' },
1384
- { text: msg('Úspěch'), value: 'success' },
1385
- { text: msg('Varování'), value: 'warning' },
1386
- { text: msg('Chyba'), value: 'error' },
1387
- { text: msg('Informace'), value: 'info' },
1388
- { text: msg('Vlastní'), value: 'custom' },
1389
- ];
1390
- const cellVariantInput = pane.addBinding(config, 'cellVariant', {
1391
- view: 'list',
1392
- label: msg('Styl buňky'),
1393
- options: cellVariantOptions,
1394
- });
1395
- // Add CSS input for custom styles (always present but controlled by visibility)
1396
- const cssInput = pane.addBinding(config, 'customCSS', {
1397
- label: msg('CSS buňky'),
1398
- multiline: true,
1399
- rows: 4,
1400
- });
1401
- cssInput.on('change', (ev) => {
1402
- try {
1403
- const cssObject = JSON.parse(ev.value || '{}');
1404
- this.setCellStyle(cssObject, cell);
1405
- }
1406
- catch (error) {
1407
- console.error('Invalid JSON for custom CSS:', error);
1408
- }
1409
- });
1410
- const showCssInput = () => {
1411
- if (cssInput.element) {
1412
- cssInput.element.style.display = '';
1413
- }
1414
- };
1415
- const hideCssInput = () => {
1416
- if (cssInput.element) {
1417
- cssInput.element.style.display = 'none';
1418
- }
1419
- };
1420
- // Set initial visibility based on whether custom style is selected
1421
- if (config.cellCustomStyles) {
1422
- showCssInput();
1423
- }
1424
- else {
1425
- hideCssInput();
1426
- }
1427
- cellVariantInput.on('change', (ev) => {
1428
- if (ev.value === 'custom') {
1429
- this.enableCellCustomStyles(cell);
1430
- showCssInput();
1431
- }
1432
- else {
1433
- this.setCellVariant(ev.value, cell);
1434
- hideCssInput();
1435
- }
1436
- });
1437
- this.colorizeVariantSelect(cellVariantInput, cellVariantOptions, config, 'cellVariant');
1438
- // Add cell type-specific controls
1439
- this.addCellTypeSpecificControls(pane, config, cell);
1440
- // Add conditional formatting section
1441
- const conditionFolder = pane.addFolder({
1442
- title: msg('Podmíněné formátování buněk'),
1443
- expanded: false,
1444
- });
1445
- // "Add Condition" button
1446
- const addConditionBtn = conditionFolder.addButton({ title: `+ ${msg('Přidat podmínku')}` });
1447
- addConditionBtn.on('click', () => {
1448
- this.addConditionToCell(cell);
1449
- // Rebuild conditions UI
1450
- config.conditions = this.normalizeConditions(this.rows.find((c) => c.field === cell.field)?.conditionalFormatting);
1451
- this.rebuildConditionFolders(conditionFolder, config, cell);
1452
- });
1453
- // Build initial condition sub-folders
1454
- this.rebuildConditionFolders(conditionFolder, config, cell);
1455
- // Add conditional hide section
1456
- const hideFolder = pane.addFolder({
1457
- title: msg('Podmíněné skrytí buněk'),
1458
- expanded: false,
1459
- });
1460
- // Enable/disable conditional hide
1461
- const hideConditionEnabledInput = hideFolder.addBinding(config, 'hideConditionEnabled', {
1462
- label: msg('Povolit podmínky skrytí'),
1463
- });
1464
- hideConditionEnabledInput.on('change', (ev) => {
1465
- this.setHideCondition(cell, 'enabled', ev.value);
1466
- });
1467
- // Variable selector for hide condition
1468
- const hideVariableOptions = this.getAllAvailableVariables();
1469
- if (hideVariableOptions.length > 0) {
1470
- // Set default variable to current cell's field if not already set
1471
- if (!config.hideConditionVariable) {
1472
- config.hideConditionVariable = cell.field;
1473
- this.setHideCondition(cell, 'variable', cell.field);
1474
- }
1475
- const hideConditionVariableInput = hideFolder.addBinding(config, 'hideConditionVariable', {
1476
- view: 'list',
1477
- label: msg('Proměnná'),
1478
- options: hideVariableOptions,
1479
- });
1480
- hideConditionVariableInput.on('change', (ev) => {
1481
- this.setHideCondition(cell, 'variable', ev.value);
1482
- // Update operators when variable changes
1483
- this.updateHideConditionOperators(hideFolder, config, cell, ev.value);
1484
- });
1485
- }
1486
- // Hide condition operator - use operators based on cell type
1487
- // Get the actual cell/variable to determine correct operators
1488
- const hideVariableType = this.getVariableTypeByField(config.hideConditionVariable);
1489
- const tempHideCell = {
1490
- ...cell,
1491
- field: config.hideConditionVariable,
1492
- type: hideVariableType,
1493
- };
1494
- const hideOperatorOptions = this.getOperatorOptionsForCell(tempHideCell);
1495
- const hideOperatorInput = hideFolder.addBinding(config, 'hideConditionOperator', {
1496
- view: 'list',
1497
- label: msg('Operátor'),
1498
- options: hideOperatorOptions,
1499
- });
1500
- hideOperatorInput.on('change', (ev) => {
1501
- this.setHideCondition(cell, 'operator', ev.value);
1502
- });
1503
- // Store the operator input for future disposal
1504
- const hideOperatorKey = `${cell.field}_hide_operator`;
1505
- this.hideOperatorInputs.set(hideOperatorKey, hideOperatorInput);
1506
- // Hide condition value - dynamic input based on variable type
1507
- this.addHideConditionValueInput(hideFolder, config, cell, config.hideConditionVariable);
1508
- // Expert mode button
1509
- const expertBtn = pane.addButton({ title: msg('Expertní mód') });
1510
- expertBtn.on('click', () => {
1511
- this.openExpertModeModal(cell);
1512
- });
1513
- // Add delete button
1514
- const deleteBtn = pane.addButton({ title: msg('Smazat buňku') });
1515
- deleteBtn.on('click', () => {
1516
- this.removeCellFromRows(cell.field);
1517
- this.closeSettingsPopper();
1518
- });
1519
- this.settingsPanes.set(cell.field, pane);
1520
- return pane;
1521
- }
1522
- addCellTypeSpecificControls(pane, config, cell) {
1523
- const cellType = cell.type || 'string';
1524
- // Create a tab for the current cell type only
1525
- const tabTitle = this.getCellTypeTabTitle(cellType);
1526
- if (tabTitle) {
1527
- const tab = pane.addTab({
1528
- pages: [{ title: tabTitle }],
1529
- });
1530
- const tabPage = tab.pages[0];
1531
- // Add controls based on cell type
1532
- switch (cellType) {
1533
- case 'link':
1534
- this.addLinkControls(tabPage, config, cell);
1535
- break;
1536
- case 'number':
1537
- this.addNumberControls(tabPage, config, cell);
1538
- break;
1539
- case 'progress':
1540
- this.addProgressControls(tabPage, config, cell);
1541
- break;
1542
- case 'button':
1543
- this.addButtonControls(tabPage, config, cell);
1544
- break;
1545
- case 'currency':
1546
- this.addCurrencyControls(tabPage, config, cell);
1547
- break;
1548
- case 'date':
1549
- this.addDateControls(tabPage, config, cell);
1550
- break;
1551
- case 'checkbox':
1552
- // Checkbox has no additional type-specific controls
1553
- break;
1554
- default:
1555
- this.addStringControls(tabPage, config, cell);
1556
- break;
1557
- }
1558
- }
1559
- }
1560
- getCellTypeTabTitle(cellType) {
1561
- const typeTitles = {
1562
- string: msg('Nastavení textu'),
1563
- link: msg('Nastavení odkazu'),
1564
- number: msg('Nastavení čísla'),
1565
- button: msg('Nastavení tlačítka'),
1566
- currency: msg('Nastavení měny'),
1567
- date: msg('Nastavení data'),
1568
- progress: msg('Nastavení postupu'),
1569
- checkbox: msg('Nastavení checkboxu'),
1570
- };
1571
- return typeTitles[cellType] || msg('Nastavení buňky');
1572
- }
1573
- addStringControls(pane, config, cell) {
1574
- // Value Bold
1575
- const fontInput = pane.addBinding(config, 'fontWeight', {
1576
- label: msg('Tučná hodnota'),
1577
- });
1578
- fontInput.on('change', () => {
1579
- this.setFontWeight(cell);
1580
- });
1581
- // Value Variant
1582
- const valueVariantOptions = [
1583
- { text: msg('Výchozí'), value: 'default' },
1584
- { text: msg('Primární'), value: 'primary' },
1585
- { text: msg('Úspěch'), value: 'success' },
1586
- { text: msg('Varování'), value: 'warning' },
1587
- { text: msg('Chyba'), value: 'error' },
1588
- { text: msg('Informace'), value: 'info' },
1589
- ];
1590
- const valueVariantInput = pane.addBinding(config, 'valueVariant', {
1591
- view: 'list',
1592
- label: 'Value Color',
1593
- options: valueVariantOptions,
1594
- });
1595
- valueVariantInput.on('change', (ev) => {
1596
- if (ev.value === 'custom') {
1597
- this.enableValueCustomStyles(cell);
1598
- }
1599
- else {
1600
- this.setValueVariant(ev.value, cell);
1601
- }
1602
- });
1603
- this.colorizeVariantSelect(valueVariantInput, valueVariantOptions, config, 'valueVariant');
1604
- }
1605
- addLinkControls(pane, config, cell) {
1606
- // Link Type selector
1607
- const linkTypeInput = pane.addBinding(config, 'linkType', {
1608
- view: 'list',
1609
- label: msg('Typ odkazu'),
1610
- options: [
1611
- { text: msg('Vlastní URL'), value: 'custom' },
1612
- { text: msg('Dynamický odkaz'), value: 'dynamic' },
1613
- ],
1614
- });
1615
- linkTypeInput.on('change', (ev) => {
1616
- this.setLinkType(cell, ev.value);
1617
- });
1618
- // Custom URL input (shown when linkType is 'custom')
1619
- const hrefInput = pane.addBinding(config, 'href', {
1620
- label: msg('Vlastní URL'),
1621
- });
1622
- hrefInput.on('change', (ev) => {
1623
- this.setHref(cell, ev.value);
1624
- });
1625
- }
1626
- addNumberControls(pane, config, cell) {
1627
- // Value Bold
1628
- const fontInput = pane.addBinding(config, 'fontWeight', {
1629
- label: msg('Hodnota tučně'),
1630
- });
1631
- fontInput.on('change', () => {
1632
- this.setFontWeight(cell);
1633
- });
1634
- // Value Variant
1635
- const valueVariantOptions = [
1636
- { text: msg('Výchozí'), value: 'default' },
1637
- { text: msg('Primární'), value: 'primary' },
1638
- { text: msg('Úspěch'), value: 'success' },
1639
- { text: msg('Varování'), value: 'warning' },
1640
- { text: msg('Chyba'), value: 'error' },
1641
- { text: msg('Informace'), value: 'info' },
1642
- ];
1643
- const valueVariantInput = pane.addBinding(config, 'valueVariant', {
1644
- view: 'list',
1645
- label: 'Value Color',
1646
- options: valueVariantOptions,
1647
- });
1648
- valueVariantInput.on('change', (ev) => {
1649
- if (ev.value === 'custom') {
1650
- this.enableValueCustomStyles(cell);
1651
- }
1652
- else {
1653
- this.setValueVariant(ev.value, cell);
1654
- }
1655
- });
1656
- this.colorizeVariantSelect(valueVariantInput, valueVariantOptions, config, 'valueVariant');
1657
- }
1658
- addButtonControls(pane, config, cell) {
1659
- // Button Variant
1660
- const variantInput = pane.addBinding(config, 'buttonVariant', {
1661
- view: 'list',
1662
- label: msg('Varianta tlačítka'),
1663
- options: [
1664
- { text: msg('Vyplněné'), value: 'contained' },
1665
- { text: msg('Ohraničené'), value: 'outlined' },
1666
- { text: msg('Textové'), value: 'text' },
1667
- { text: msg('Čárkované'), value: 'dashed' },
1668
- ],
1669
- });
1670
- variantInput.on('change', (ev) => {
1671
- this.setButtonVariant(cell, ev.value);
1672
- });
1673
- // Button Color
1674
- const buttonColorOptions = [
1675
- { text: msg('Primární'), value: 'primary' },
1676
- { text: msg('Druhé'), value: 'secondary' },
1677
- { text: msg('Chyba'), value: 'error' },
1678
- { text: msg('Varování'), value: 'warning' },
1679
- { text: msg('AI'), value: 'ai' },
1680
- ];
1681
- const colorInput = pane.addBinding(config, 'buttonColor', {
1682
- view: 'list',
1683
- label: msg('Barva tlačítka'),
1684
- options: buttonColorOptions,
1685
- });
1686
- colorInput.on('change', (ev) => {
1687
- this.setButtonColor(cell, ev.value);
1688
- });
1689
- this.colorizeVariantSelect(colorInput, buttonColorOptions, config, 'buttonColor');
1690
- // Button Full Width
1691
- const fullWidthInput = pane.addBinding(config, 'buttonFullWidth', {
1692
- label: msg('Tlačítko na celou šířku'),
1693
- });
1694
- fullWidthInput.on('change', (ev) => {
1695
- this.setButtonFullWidth(cell, ev.value);
1696
- });
1697
- // Add link configuration for buttons (same as links)
1698
- // Link Type selector
1699
- const linkTypeInput = pane.addBinding(config, 'linkType', {
1700
- view: 'list',
1701
- label: msg('Typ odkazu'),
1702
- options: [
1703
- { text: msg('Vlastní URL'), value: 'custom' },
1704
- { text: msg('Dynamický odkaz'), value: 'dynamic' },
1705
- ],
1706
- });
1707
- linkTypeInput.on('change', (ev) => {
1708
- this.setLinkType(cell, ev.value);
1709
- });
1710
- // Custom URL input (shown when linkType is 'custom')
1711
- const hrefInput = pane.addBinding(config, 'href', {
1712
- label: msg('Vlastní URL'),
1713
- });
1714
- hrefInput.on('change', (ev) => {
1715
- this.setHref(cell, ev.value);
1716
- });
1717
- }
1718
- addCurrencyControls(pane, config, cell) {
1719
- // Value Bold
1720
- const fontInput = pane.addBinding(config, 'fontWeight', {
1721
- label: msg('Tučná hodnota'),
1722
- });
1723
- fontInput.on('change', () => {
1724
- this.setFontWeight(cell);
1725
- });
1726
- // Value Variant
1727
- const valueVariantOptions = [
1728
- { text: msg('Výchozí'), value: 'default' },
1729
- { text: msg('Primární'), value: 'primary' },
1730
- { text: msg('Úspěch'), value: 'success' },
1731
- { text: msg('Varování'), value: 'warning' },
1732
- { text: msg('Chyba'), value: 'error' },
1733
- { text: msg('Informace'), value: 'info' },
1734
- { text: msg('Vlastní'), value: 'custom' },
1735
- ];
1736
- const valueVariantInput = pane.addBinding(config, 'valueVariant', {
1737
- view: 'list',
1738
- label: 'Value Color',
1739
- options: valueVariantOptions,
1740
- });
1741
- valueVariantInput.on('change', (ev) => {
1742
- if (ev.value === 'custom') {
1743
- this.enableValueCustomStyles(cell);
1744
- }
1745
- else {
1746
- this.setValueVariant(ev.value, cell);
1747
- }
1748
- });
1749
- this.colorizeVariantSelect(valueVariantInput, valueVariantOptions, config, 'valueVariant');
1750
- // Number of Decimals
1751
- const decimalInput = pane.addBinding(config, 'numberOfDecimal', {
1752
- label: msg('Počet desetinných míst'),
1753
- min: 0,
1754
- max: 10,
1755
- step: 1,
1756
- });
1757
- decimalInput.on('change', (ev) => {
1758
- this.setNumberOfDecimal(cell, ev.value);
1759
- });
1760
- // Currency Type
1761
- const currencyOptions = [
1762
- { text: 'CZK', value: 'CZK' },
1763
- { text: 'EUR', value: 'EUR' },
1764
- { text: 'USD', value: 'USD' },
1765
- { text: 'GBP', value: 'GBP' },
1766
- { text: 'CHF', value: 'CHF' },
1767
- { text: 'PLN', value: 'PLN' },
1768
- { text: 'HUF', value: 'HUF' },
1769
- { text: 'JPY', value: 'JPY' },
1770
- { text: 'AUD', value: 'AUD' },
1771
- { text: 'CAD', value: 'CAD' },
1772
- { text: 'NOK', value: 'NOK' },
1773
- { text: 'SEK', value: 'SEK' },
1774
- { text: 'DKK', value: 'DKK' },
1775
- { text: 'CNY', value: 'CNY' },
1776
- { text: 'RUB', value: 'RUB' },
1777
- ];
1778
- // Add variables as currency source options
1779
- const variableOptions = this.getAllAvailableVariables();
1780
- variableOptions.forEach((v) => {
1781
- currencyOptions.push({
1782
- text: `${v.text}`,
1783
- value: `var:${v.value}`,
1784
- });
1785
- });
1786
- const currencyInput = pane.addBinding(config, 'currencyType', {
1787
- view: 'list',
1788
- label: msg('Typ měny'),
1789
- options: currencyOptions,
1790
- });
1791
- currencyInput.on('change', (ev) => {
1792
- this.setCurrencyType(cell, ev.value);
1793
- });
1794
- }
1795
- addDateControls(pane, config, cell) {
1796
- // Value Bold
1797
- const fontInput = pane.addBinding(config, 'fontWeight', {
1798
- label: msg('Tučná hodnota'),
1799
- });
1800
- fontInput.on('change', () => {
1801
- this.setFontWeight(cell);
1802
- });
1803
- // Value Variant
1804
- const valueVariantOptions = [
1805
- { text: msg('Výchozí'), value: 'default' },
1806
- { text: msg('Primární'), value: 'primary' },
1807
- { text: msg('Úspěch'), value: 'success' },
1808
- { text: msg('Varování'), value: 'warning' },
1809
- { text: msg('Chyba'), value: 'error' },
1810
- { text: msg('Informace'), value: 'info' },
1811
- ];
1812
- const valueVariantInput = pane.addBinding(config, 'valueVariant', {
1813
- view: 'list',
1814
- label: 'Value Color',
1815
- options: valueVariantOptions,
1816
- });
1817
- valueVariantInput.on('change', (ev) => {
1818
- if (ev.value === 'custom') {
1819
- this.enableValueCustomStyles(cell);
1820
- }
1821
- else {
1822
- this.setValueVariant(ev.value, cell);
1823
- }
1824
- });
1825
- this.colorizeVariantSelect(valueVariantInput, valueVariantOptions, config, 'valueVariant');
1826
- }
1827
- addProgressControls(pane, config, cell) {
1828
- // Progress Max — numeric value for 100%
1829
- const progressMaxInput = pane.addBinding(config, 'progressMax', {
1830
- label: msg('Hodnota 100%'),
1831
- min: 1,
1832
- step: 1,
1833
- });
1834
- progressMaxInput.on('change', (ev) => {
1835
- this.setProgressMax(cell, ev.value);
1836
- });
1837
- // Progress Color
1838
- const progressColorOptions = [
1839
- { text: msg('Primární'), value: 'primary' },
1840
- { text: msg('Úspěch'), value: 'success' },
1841
- { text: msg('Varování'), value: 'warning' },
1842
- { text: msg('Chyba'), value: 'error' },
1843
- { text: msg('Informace'), value: 'info' },
1844
- ];
1845
- const progressColorInput = pane.addBinding(config, 'progressColor', {
1846
- view: 'list',
1847
- label: msg('Barva postupu'),
1848
- options: progressColorOptions,
1849
- });
1850
- progressColorInput.on('change', (ev) => {
1851
- this.setProgressColor(cell, ev.value);
1852
- });
1853
- this.colorizeVariantSelect(progressColorInput, progressColorOptions, config, 'progressColor');
1854
- // Value Variant (for progress text color if needed)
1855
- const valueVariantOptions = [
1856
- { text: msg('Výchozí'), value: 'default' },
1857
- { text: msg('Primární'), value: 'primary' },
1858
- { text: msg('Úspěch'), value: 'success' },
1859
- { text: msg('Varování'), value: 'warning' },
1860
- { text: msg('Chyba'), value: 'error' },
1861
- { text: msg('Informace'), value: 'info' },
1862
- ];
1863
- const valueVariantInput = pane.addBinding(config, 'valueVariant', {
1864
- view: 'list',
1865
- label: 'Value Color',
1866
- options: valueVariantOptions,
1867
- });
1868
- valueVariantInput.on('change', (ev) => {
1869
- if (ev.value === 'custom') {
1870
- this.enableValueCustomStyles(cell);
1871
- }
1872
- else {
1873
- this.setValueVariant(ev.value, cell);
1874
- }
1875
- });
1876
- this.colorizeVariantSelect(valueVariantInput, valueVariantOptions, config, 'valueVariant');
1877
- }
1878
- getVariableValue(fieldName) {
1879
- // First check in variables array for a matching field
1880
- if (this.variables && Array.isArray(this.variables)) {
1881
- const variableObj = this.variables.find((variable) => variable.field === fieldName);
1882
- if (variableObj) {
1883
- return variableObj.value;
1884
- }
1885
- }
1886
- // Then check in current rows
1887
- const rowCell = this.rows.find((cell) => cell.field === fieldName);
1888
- if (rowCell) {
1889
- return rowCell.value;
1890
- }
1891
- // Finally check in variables array
1892
- const variableItem = this.variables.find((variable) => variable.field === fieldName);
1893
- if (variableItem) {
1894
- return variableItem.value;
1895
- }
1896
- return '';
1897
- }
1898
- evaluateCondition(variableField, conditionValue, operator) {
1899
- // Get the value of the selected variable
1900
- const variableValue = this.getVariableValue(variableField);
1901
- // For isEmpty/isNotEmpty operators, check the raw value first
1902
- if (operator === 'isEmpty') {
1903
- return (variableValue === null ||
1904
- variableValue === undefined ||
1905
- variableValue === '' ||
1906
- (typeof variableValue === 'string' && variableValue.trim() === ''));
1907
- }
1908
- if (operator === 'isNotEmpty') {
1909
- return !(variableValue === null ||
1910
- variableValue === undefined ||
1911
- variableValue === '' ||
1912
- (typeof variableValue === 'string' && variableValue.trim() === ''));
1913
- }
1914
- // Determine field type for proper comparison
1915
- const fieldCell = this.rows.find((cell) => cell.field === variableField);
1916
- // Determine field type for proper comparison
1917
- let fieldType = 'string';
1918
- if (fieldCell) {
1919
- // Use cell.type directly for field type determination
1920
- switch (fieldCell.type) {
1921
- case 'number':
1922
- case 'progress':
1923
- case 'currency':
1924
- fieldType = 'number';
1925
- break;
1926
- case 'date':
1927
- fieldType = 'date';
1928
- break;
1929
- case 'select':
1930
- case 'checkbox':
1931
- fieldType = 'select';
1932
- break;
1933
- case 'multiselect':
1934
- fieldType = 'multiselect';
1935
- break;
1936
- case 'string':
1937
- case 'button':
1938
- case 'link':
1939
- default:
1940
- fieldType = 'string';
1941
- break;
1942
- }
1943
- }
1944
- const variableStr = String(variableValue || '').toLowerCase();
1945
- const conditionStr = String(conditionValue || '').toLowerCase();
1946
- const variableNum = parseFloat(variableValue);
1947
- const conditionNum = parseFloat(conditionValue);
1948
- // For date fields, parse dates for comparison
1949
- let variableDate = null;
1950
- let conditionDate = null;
1951
- if (fieldType === 'date') {
1952
- variableDate = variableValue ? new Date(variableValue) : null;
1953
- conditionDate = conditionValue ? new Date(conditionValue) : null;
1954
- }
1955
- switch (operator) {
1956
- case '=':
1957
- // Use numeric comparison for number/currency/progress types
1958
- if (fieldType === 'number' /* ||
1959
- fieldType === 'currency' ||
1960
- fieldType === 'progress' */) {
1961
- return (!isNaN(variableNum) && !isNaN(conditionNum) && variableNum === conditionNum);
1962
- }
1963
- else if (fieldType === 'date') {
1964
- if (!variableDate || !conditionDate)
1965
- return false;
1966
- return variableDate.getTime() === conditionDate.getTime();
1967
- }
1968
- else {
1969
- return variableStr === conditionStr;
1970
- }
1971
- case '!=':
1972
- // Use numeric comparison for number/currency/progress types
1973
- if (fieldType === 'number' /* ||
1974
- fieldType === 'currency' ||
1975
- fieldType === 'progress' */) {
1976
- return (!isNaN(variableNum) && !isNaN(conditionNum) && variableNum !== conditionNum);
1977
- }
1978
- else if (fieldType === 'date') {
1979
- if (!variableDate || !conditionDate)
1980
- return true;
1981
- return variableDate.getTime() !== conditionDate.getTime();
1982
- }
1983
- else {
1984
- return variableStr !== conditionStr;
1985
- }
1986
- case '>':
1987
- if (fieldType === 'date') {
1988
- if (!variableDate || !conditionDate)
1989
- return false;
1990
- return variableDate.getTime() > conditionDate.getTime();
1991
- }
1992
- return !isNaN(variableNum) && !isNaN(conditionNum) && variableNum > conditionNum;
1993
- case '>=':
1994
- if (fieldType === 'date') {
1995
- if (!variableDate || !conditionDate)
1996
- return false;
1997
- return variableDate.getTime() >= conditionDate.getTime();
1998
- }
1999
- return !isNaN(variableNum) && !isNaN(conditionNum) && variableNum >= conditionNum;
2000
- case '<':
2001
- if (fieldType === 'date') {
2002
- if (!variableDate || !conditionDate)
2003
- return false;
2004
- return variableDate.getTime() < conditionDate.getTime();
2005
- }
2006
- return !isNaN(variableNum) && !isNaN(conditionNum) && variableNum < conditionNum;
2007
- case '<=':
2008
- if (fieldType === 'date') {
2009
- if (!variableDate || !conditionDate)
2010
- return false;
2011
- return variableDate.getTime() <= conditionDate.getTime();
2012
- }
2013
- return !isNaN(variableNum) && !isNaN(conditionNum) && variableNum <= conditionNum;
2014
- case 'contains':
2015
- return variableStr.includes(conditionStr);
2016
- case 'doesNotContain':
2017
- return !variableStr.includes(conditionStr);
2018
- case 'startsWith':
2019
- return variableStr.startsWith(conditionStr);
2020
- case 'endsWith':
2021
- return variableStr.endsWith(conditionStr);
2022
- case 'is':
2023
- // "is" operator - for select type fields only
2024
- // Use exact value matching (case-sensitive)
2025
- return String(variableValue) === String(conditionValue);
2026
- case 'not':
2027
- // "not" operator (is not) - for select type fields only
2028
- // Use exact value matching (case-sensitive)
2029
- return String(variableValue) !== String(conditionValue);
2030
- case 'in':
2031
- // "in" operator - for multiselect fields
2032
- // variableValue is an array, check if conditionValue is in that array
2033
- if (Array.isArray(variableValue)) {
2034
- return variableValue.includes(conditionValue);
2035
- }
2036
- // Fallback: check if variableValue is in conditionValue list
2037
- if (Array.isArray(conditionValue)) {
2038
- return conditionValue.includes(variableValue);
2039
- }
2040
- else {
2041
- // Parse comma-separated string
2042
- const values = String(conditionValue)
2043
- .split(',')
2044
- .map((v) => v.trim());
2045
- return values.includes(String(variableValue));
2046
- }
2047
- case 'nin':
2048
- // "nin" operator (not in) - for multiselect fields
2049
- // variableValue is an array, check if conditionValue is not in that array
2050
- if (Array.isArray(variableValue)) {
2051
- return !variableValue.includes(conditionValue);
2052
- }
2053
- // Fallback: check if variableValue is not in conditionValue list
2054
- if (Array.isArray(conditionValue)) {
2055
- return !conditionValue.includes(variableValue);
2056
- }
2057
- else {
2058
- // Parse comma-separated string
2059
- const values = String(conditionValue)
2060
- .split(',')
2061
- .map((v) => v.trim());
2062
- return !values.includes(String(variableValue));
2063
- }
2064
- case 'isAnyOfValue':
2065
- // "isAnyOfValue" operator - checks if variableValue matches any of the comma-separated values
2066
- if (!conditionValue)
2067
- return false;
2068
- // If variableValue is an array (for multiselect), check if any element matches any condition value
2069
- if (Array.isArray(variableValue)) {
2070
- const conditionValues = String(conditionValue)
2071
- .split(',')
2072
- .map((v) => v.trim().toLowerCase());
2073
- return variableValue.some((val) => conditionValues.includes(String(val).toLowerCase()));
2074
- }
2075
- // For single values, check if variableValue matches any of the condition values
2076
- const values = String(conditionValue)
2077
- .split(',')
2078
- .map((v) => v.trim().toLowerCase());
2079
- return values.includes(variableStr);
2080
- default:
2081
- return false;
2082
- }
2083
- }
2084
- computeCellStyles(cell) {
2085
- // Priority order:
2086
- // 1. Conditional formatting (highest)
2087
- // 2. Custom cellStyle (if cellCustomStyles = true)
2088
- // 3. Semantic cellVariant
2089
- // 4. Default styles (lowest)
2090
- let styles = {};
2091
- // 4. Default styles (base)
2092
- // (none for now, could add default cell styling here)
2093
- // 3. Semantic cellVariant
2094
- if (cell.cellVariant && cell.cellVariant !== 'default') {
2095
- const variantStyle = this.getCellVariantStyle(cell.cellVariant);
2096
- styles = { ...styles, ...variantStyle };
2097
- }
2098
- // 2. Custom cellStyle (if explicitly enabled)
2099
- if (cell.cellCustomStyles && cell.cellStyle) {
2100
- styles = { ...styles, ...cell.cellStyle };
2101
- }
2102
- // 1. Conditional formatting cell variant (highest priority, first match wins)
2103
- const conditions = this.normalizeConditions(cell.conditionalFormatting);
2104
- for (const condition of conditions) {
2105
- if (condition?.enabled && condition?.variable) {
2106
- const conditionMet = this.evaluateCondition(condition.variable, condition.value, condition.operator);
2107
- if (conditionMet &&
2108
- condition.cellVariant &&
2109
- condition.cellVariant !== 'default') {
2110
- const conditionalCellStyle = this.getCellVariantStyle(condition.cellVariant);
2111
- styles = { ...styles, ...conditionalCellStyle };
2112
- break;
2113
- }
2114
- }
2115
- }
2116
- return styles;
2117
- }
2118
- getCellVariantStyle(variant) {
2119
- const variantMap = {
2120
- primary: {
2121
- backgroundColor: 'var(--color-primary-light)',
2122
- borderRadius: '8px',
2123
- border: '1px solid var(--color-primary-dark)',
2124
- },
2125
- success: {
2126
- backgroundColor: 'var(--color-success-light)',
2127
- borderRadius: '8px',
2128
- border: '1px solid var(--color-success-dark)',
2129
- },
2130
- warning: {
2131
- backgroundColor: 'var(--color-warning-light)',
2132
- borderRadius: '8px',
2133
- border: '1px solid var(--color-warning-dark)',
2134
- },
2135
- error: {
2136
- backgroundColor: 'var(--color-error-light)',
2137
- borderRadius: '8px',
2138
- border: '1px solid var(--color-error-dark)',
2139
- },
2140
- info: {
2141
- backgroundColor: 'var(--color-info-light)',
2142
- borderRadius: '8px',
2143
- border: '1px solid var(--color-info-dark)',
2144
- },
2145
- };
2146
- return variantMap[variant] || {};
2147
- }
2148
- computeValueStyles(cell) {
2149
- // Priority order:
2150
- // 1. Conditional formatting value variant (highest)
2151
- // 2. Custom valueStyle (if valueCustomStyles = true)
2152
- // 3. Semantic valueVariant + Independent fontWeight
2153
- // 4. Default styles (lowest)
2154
- let styles = {};
2155
- // 4. Default styles (base)
2156
- // (none for now, could add default value styling here)
2157
- // 3. Semantic valueVariant
2158
- if (cell.valueVariant && cell.valueVariant !== 'default') {
2159
- const variantStyle = this.getValueVariantStyle(cell.valueVariant);
2160
- styles = { ...styles, ...variantStyle };
2161
- }
2162
- // 3b. Independent fontWeight (works alongside variants)
2163
- if (cell.fontWeight && cell.fontWeight !== 'normal') {
2164
- styles = { ...styles, fontWeight: cell.fontWeight };
2165
- }
2166
- // 2. Custom valueStyle (if explicitly enabled)
2167
- if (cell.valueCustomStyles && cell.valueStyle) {
2168
- styles = { ...styles, ...cell.valueStyle };
2169
- }
2170
- // 1. Conditional formatting value variant (highest priority, first match wins)
2171
- const conditions = this.normalizeConditions(cell.conditionalFormatting);
2172
- for (const condition of conditions) {
2173
- if (condition?.enabled && condition?.variable) {
2174
- const conditionMet = this.evaluateCondition(condition.variable, condition.value, condition.operator);
2175
- if (conditionMet &&
2176
- condition.valueVariant &&
2177
- condition.valueVariant !== 'default') {
2178
- const conditionalValueStyle = this.getValueVariantStyle(condition.valueVariant);
2179
- styles = { ...styles, ...conditionalValueStyle };
2180
- break;
2181
- }
2182
- }
2183
- }
2184
- return styles;
2185
- }
2186
- // Helper method to convert progress value (string or number) to numeric value
2187
- parseProgressValue(value) {
2188
- if (typeof value === 'number') {
2189
- return value;
2190
- }
2191
- else if (typeof value === 'string') {
2192
- const parsed = parseFloat(value);
2193
- return !isNaN(parsed) ? parsed : 0;
2194
- }
2195
- return 0;
2196
- }
2197
- computeProgressColor(cell) {
2198
- // Priority order:
2199
- // 1. Conditional formatting progressColor (highest)
2200
- // 2. Cell progressColor property
2201
- // 3. Default 'primary' color (lowest)
2202
- let progressColor = cell.progressColor || 'primary';
2203
- // 1. Conditional formatting progressColor (highest priority, first match wins)
2204
- const conditions = this.normalizeConditions(cell.conditionalFormatting);
2205
- for (const condition of conditions) {
2206
- if (condition?.enabled && condition?.variable) {
2207
- const conditionMet = this.evaluateCondition(condition.variable, condition.value, condition.operator);
2208
- if (conditionMet && condition.progressColor) {
2209
- progressColor = condition.progressColor;
2210
- break;
2211
- }
2212
- }
2213
- }
2214
- return progressColor;
2215
- }
2216
- getValueVariantStyle(variant) {
2217
- const variantMap = {
2218
- primary: {
2219
- color: 'var(--color-primary-main)',
2220
- fontWeight: '500',
2221
- },
2222
- success: {
2223
- color: 'var(--color-success-main)',
2224
- fontWeight: '500',
2225
- },
2226
- warning: {
2227
- color: 'var(--color-warning-main)',
2228
- fontWeight: '500',
2229
- },
2230
- error: {
2231
- color: 'var(--color-error-main)',
2232
- fontWeight: '500',
2233
- },
2234
- info: {
2235
- color: 'var(--color-info-main)',
2236
- fontWeight: '500',
2237
- },
2238
- };
2239
- return variantMap[variant] || {};
2240
- }
2241
- shouldHideCell(cell) {
2242
- const hideCondition = cell.hideCondition;
2243
- if (!hideCondition?.enabled || !hideCondition?.variable || this.enableSettings) {
2244
- return false;
2245
- }
2246
- return this.evaluateCondition(hideCondition.variable, hideCondition.value, hideCondition.operator);
2247
- }
2248
- initSortable() {
2249
- this.updateComplete.then(() => {
2250
- const containers = this.shadowRoot?.querySelectorAll('.grid-container');
2251
- if (!containers?.length)
2252
- return;
2253
- containers.forEach((container) => {
2254
- let originalNodes = [];
2255
- const sortableInstance = Sortable.create(container, {
2256
- group: this.tabId || this.sortableGroupId,
2257
- animation: 150,
2258
- handle: '.drag-handle',
2259
- ghostClass: 'sortable-ghost',
2260
- chosenClass: 'sortable-chosen',
2261
- dragClass: 'sortable-drag',
2262
- sort: true,
2263
- onStart: (evt) => {
2264
- originalNodes = Array.from(evt.from.childNodes);
2265
- },
2266
- onEnd: (evt) => {
2267
- evt.from.innerHTML = '';
2268
- originalNodes.forEach((node) => evt.from.appendChild(node));
2269
- const { oldIndex, newIndex } = evt;
2270
- if (oldIndex == null || newIndex == null || oldIndex === newIndex)
2271
- return;
2272
- const updated = [...this.rows];
2273
- const [movedItem] = updated.splice(oldIndex, 1);
2274
- updated.splice(newIndex, 0, movedItem);
2275
- this.rows = updated;
2276
- this.handleSettingsChanged?.(updated);
2277
- },
2278
- });
2279
- this.sortableInstances.push(sortableInstance);
2280
- });
2281
- });
2282
- }
2283
- getLocaleLang() {
2284
- if (this.userLang && LOCALE_LANGS.has(this.userLang)) {
2285
- return this.userLang;
2286
- }
2287
- return 'cs';
2288
- }
2289
- setCellSize(size, cell, breakpoint = 'lg') {
2290
- this.rows = this.rows.map((c) => {
2291
- if (c.field === cell.field) {
2292
- const updatedCell = { ...c };
2293
- if (!updatedCell.size) {
2294
- updatedCell.size = {};
2295
- }
2296
- if (breakpoint === 'xs') {
2297
- updatedCell.size.xs = size;
2298
- updatedCell.size.sm = size;
2299
- }
2300
- else if (breakpoint === 'lg') {
2301
- updatedCell.size.md = size;
2302
- updatedCell.size.lg = size;
2303
- updatedCell.size.xl = size;
2304
- }
2305
- else {
2306
- updatedCell.size[breakpoint] = size;
2307
- }
2308
- return updatedCell;
2309
- }
2310
- return c;
2311
- });
2312
- this.handleSettingsChanged(this.rows);
2313
- }
2314
- setCellType(cell, type) {
2315
- this.rows = this.rows.map((c) => {
2316
- if (c.field === cell.field) {
2317
- return { ...c, type };
2318
- }
2319
- return c;
2320
- });
2321
- this.handleSettingsChanged(this.rows);
2322
- // Simply close the settings popper when cell type changes
2323
- // User can reopen to see the new type-specific controls
2324
- //this.closeSettingsPopper();
2325
- }
2326
- setHideCondition(cell, property, value) {
2327
- // Update the cell in the rows array
2328
- this.rows = this.rows.map((c) => {
2329
- if (c.field === cell.field) {
2330
- const updatedCell = { ...c };
2331
- // Initialize hide condition if not exists
2332
- if (!updatedCell.hideCondition) {
2333
- updatedCell.hideCondition = {
2334
- // Set default operator when creating new hide condition
2335
- operator: this.getDefaultOperatorForCell(cell),
2336
- };
2337
- }
2338
- else {
2339
- // Create a copy of hideCondition to avoid mutating the original
2340
- updatedCell.hideCondition = { ...updatedCell.hideCondition };
2341
- }
2342
- // Set the property
2343
- updatedCell.hideCondition[property] = value;
2344
- return updatedCell;
2345
- }
2346
- return c;
2347
- });
2348
- this.handleSettingsChanged(this.rows);
2349
- }
2350
- setHref(cell, href) {
2351
- this.rows = this.rows.map((c) => {
2352
- if (c.field === cell.field) {
2353
- return { ...c, href };
2354
- }
2355
- return c;
2356
- });
2357
- this.handleSettingsChanged(this.rows);
2358
- }
2359
- setLinkType(cell, linkType) {
2360
- this.rows = this.rows.map((c) => {
2361
- if (c.field === cell.field) {
2362
- const updatedCell = { ...c, linkType: linkType };
2363
- // Clear other link properties when switching types
2364
- {
2365
- updatedCell.href = '';
2366
- }
2367
- return updatedCell;
2368
- }
2369
- return c;
2370
- });
2371
- this.handleSettingsChanged(this.rows);
2372
- }
2373
- setNumberOfDecimal(cell, numberOfDecimal) {
2374
- this.rows = this.rows.map((c) => {
2375
- if (c.field === cell.field) {
2376
- return { ...c, numberOfDecimal };
2377
- }
2378
- return c;
2379
- });
2380
- this.handleSettingsChanged(this.rows);
2381
- }
2382
- setButtonVariant(cell, variant) {
2383
- this.rows = this.rows.map((c) => {
2384
- if (c.field === cell.field) {
2385
- return { ...c, buttonVariant: variant };
2386
- }
2387
- return c;
2388
- });
2389
- this.handleSettingsChanged(this.rows);
2390
- }
2391
- setButtonColor(cell, color) {
2392
- this.rows = this.rows.map((c) => {
2393
- if (c.field === cell.field) {
2394
- return { ...c, buttonColor: color };
2395
- }
2396
- return c;
2397
- });
2398
- this.handleSettingsChanged(this.rows);
2399
- }
2400
- setButtonFullWidth(cell, fullWidth) {
2401
- this.rows = this.rows.map((c) => {
2402
- if (c.field === cell.field) {
2403
- return { ...c, buttonFullWidth: fullWidth };
2404
- }
2405
- return c;
2406
- });
2407
- this.handleSettingsChanged(this.rows);
2408
- }
2409
- setCurrencyType(cell, currencyType) {
2410
- this.rows = this.rows.map((c) => {
2411
- if (c.field === cell.field) {
2412
- return { ...c, currencyType };
2413
- }
2414
- return c;
2415
- });
2416
- this.handleSettingsChanged(this.rows);
2417
- }
2418
- setProgressMax(cell, progressMax) {
2419
- this.rows = this.rows.map((c) => {
2420
- if (c.field === cell.field) {
2421
- return { ...c, progressMax };
2422
- }
2423
- return c;
2424
- });
2425
- this.handleSettingsChanged(this.rows);
2426
- }
2427
- setProgressColor(cell, progressColor) {
2428
- this.rows = this.rows.map((c) => {
2429
- if (c.field === cell.field) {
2430
- return { ...c, progressColor };
2431
- }
2432
- return c;
2433
- });
2434
- this.handleSettingsChanged(this.rows);
2435
- }
2436
- setCellHeaderName(cell, headerName) {
2437
- this.rows = this.rows.map((c) => {
2438
- if (c.field === cell.field) {
2439
- return { ...c, headerName };
2440
- }
2441
- return c;
2442
- });
2443
- this.handleSettingsChanged(this.rows);
2444
- }
2445
- setCellValue(cell, value) {
2446
- // Update the cell in the rows array
2447
- this.rows = this.rows.map((c) => {
2448
- if (c.field === cell.field) {
2449
- return { ...c, value };
2450
- }
2451
- return c;
2452
- });
2453
- this.handleSettingsChanged(this.rows);
2454
- }
2455
- removeCellFromRows(field) {
2456
- this.destroySortables();
2457
- const updatedRows = this.rows.filter((c) => c.field !== field);
2458
- this.rows = [];
2459
- this.updateComplete.then(() => {
2460
- this.rows = [...updatedRows];
2461
- this.initSortable();
2462
- this.handleSettingsChanged(this.rows);
2463
- });
2464
- }
2465
- closePopover() {
2466
- this.isOpen = false;
2467
- }
2468
- toggleCustomPopover() {
2469
- this.isOpen = !this.isOpen;
2470
- if (this.isOpen) {
2471
- this.updateComplete.then(() => {
2472
- setTimeout(() => {
2473
- const input = this.shadowRoot?.querySelector('#variable-filter-input') ||
2474
- document.querySelector('#variable-filter-input');
2475
- input?.focus();
2476
- }, 100);
2477
- });
2478
- }
2479
- }
2480
- get existingFields() {
2481
- return this.rows.flat().map((cell) => cell.field);
2482
- }
2483
- toggleRowCell(key) {
2484
- const exists = this.rows.some((cell) => cell.field === key);
2485
- if (exists) {
2486
- this.removeCellFromRows(key);
2487
- }
2488
- else {
2489
- const raw = this.variables.find((variable) => variable.field === key);
2490
- if (!raw)
2491
- return;
2492
- const newCell = {
2493
- field: key,
2494
- type: raw.type,
2495
- headerName: raw.headerName,
2496
- value: raw.value,
2497
- size: { xs: 4, sm: 2, md: 1, lg: 1, xl: 1 },
2498
- tooltip: raw.tooltip,
2499
- };
2500
- const newRows = [...this.rows];
2501
- newRows.push(newCell);
2502
- this.rows = newRows;
2503
- this.handleSettingsChanged(newRows);
2504
- }
2505
- }
2506
- replaceDynamicVariables(template) {
2507
- // Replace {variableName} patterns with actual variable values
2508
- return template.replace(/\{([^}]+)\}/g, (match, variableName) => {
2509
- const variableValue = this.getVariableValue(variableName.trim());
2510
- return String(variableValue || '');
2511
- });
2512
- }
2513
- getLinkUrl(cell) {
2514
- const href = cell.href || '';
2515
- // Check if href contains ${variable} patterns
2516
- if (href.includes('{')) {
2517
- // Replace ${variableName} patterns with actual values
2518
- return `${this.hostURL}/${this.replaceDynamicVariables(href)}`;
2519
- }
2520
- // Return static href as-is
2521
- return href;
2522
- }
2523
- formatDisplayValue(rawValue) {
2524
- if (rawValue === null || rawValue === undefined)
2525
- return '';
2526
- if (typeof rawValue === 'string') {
2527
- // Handle string representations of arrays, e.g. "[Finance, Legal, HR]"
2528
- if (rawValue.startsWith('[') && rawValue.endsWith(']')) {
2529
- const inner = rawValue.slice(1, -1).trim();
2530
- if (inner.length > 0) {
2531
- return inner.split(',').map((s) => s.trim()).filter((s) => s !== '').join(', ');
2532
- }
2533
- return '';
2534
- }
2535
- return rawValue;
2536
- }
2537
- if (Array.isArray(rawValue)) {
2538
- return rawValue
2539
- .map((v) => this.formatDisplayValue(v))
2540
- .filter((v) => v !== '')
2541
- .join(', ');
2542
- }
2543
- if (typeof rawValue === 'object') {
2544
- const entries = Object.entries(rawValue)
2545
- .map(([key, v]) => {
2546
- const val = this.formatDisplayValue(v);
2547
- return val ? `${key}: ${val}` : '';
2548
- })
2549
- .filter((entry) => entry !== '');
2550
- return entries.length > 0 ? entries.join(' | ') : '';
2551
- }
2552
- return String(rawValue);
2553
- }
2554
- getAllDataGridRefs() {
2555
- if (this.dataGridRefs?.length)
2556
- return this.dataGridRefs;
2557
- if (this.dataGridRef)
2558
- return [{ id: '_default', label: 'DataGrid', ref: this.dataGridRef }];
2559
- return [];
2560
- }
2561
- hasAnyDataGridRef() {
2562
- return this.getAllDataGridRefs().length > 0;
2563
- }
2564
- getDataGridBySource(source) {
2565
- const refs = this.getAllDataGridRefs();
2566
- if (!source) {
2567
- return refs.length === 1 ? refs[0].ref : null;
2568
- }
2569
- const entry = refs.find((r) => r.id === source);
2570
- return entry?.ref || null;
2571
- }
2572
- resolveExpertOperand(type, value, source, aggregation = 'sum') {
2573
- if (!value)
2574
- return null;
2575
- if (type === 'column') {
2576
- const grid = this.getDataGridBySource(source || '');
2577
- if (!grid)
2578
- return null;
2579
- const rows = grid.row || [];
2580
- const values = rows.map((row) => parseFloat(row[value])).filter((v) => !isNaN(v));
2581
- if (values.length === 0)
2582
- return 0;
2583
- switch (aggregation) {
2584
- case 'sum':
2585
- return values.reduce((acc, v) => acc + v, 0);
2586
- case 'avg':
2587
- return values.reduce((acc, v) => acc + v, 0) / values.length;
2588
- case 'min':
2589
- return Math.min(...values);
2590
- case 'max':
2591
- return Math.max(...values);
2592
- case 'count':
2593
- return values.length;
2594
- default:
2595
- return values.reduce((acc, v) => acc + v, 0);
2596
- }
2597
- }
2598
- // type === 'variable'
2599
- // Use pre-computed expert value if available, otherwise fall back to raw value
2600
- if (this._resolvedExpertValues.has(value)) {
2601
- return this._resolvedExpertValues.get(value);
2602
- }
2603
- const variable = [...this.rows, ...this.variables].find((v) => v.field === value);
2604
- if (!variable)
2605
- return null;
2606
- const parsed = parseFloat(String(variable.value || 0));
2607
- return isNaN(parsed) ? 0 : parsed;
2608
- }
2609
- computeExpertValue(cell) {
2610
- const config = cell.expertMode;
2611
- if (!config?.enabled)
2612
- return null;
2613
- const leftType = config.leftType || 'column';
2614
- const rightType = config.rightType || 'variable';
2615
- const left = this.resolveExpertOperand(leftType, config.leftValue || config.sourceColumn || '', config.leftSource, config.leftAggregation || 'sum');
2616
- if (left === null)
2617
- return null;
2618
- if (config.operation === 'none')
2619
- return left;
2620
- const rightValue = config.rightValue || config.variableField || '';
2621
- if (!rightValue)
2622
- return left;
2623
- const right = this.resolveExpertOperand(rightType, rightValue, config.rightSource, config.rightAggregation || 'sum');
2624
- if (right === null)
2625
- return left;
2626
- switch (config.operation) {
2627
- case '+':
2628
- return left + right;
2629
- case '-':
2630
- return left - right;
2631
- case '*':
2632
- return left * right;
2633
- case '/':
2634
- return right !== 0 ? left / right : null;
2635
- default:
2636
- return left;
2637
- }
2638
- }
2639
- precomputeExpertValues() {
2640
- this._resolvedExpertValues.clear();
2641
- const maxPasses = 3;
2642
- for (let pass = 0; pass < maxPasses; pass++) {
2643
- let changed = false;
2644
- for (const cell of this.rows) {
2645
- const expertValue = this.computeExpertValue(cell);
2646
- if (expertValue !== null) {
2647
- const prev = this._resolvedExpertValues.get(cell.field);
2648
- if (prev !== expertValue) {
2649
- this._resolvedExpertValues.set(cell.field, expertValue);
2650
- changed = true;
2651
- }
2652
- }
2653
- }
2654
- if (!changed)
2655
- break;
2656
- }
2657
- }
2658
- getCellWithExpertValue(cell) {
2659
- const expertValue = this._resolvedExpertValues.get(cell.field);
2660
- if (expertValue !== undefined) {
2661
- return { ...cell, value: expertValue };
2662
- }
2663
- return cell;
2664
- }
2665
- openExpertModeModal(cell) {
2666
- this.closeSettingsPopper();
2667
- this.expertModeCell = cell;
2668
- const existing = cell.expertMode;
2669
- const defaultSource = this.getAllDataGridRefs().length === 1 ? this.getAllDataGridRefs()[0].id : '';
2670
- // Load headerName mutations from cell
2671
- const mutations = {};
2672
- const supportedLangs = ['cs', 'en', 'de', 'sk', 'pl', 'hu', 'fr', 'it', 'es', 'sr'];
2673
- supportedLangs.forEach((lang) => {
2674
- const val = cell[`headerName_${lang}`];
2675
- if (val)
2676
- mutations[lang] = val;
2677
- });
2678
- this.expertModeConfig = {
2679
- enabled: existing?.enabled || false,
2680
- leftType: existing?.leftType || 'column',
2681
- leftSource: existing?.leftSource || defaultSource,
2682
- leftValue: existing?.leftValue || existing?.sourceColumn || '',
2683
- leftAggregation: existing?.leftAggregation || 'sum',
2684
- operation: existing?.operation || 'none',
2685
- rightType: existing?.rightType || 'variable',
2686
- rightSource: existing?.rightSource || defaultSource,
2687
- rightValue: existing?.rightValue || existing?.variableField || '',
2688
- rightAggregation: existing?.rightAggregation || 'sum',
2689
- headerName: cell.headerName || '',
2690
- headerNameMutations: mutations,
2691
- pmEnabled: existing?.pmEnabled || false,
2692
- pmLeftType: existing?.pmLeftType || 'column',
2693
- pmLeftSource: existing?.pmLeftSource || defaultSource,
2694
- pmLeftValue: existing?.pmLeftValue || '',
2695
- pmLeftAggregation: existing?.pmLeftAggregation || 'sum',
2696
- pmOperation: existing?.pmOperation || 'none',
2697
- pmRightType: existing?.pmRightType || 'variable',
2698
- pmRightSource: existing?.pmRightSource || defaultSource,
2699
- pmRightValue: existing?.pmRightValue || '',
2700
- pmRightAggregation: existing?.pmRightAggregation || 'sum',
2701
- };
2702
- this.expertModeModalOpen = true;
2703
- }
2704
- saveExpertMode() {
2705
- if (!this.expertModeCell)
2706
- return;
2707
- this.rows = this.rows.map((c) => {
2708
- if (c.field === this.expertModeCell.field) {
2709
- const { headerName, headerNameMutations, ...expertModeData } = this.expertModeConfig;
2710
- const updated = {
2711
- ...c,
2712
- expertMode: expertModeData,
2713
- headerName: headerName,
2714
- };
2715
- // Write language mutations
2716
- Object.entries(headerNameMutations).forEach(([lang, val]) => {
2717
- updated[`headerName_${lang}`] = val;
2718
- });
2719
- // Remove empty mutations
2720
- const supportedLangs = ['cs', 'en', 'de', 'sk', 'pl', 'hu', 'fr', 'it', 'es', 'sr'];
2721
- supportedLangs.forEach((lang) => {
2722
- if (!headerNameMutations[lang]) {
2723
- delete updated[`headerName_${lang}`];
2724
- }
2725
- });
2726
- return updated;
2727
- }
2728
- return c;
2729
- });
2730
- this.handleSettingsChanged(this.rows);
2731
- this.expertModeModalOpen = false;
2732
- this.expertModeCell = null;
2733
- }
2734
- getGridColumnOptions(source) {
2735
- const grid = this.getDataGridBySource(source || '');
2736
- if (!grid?.columns)
2737
- return [];
2738
- return grid.columns
2739
- .filter((col) => {
2740
- const type = col.type;
2741
- return type === 'number' || type === 'currency' || type === 'range' || type === 'numberRange';
2742
- })
2743
- .map((col) => ({
2744
- label: col.headerName || col.field,
2745
- value: col.field,
2746
- }));
2747
- }
2748
- getGridSourceOptions() {
2749
- return this.getAllDataGridRefs().map((entry) => ({
2750
- label: entry.label,
2751
- value: entry.id,
2752
- }));
2753
- }
2754
- getExpertVariableOptions() {
2755
- const numericTypes = ['currency', 'number'];
2756
- const variables = [];
2757
- this.rows.forEach((cell) => {
2758
- if (cell.type && numericTypes.includes(cell.type)) {
2759
- variables.push({
2760
- label: cell.headerName || cell.field,
2761
- value: cell.field,
2762
- });
2763
- }
2764
- });
2765
- const existingFields = this.rows.map((cell) => cell.field);
2766
- this.variables.forEach((variable) => {
2767
- if (!existingFields.includes(variable.field) && variable.type && numericTypes.includes(variable.type)) {
2768
- variables.push({
2769
- label: variable.headerName || variable.field,
2770
- value: variable.field,
2771
- });
2772
- }
2773
- });
2774
- return variables.sort((a, b) => a.label.localeCompare(b.label));
2775
- }
2776
- getExpertModePreviewValue() {
2777
- if (!this.expertModeConfig.enabled || !this.expertModeConfig.leftValue) {
2778
- return '-';
2779
- }
2780
- const tempCell = { field: '__preview__', expertMode: { ...this.expertModeConfig } };
2781
- const value = this.computeExpertValue(tempCell);
2782
- if (value === null)
2783
- return 'N/A';
2784
- return formatDecimal(value, { locale: this.getLocaleLang(), numberOfDecimal: 2 }) || String(value);
2785
- }
2786
- computeProgressMaxExpertValue(config) {
2787
- if (!config?.pmEnabled)
2788
- return null;
2789
- const leftType = config.pmLeftType || 'column';
2790
- const rightType = config.pmRightType || 'variable';
2791
- const left = this.resolveExpertOperand(leftType, config.pmLeftValue || '', config.pmLeftSource, config.pmLeftAggregation || 'sum');
2792
- if (left === null)
2793
- return null;
2794
- if (config.pmOperation === 'none')
2795
- return left;
2796
- const rightValue = config.pmRightValue || '';
2797
- if (!rightValue)
2798
- return left;
2799
- const right = this.resolveExpertOperand(rightType, rightValue, config.pmRightSource, config.pmRightAggregation || 'sum');
2800
- if (right === null)
2801
- return left;
2802
- switch (config.pmOperation) {
2803
- case '+': return left + right;
2804
- case '-': return left - right;
2805
- case '*': return left * right;
2806
- case '/': return right !== 0 ? left / right : null;
2807
- default: return left;
2808
- }
2809
- }
2810
- getProgressMaxPreviewValue() {
2811
- if (!this.expertModeConfig.pmEnabled || !this.expertModeConfig.pmLeftValue) {
2812
- return '-';
2813
- }
2814
- const value = this.computeProgressMaxExpertValue(this.expertModeConfig);
2815
- if (value === null)
2816
- return 'N/A';
2817
- return formatDecimal(value, { locale: this.getLocaleLang(), numberOfDecimal: 2 }) || String(value);
2818
- }
2819
- getExpertOperandLabel(type, value, source, aggregation = 'sum') {
2820
- if (!value)
2821
- return '?';
2822
- if (type === 'column') {
2823
- const refs = this.getAllDataGridRefs();
2824
- const sourceLabel = refs.length > 1
2825
- ? refs.find((r) => r.id === source)?.label || ''
2826
- : '';
2827
- const aggLabel = (aggregation || 'sum').toUpperCase();
2828
- return sourceLabel ? `${aggLabel}(${sourceLabel}.${value})` : `${aggLabel}(${value})`;
2829
- }
2830
- const variable = [...this.rows, ...this.variables].find((v) => v.field === value);
2831
- return variable?.headerName || value;
2832
- }
2833
- renderExpertOperandSelector(side, label, tooltipText, prefix = '') {
2834
- const pre = prefix || '';
2835
- const typeKey = (pre + (side === 'left' ? 'LeftType' : 'RightType'));
2836
- const sourceKey = (pre + (side === 'left' ? 'LeftSource' : 'RightSource'));
2837
- const valueKey = (pre + (side === 'left' ? 'LeftValue' : 'RightValue'));
2838
- const aggregationKey = (pre + (side === 'left' ? 'LeftAggregation' : 'RightAggregation'));
2839
- const enabledKey = (pre ? pre + 'Enabled' : 'enabled');
2840
- // For non-prefixed keys (default), use original camelCase: leftType, rightType, etc.
2841
- const typeKeyFinal = pre ? typeKey : (side === 'left' ? 'leftType' : 'rightType');
2842
- const sourceKeyFinal = pre ? sourceKey : (side === 'left' ? 'leftSource' : 'rightSource');
2843
- const valueKeyFinal = pre ? valueKey : (side === 'left' ? 'leftValue' : 'rightValue');
2844
- const aggregationKeyFinal = pre ? aggregationKey : (side === 'left' ? 'leftAggregation' : 'rightAggregation');
2845
- const currentType = this.expertModeConfig[typeKeyFinal];
2846
- const currentSource = this.expertModeConfig[sourceKeyFinal];
2847
- const currentValue = this.expertModeConfig[valueKeyFinal];
2848
- const currentAggregation = this.expertModeConfig[aggregationKeyFinal];
2849
- const isEnabled = !!this.expertModeConfig[enabledKey];
2850
- const typeOptions = [
2851
- { label: msg('Tabulka'), value: 'column' },
2852
- { label: msg('Proměnná'), value: 'variable' },
2853
- ];
2854
- const aggregationOptions = [
2855
- { label: 'SUM', value: 'sum' },
2856
- { label: 'AVG', value: 'avg' },
2857
- { label: 'MIN', value: 'min' },
2858
- { label: 'MAX', value: 'max' },
2859
- { label: 'COUNT', value: 'count' },
2860
- ];
2861
- const gridSourceOptions = this.getGridSourceOptions();
2862
- const showSourceSelector = currentType === 'column' && gridSourceOptions.length >= 1;
2863
- const valueOptions = currentType === 'column'
2864
- ? this.getGridColumnOptions(currentSource)
2865
- : this.getExpertVariableOptions();
2866
- return html `
2867
- <div style="flex: 1; display: flex; flex-direction: column; gap: 4px;">
2868
- <lit-label .label="${label}" .tooltip="${tooltipText}"></lit-label>
2869
- <div style="display: flex; flex-direction: column; gap: 4px;">
2870
- <lit-select
2871
- .options=${typeOptions}
2872
- .value=${currentType}
2873
- .placeholder=${msg('Vyberte typ')}
2874
- .disabled=${!isEnabled}
2875
- .onChange=${(value) => {
2876
- const val = (Array.isArray(value) ? value[0] : value);
2877
- this.expertModeConfig = {
2878
- ...this.expertModeConfig,
2879
- [typeKeyFinal]: val || 'column',
2880
- [valueKeyFinal]: '',
2881
- };
2882
- this.requestUpdate();
2883
- }}
2884
- ></lit-select>
2885
- ${currentType === 'column' && gridSourceOptions.length === 0
2886
- ? html `
2887
- <div style="
2888
- display: flex;
2889
- align-items: center;
2890
- gap: 0.375rem;
2891
- padding: 0.5rem 0.75rem;
2892
- background: var(--color-info-light, #e0f2fe);
2893
- color: var(--color-info-dark, #0369a1);
2894
- border-radius: var(--border-radius-small, 8px);
2895
- font-size: 0.75rem;
2896
- font-weight: 500;
2897
- ">
2898
- <lit-icon name="info" size="16"></lit-icon>
2899
- ${msg('Pro výpočet z tabulky je nutné přidat tabulku.')}
2900
- </div>
2901
- `
2902
- : html `
2903
- ${showSourceSelector
2904
- ? html `
2905
- <lit-select
2906
- .options=${gridSourceOptions}
2907
- .value=${currentSource}
2908
- .placeholder=${msg('Vyberte tabulku')}
2909
- .disabled=${!isEnabled}
2910
- .onChange=${(value) => {
2911
- const val = Array.isArray(value) ? value[0] : value;
2912
- this.expertModeConfig = {
2913
- ...this.expertModeConfig,
2914
- [sourceKeyFinal]: val || '',
2915
- [valueKeyFinal]: '',
2916
- };
2917
- this.requestUpdate();
2918
- }}
2919
- ></lit-select>
2920
- `
2921
- : ''}
2922
- <lit-select
2923
- .options=${valueOptions}
2924
- .value=${currentValue}
2925
- .placeholder=${currentType === 'column' ? msg('Vyberte sloupec') : msg('Vyberte proměnnou')}
2926
- .disabled=${!isEnabled}
2927
- .onChange=${(value) => {
2928
- const val = Array.isArray(value) ? value[0] : value;
2929
- this.expertModeConfig = {
2930
- ...this.expertModeConfig,
2931
- [valueKeyFinal]: val || '',
2932
- };
2933
- this.requestUpdate();
2934
- }}
2935
- ></lit-select>
2936
- ${currentType === 'column'
2937
- ? html `
2938
- <lit-select
2939
- .options=${aggregationOptions}
2940
- .value=${currentAggregation}
2941
- .placeholder=${msg('Vyberte agregaci')}
2942
- .disabled=${!isEnabled}
2943
- .onChange=${(value) => {
2944
- const val = Array.isArray(value) ? value[0] : value;
2945
- this.expertModeConfig = {
2946
- ...this.expertModeConfig,
2947
- [aggregationKeyFinal]: val || 'sum',
2948
- };
2949
- this.requestUpdate();
2950
- }}
2951
- ></lit-select>
2952
- `
2953
- : ''}
2954
- `}
2955
- </div>
2956
- </div>
2957
- `;
2958
- }
2959
- renderExpertModeModal() {
2960
- const operationOptions = [
2961
- { label: msg('Žádná'), value: 'none' },
2962
- { label: '+ (' + msg('Sčítání') + ')', value: '+' },
2963
- { label: '- (' + msg('Odčítání') + ')', value: '-' },
2964
- { label: '× (' + msg('Násobení') + ')', value: '*' },
2965
- { label: '÷ (' + msg('Dělení') + ')', value: '/' },
2966
- ];
2967
- const isNoOperation = this.expertModeConfig.operation === 'none';
2968
- const closeModal = () => {
2969
- this.expertModeModalOpen = false;
2970
- this.expertModeCell = null;
2971
- };
2972
- return html `
2973
- <lit-modal
2974
- .open=${this.expertModeModalOpen}
2975
- .onClose=${closeModal}
2976
- .closeOnOutsideClick=${false}
2977
- style="min-width: 60vw;"
2978
- >
2979
- <lit-modal-header
2980
- style="display: flex; align-items: center; justify-content: space-between; color: var(--text-secondary, #777);"
2981
- >
2982
- ${msg('Expertní mód')} - ${this.expertModeCell?.headerName || this.expertModeCell?.field || ''}
2983
- <lit-icon-button
2984
- .icon="${'close'}"
2985
- variant="text"
2986
- color="secondary"
2987
- size="${'small'}"
2988
- @click=${closeModal}
2989
- ></lit-icon-button>
2990
- </lit-modal-header>
2991
- <lit-modal-body>
2992
- <div style="height: 400px;">
2993
- <lit-tabs-overview
2994
- .tabs=${[
2995
- ...(this.expertModeCell?.type === 'progress'
2996
- ? [{ id: 'progressMax', label: { default: msg('Hodnota 100%') }, icon: 'accomplish' }]
2997
- : []),
2998
- ...(this.expertModeCell?.type === 'number' || this.expertModeCell?.type === 'currency' || this.expertModeCell?.type === 'progress'
2999
- ? [{ id: 'calculation', label: { default: msg('Výpočet') }, icon: 'calculator' }]
3000
- : []),
3001
- ...(this.expertModeCell?.field.startsWith('custom_cell_')
3002
- ? [{ id: 'header', label: { default: msg('Název buňky') }, icon: 'lang' }]
3003
- : []),
3004
- ]}
3005
- .userLang=${this.userLang}
3006
- >
3007
- <!-- Tab: Výpočet (Calculation) -->
3008
- <div slot="calculation" style="display: flex; flex-direction: column; gap: 1rem;">
3009
- <lit-toggle
3010
- .label="${msg('Povolit expertní výpočet')}"
3011
- .tooltip="${msg('Vypočítá hodnotu na základě vybraných sloupců/proměnných a zvolené matematické operace.')}"
3012
- .checked=${this.expertModeConfig.enabled}
3013
- @change=${(e) => {
3014
- this.expertModeConfig = {
3015
- ...this.expertModeConfig,
3016
- enabled: e.detail,
3017
- };
3018
- }}
3019
- ></lit-toggle>
3020
-
3021
- ${this.expertModeConfig.enabled ? html `
3022
- <div style="display: flex; flex-direction: row; gap: 1rem; align-items: flex-start;">
3023
- ${this.renderExpertOperandSelector('left', msg('Levý operand'), msg('Vyberte typ a hodnotu levého operandu. Sloupec vypočítá agregaci (SUM, AVG, MIN, MAX, COUNT) všech hodnot, proměnná použije hodnotu vybrané buňky.'))}
3024
-
3025
- <div style="flex: 0 0 auto; display: flex; flex-direction: column; gap: 4px; min-width: 120px;">
3026
- <lit-label .label="${msg('Operace')}" .tooltip="${msg('Matematická operace mezi levým a pravým operandem. Zvolte Žádná pro zobrazení pouze levého operandu.')}"></lit-label>
3027
- <lit-select
3028
- .options=${operationOptions}
3029
- .value=${this.expertModeConfig.operation}
3030
- .placeholder=${msg('Vyberte operaci')}
3031
- .onChange=${(value) => {
3032
- const val = Array.isArray(value) ? value[0] : value;
3033
- this.expertModeConfig = {
3034
- ...this.expertModeConfig,
3035
- operation: val || '+',
3036
- };
3037
- this.requestUpdate();
3038
- }}
3039
- ></lit-select>
3040
- </div>
3041
-
3042
- ${!isNoOperation ? this.renderExpertOperandSelector('right', msg('Pravý operand'), msg('Vyberte typ a hodnotu pravého operandu. Sloupec vypočítá agregaci (SUM, AVG, MIN, MAX, COUNT) všech hodnot, proměnná použije hodnotu vybrané buňky.')) : ''}
3043
- </div>
3044
-
3045
- <div
3046
- style="
3047
- padding: 0.75rem;
3048
- background: var(--background-default, #f5f5f5);
3049
- border-radius: var(--border-radius-small, 8px);
3050
- font-size: 0.8125rem;
3051
- color: var(--text-secondary, #5d6371);
3052
- "
3053
- >
3054
- <div style="font-weight: 600; margin-bottom: 0.25rem;">${msg('Náhled výpočtu')}</div>
3055
- <div>
3056
- ${this.getExpertOperandLabel(this.expertModeConfig.leftType, this.expertModeConfig.leftValue, this.expertModeConfig.leftSource, this.expertModeConfig.leftAggregation)}
3057
- ${this.expertModeConfig.operation !== 'none'
3058
- ? html `${this.expertModeConfig.operation} ${this.getExpertOperandLabel(this.expertModeConfig.rightType, this.expertModeConfig.rightValue, this.expertModeConfig.rightSource, this.expertModeConfig.rightAggregation)}`
3059
- : ''}
3060
- = <strong style="color: var(--text-primary, #111827);">${this.getExpertModePreviewValue()}</strong>
3061
- </div>
3062
- </div>
3063
- ` : ''}
3064
- </div>
3065
-
3066
- <!-- Tab: Hodnota 100% (Progress Max) -->
3067
- ${this.expertModeCell?.type === 'progress' ? html `
3068
- <div slot="progressMax" style="display: flex; flex-direction: column; gap: 1rem;">
3069
- <lit-toggle
3070
- .label="${msg('Vypočítat hodnotu 100%')}"
3071
- .tooltip="${msg('Vypočítá hodnotu pro 100% na základě vybraných sloupců/proměnných a zvolené matematické operace.')}"
3072
- .checked=${this.expertModeConfig.pmEnabled}
3073
- @change=${(e) => {
3074
- this.expertModeConfig = {
3075
- ...this.expertModeConfig,
3076
- pmEnabled: e.detail,
3077
- };
3078
- }}
3079
- ></lit-toggle>
3080
-
3081
- ${this.expertModeConfig.pmEnabled ? html `
3082
- <div style="display: flex; flex-direction: row; gap: 1rem; align-items: flex-start;">
3083
- ${this.renderExpertOperandSelector('left', msg('Levý operand'), msg('Vyberte typ a hodnotu levého operandu pro výpočet hodnoty 100%.'), 'pm')}
3084
-
3085
- <div style="flex: 0 0 auto; display: flex; flex-direction: column; gap: 4px; min-width: 120px;">
3086
- <lit-label .label="${msg('Operace')}" .tooltip="${msg('Matematická operace mezi levým a pravým operandem.')}"></lit-label>
3087
- <lit-select
3088
- .options=${operationOptions}
3089
- .value=${this.expertModeConfig.pmOperation}
3090
- .placeholder=${msg('Vyberte operaci')}
3091
- .onChange=${(value) => {
3092
- const val = Array.isArray(value) ? value[0] : value;
3093
- this.expertModeConfig = {
3094
- ...this.expertModeConfig,
3095
- pmOperation: val || 'none',
3096
- };
3097
- this.requestUpdate();
3098
- }}
3099
- ></lit-select>
3100
- </div>
3101
-
3102
- ${this.expertModeConfig.pmOperation !== 'none' ? this.renderExpertOperandSelector('right', msg('Pravý operand'), msg('Vyberte typ a hodnotu pravého operandu pro výpočet hodnoty 100%.'), 'pm') : ''}
3103
- </div>
3104
-
3105
- <div
3106
- style="
3107
- padding: 0.75rem;
3108
- background: var(--background-default, #f5f5f5);
3109
- border-radius: var(--border-radius-small, 8px);
3110
- font-size: 0.8125rem;
3111
- color: var(--text-secondary, #5d6371);
3112
- "
3113
- >
3114
- <div style="font-weight: 600; margin-bottom: 0.25rem;">${msg('Náhled hodnoty 100%')}</div>
3115
- <div>
3116
- ${this.getExpertOperandLabel(this.expertModeConfig.pmLeftType, this.expertModeConfig.pmLeftValue, this.expertModeConfig.pmLeftSource, this.expertModeConfig.pmLeftAggregation)}
3117
- ${this.expertModeConfig.pmOperation !== 'none'
3118
- ? html `${this.expertModeConfig.pmOperation} ${this.getExpertOperandLabel(this.expertModeConfig.pmRightType, this.expertModeConfig.pmRightValue, this.expertModeConfig.pmRightSource, this.expertModeConfig.pmRightAggregation)}`
3119
- : ''}
3120
- = <strong style="color: var(--text-primary, #111827);">${this.getProgressMaxPreviewValue()}</strong>
3121
- </div>
3122
- </div>
3123
- ` : ''}
3124
- </div>
3125
- ` : ''}
3126
-
3127
- <!-- Tab: Název buňky (Header) -->
3128
- <div slot="header" style="display: flex; flex-direction: column; gap: 1rem;">
3129
- <lit-label
3130
- .label="${msg('Název buňky')}"
3131
- .tooltip="${msg('Název buňky zobrazený v záhlaví. Můžete nastavit překlad pro různé jazyky.')}"
3132
- ></lit-label>
3133
- <div style="display: flex; flex-direction: column; gap: 0.5rem;">
3134
- <lit-text-field
3135
- .label="${msg('Výchozí název')}"
3136
- .value=${this.expertModeConfig.headerName}
3137
- @onChange=${(e) => {
3138
- this.expertModeConfig = {
3139
- ...this.expertModeConfig,
3140
- headerName: e.detail,
3141
- };
3142
- }}
3143
- ></lit-text-field>
3144
-
3145
- ${(this.allowedLang || []).map((lang) => html `
3146
- <lit-text-field
3147
- .label="${msg('Název')} ${lang.toUpperCase()}"
3148
- .placeholder="${msg('Zadejte název')} ${lang.toUpperCase()}"
3149
- .value=${this.expertModeConfig.headerNameMutations[lang] || ''}
3150
- @onChange=${(e) => {
3151
- this.expertModeConfig = {
3152
- ...this.expertModeConfig,
3153
- headerNameMutations: {
3154
- ...this.expertModeConfig.headerNameMutations,
3155
- [lang]: e.detail,
3156
- },
3157
- };
3158
- }}
3159
- ></lit-text-field>
3160
- `)}
3161
- </div>
3162
- </div>
3163
- </lit-tabs-overview>
3164
- </div>
3165
- </lit-modal-body>
3166
- <lit-modal-footer
3167
- style="display: flex; flex-direction: row; gap: 0.5rem; justify-content: center;"
3168
- >
3169
- <lit-button
3170
- .label="${msg('Uložit')}"
3171
- .icon="check"
3172
- .size="medium"
3173
- @click=${() => this.saveExpertMode()}
3174
- ></lit-button>
3175
- <lit-button
3176
- color="secondary"
3177
- .label="${msg('Zrušit')}"
3178
- variant="text"
3179
- .icon="close"
3180
- .size="medium"
3181
- @click=${closeModal}
3182
- ></lit-button>
3183
- </lit-modal-footer>
3184
- </lit-modal>
3185
- `;
3186
- }
3187
- resolveCurrencyType(cell) {
3188
- const currencyType = cell.currencyType || 'CZK';
3189
- if (currencyType.startsWith('var:')) {
3190
- const variableField = currencyType.substring(4);
3191
- const variable = this.variables.find((v) => v.field === variableField)
3192
- || this.rows.find((c) => c.field === variableField);
3193
- const resolved = String(variable?.value || '').toUpperCase();
3194
- if (LitCaseVariablesTab.SUPPORTED_CURRENCIES.includes(resolved)) {
3195
- return resolved;
3196
- }
3197
- return 'CZK';
3198
- }
3199
- return currencyType;
3200
- }
3201
- resolveProgressMax(cell) {
3202
- // 1. If expert mode progressMax computation is enabled, use it
3203
- const config = cell.expertMode;
3204
- if (config?.pmEnabled) {
3205
- const computed = this.computeProgressMaxExpertValue(config);
3206
- if (computed !== null && computed > 0)
3207
- return computed;
3208
- }
3209
- // 2. Otherwise use static progressMax from tweakpane
3210
- const staticMax = cell.progressMax;
3211
- if (typeof staticMax === 'number' && staticMax > 0)
3212
- return staticMax;
3213
- return 100;
3214
- }
3215
- parseBooleanValue(value) {
3216
- if (typeof value === 'boolean')
3217
- return value;
3218
- if (typeof value === 'string') {
3219
- const lower = value.toLowerCase().trim();
3220
- return lower === 'true' || lower === '1' || lower === 'yes' || lower === 'ano';
3221
- }
3222
- if (typeof value === 'number')
3223
- return value !== 0;
3224
- return !!value;
3225
- }
3226
- render() {
3227
- if (this.hideTabWhen)
3228
- return null;
3229
- this.precomputeExpertValues();
3230
- const filteredKeys = this.variables
3231
- .map((variable) => variable.field)
3232
- .filter((key) => {
3233
- const item = this.variables.find((variable) => variable.field === key);
3234
- const headerName = item?.headerName || '';
3235
- return (key.toLowerCase().includes(this.filterText) ||
3236
- headerName.toLowerCase().includes(this.filterText));
3237
- });
3238
- return html `
3239
- ${this.enableSettings
3240
- ? html `
3241
- <div
3242
- style="display: flex; gap: 0.5rem; justify-content: right; margin-bottom: 1rem; flex-wrap: wrap;"
3243
- >
3244
- <lit-button
3245
- color="secondary"
3246
- variant="${'dashed'}"
3247
- label="${msg('Přiřadit vlastní buňku')}"
3248
- icon="add"
3249
- .fullWidth=${this.isMobile}
3250
- @click="${this.addCustomEmptyCell}"
3251
- class="add-variable-button"
3252
- ></lit-button>
3253
- <lit-button
3254
- color="secondary"
3255
- variant="${'dashed'}"
3256
- label="${msg('Přiřadit proměnnou')}"
3257
- icon="add"
3258
- .fullWidth=${this.isMobile}
3259
- @click="${this.toggleCustomPopover}"
3260
- class="add-variable-button"
3261
- ></lit-button>
3262
-
3263
- <simple-popper
3264
- .showing=${this.isOpen}
3265
- .placement=${'bottom-end'}
3266
- .manualOpening=${true}
3267
- .maxWidthAsTarget=${false}
3268
- .minWidth=${'400px'}
3269
- .minHeight=${'300px'}
3270
- .onClose=${() => this.closePopover()}
3271
- >
3272
- <div
3273
- class="popper-input"
3274
- style="position: sticky; top: 0; z-index: 1;"
3275
- >
3276
- <lit-input
3277
- id="variable-filter-input"
3278
- .value=${this.filterText}
3279
- .onInput=${(value) => {
3280
- this.filterText = value?.toLowerCase?.() || '';
3281
- }}
3282
- .onClear=${() => {
3283
- this.filterText = '';
3284
- }}
3285
- placeholder="${msg('Zadejte název proměnné')}"
3286
- .size=${'small'}
3287
- ></lit-input>
3288
- </div>
3289
- <lit-menu tabindex="0">
3290
- ${this.variables
3291
- .map((variable) => variable.field)
3292
- .filter((key) => {
3293
- const item = this.variables.find((variable) => variable.field === key);
3294
- const headerName = item?.headerName || '';
3295
- return (key.toLowerCase().includes(this.filterText) ||
3296
- headerName.toLowerCase().includes(this.filterText));
3297
- })
3298
- .sort((a, b) => {
3299
- const aIsUnderscore = a.startsWith('_');
3300
- const bIsUnderscore = b.startsWith('_');
3301
- if (aIsUnderscore && !bIsUnderscore)
3302
- return 1;
3303
- if (!aIsUnderscore && bIsUnderscore)
3304
- return -1;
3305
- const aItem = this.variables.find((v) => v.field === a);
3306
- const bItem = this.variables.find((v) => v.field === b);
3307
- const aName = aItem?.headerName || a;
3308
- const bName = bItem?.headerName || b;
3309
- return aName.localeCompare(bName, this.getLocaleLang());
3310
- })
3311
- .map((key) => html `
3312
- <lit-menu-item
3313
- .onClick=${() => this.toggleRowCell(key)}
3314
- .isActive=${this.existingFields.includes(key)}
3315
- >
3316
- <span class="menu-item--multiple">
3317
- <lit-checkbox
3318
- class="cursor"
3319
- .checked=${this.existingFields.includes(key)}
3320
- ></lit-checkbox>
3321
- ${this.variables.find((variable) => variable.field === key)?.headerName || key}
3322
- (${key})
3323
- </span>
3324
- </lit-menu-item>
3325
- `)}
3326
- </lit-menu>
3327
-
3328
- ${isEqual(filteredKeys.length, 0)
3329
- ? html `
3330
- <div
3331
- style="display: flex;flex-direction: column; align-items: center; justify-content: center; height: 15.625rem;"
3332
- >
3333
- <div style="max-height: 7.125rem; max-width: 7.125rem">
3334
- <not-found></not-found>
3335
- </div>
3336
- ${msg('Nenalezeno')}
3337
- </div>
3338
- `
3339
- : null}
3340
- </simple-popper>
3341
- </div>
3342
- `
3343
- : ''}
3344
- ${this.isLoading
3345
- ? html `<lit-loader></lit-loader>`
3346
- : this.gridVariables
3347
- ? html ` <div class="grid-container">
3348
- ${repeat(this.rows, (cell, index) => cell.field, (cell) => {
3349
- const resolvedCell = this.getCellWithExpertValue(cell);
3350
- const classes = `cell--span4`;
3351
- return html `
3352
- <div class="${classes}" data-field="${cell.field}">
3353
- ${this.getInlineCellValue(resolvedCell)}
3354
- </div>
3355
- `;
3356
- })}
3357
- </div>`
3358
- : html `
3359
- <div class="grid-container">
3360
- ${repeat(this.rows, (cell, index) => cell.field, (cell) => {
3361
- const resolvedCell = this.getCellWithExpertValue(cell);
3362
- const bpIndex = this.BREAKPOINTS.indexOf(this.currentBreakpoint);
3363
- const spanSize = this.gridVariables
3364
- ? 4
3365
- : (this.BREAKPOINTS.slice(0, bpIndex + 1)
3366
- .reverse()
3367
- .map((bp) => cell.size?.[bp])
3368
- .find((s) => s !== undefined) ?? 2);
3369
- const classes = `cell--span${spanSize} cell ${this.shouldHideCell(cell) ? 'hidden-cell' : ''}`;
3370
- return html `
3371
- <div
3372
- class="${classes}"
3373
- style="${styleMap(this.computeCellStyles(cell))}"
3374
- data-field="${cell.field}"
3375
- >
3376
- ${resolvedCell?.type === 'button'
3377
- ? this.getCellButton(resolvedCell)
3378
- : resolvedCell.type === 'link'
3379
- ? this.getCellLink(resolvedCell)
3380
- : resolvedCell.type === 'progress'
3381
- ? this.getCellProgress(resolvedCell)
3382
- : resolvedCell.type === 'currency'
3383
- ? this.getCellCurrency(resolvedCell)
3384
- : resolvedCell.type === 'date'
3385
- ? this.getCellDate(resolvedCell)
3386
- : resolvedCell.type === 'number'
3387
- ? this.getCellNumber(resolvedCell)
3388
- : resolvedCell.type === 'checkbox'
3389
- ? this.getCellCheckbox(resolvedCell)
3390
- : this.getCellValue(resolvedCell)}
3391
- </div>
3392
- `;
3393
- })}
3394
- </div>
3395
- `}
3396
- ${this.settingsPopperOpen && this.activeSettingsCell
3397
- ? html `
3398
- <div class="tweakpane-overlay" @click=${this.closeSettingsPopper}>
3399
- <div class="tweakpane-panel" @click=${(e) => e.stopPropagation()}>
3400
- ${this.renderSettingsContent()}
3401
- </div>
3402
- </div>
3403
- `
3404
- : ''}
3405
- ${this.renderExpertModeModal()}
3406
- `;
3407
- }
3408
- }
3409
- LitCaseVariablesTab.VARIANT_CSS_VARS = {
3410
- default: { var: '--background-paper', fallback: '#9ca3af' },
3411
- primary: { var: '--color-primary-light', fallback: '#76b703' },
3412
- secondary: { var: '--color-secondary-light', fallback: '#6b7280' },
3413
- success: { var: '--color-success-light', fallback: '#22c55e' },
3414
- warning: { var: '--color-warning-light', fallback: '#f59e0b' },
3415
- error: { var: '--color-error-light', fallback: '#ef4444' },
3416
- info: { var: '--color-info-light', fallback: '#3b82f6' },
3417
- custom: { var: '--color-primary-light', fallback: '#8b5cf6' },
3418
- ai: { var: '--color-primary-light', fallback: '#a855f7' },
3419
- };
3420
- LitCaseVariablesTab.styles = [
3421
- // styles,
3422
- css `
3423
- :host {
3424
- font-family: 'Inter', sans-serif;
3425
- }
3426
-
3427
- .header-cell {
3428
- min-height: 1.25rem;
3429
- display: flex;
3430
- justify-content: space-between;
3431
- align-items: center;
3432
- }
3433
-
3434
- .drag-handle {
3435
- cursor: grab;
3436
- opacity: 0.6;
3437
- transition: opacity 0.2s;
3438
- }
3439
-
3440
- .drag-handle:hover {
3441
- opacity: 1;
3442
- }
3443
-
3444
- .grid-container {
3445
- display: grid;
3446
- justify-content: start;
3447
- align-items: start;
3448
- gap: 0.5rem;
3449
- border: 1px dashed transparent;
3450
- border-radius: var(--border-radius-small, 8px);
3451
- transition: border-color 0.2s;
3452
- }
3453
-
3454
- .grid-container.sortable-drag-over {
3455
- border-color: var(--color-primary-main, #76b703);
3456
- }
3457
-
3458
- .process-data-heading {
3459
- font-size: 0.75rem;
3460
- color: var(--text-secondary, #5d6371);
3461
- font-weight: 400 !important;
3462
- display: flex;
3463
- gap: 5px;
3464
- align-items: center;
3465
- }
3466
-
3467
- .process-data-value {
3468
- min-height: 1.25rem;
3469
- font-size: 0.8125rem;
3470
- color: var(--text-primary, #111827);
3471
- margin: 0;
3472
- font-weight: 500;
3473
- display: flex;
3474
- align-items: center;
3475
- }
3476
-
3477
- .status {
3478
- color: var(--color-primary-main, #76b703);
3479
- }
3480
-
3481
- .link {
3482
- font-size: 0.8125rem;
3483
- text-decoration: none;
3484
- color: var(--text-primary, #111827);
3485
- border-bottom: 0.0625rem solid var(--color-primary-main, #76b703);
3486
- }
3487
-
3488
- .cell {
3489
- padding: 0.375rem;
3490
- border-radius: var(--border-radius-small, 8px);
3491
- border: 1px solid transparent;
3492
- transition: all 0.2s;
3493
- min-height: 2.625rem;
3494
- }
3495
-
3496
- .cell.one-column {
3497
- min-height: 0 !important;
3498
- }
3499
-
3500
- .cell.sortable-ghost {
3501
- opacity: 0.5;
3502
- border-color: var(--color-primary-main, #76b703);
3503
- }
3504
-
3505
- .cell.sortable-chosen {
3506
- transform: scale(1.02);
3507
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
3508
- }
3509
-
3510
- .link:hover {
3511
- color: var(--color-primary-main, #76b703);
3512
- }
3513
-
3514
- .settings-buttons {
3515
- display: flex;
3516
- align-items: center;
3517
- }
3518
-
3519
- .header-buttons {
3520
- display: flex;
3521
- gap: 0.5rem;
3522
- color: var(--text-secondary, #777);
3523
- }
3524
-
3525
- .size-button {
3526
- margin-right: 0.5rem;
3527
- cursor: pointer;
3528
- }
3529
-
3530
- .size-button--active {
3531
- text-decoration: underline;
3532
- font-weight: bold;
3533
- }
3534
-
3535
- .popper-input {
3536
- margin-bottom: 0.5rem;
3537
- background-color: var(--background-paper, #fff);
3538
- z-index: 1;
3539
- }
3540
-
3541
- .cell-background {
3542
- width: 14px;
3543
- height: 14px;
3544
- border-radius: 4px;
3545
- border: 1px solid #ccc;
3546
- cursor: pointer;
3547
- margin-right: 0.5rem;
3548
- }
3549
-
3550
- .bold-toggle {
3551
- cursor: pointer;
3552
- }
3553
-
3554
- .bold-toggle--active {
3555
- text-decoration: underline;
3556
- font-weight: bold;
3557
- }
3558
-
3559
- .color-label {
3560
- margin: 0.5rem 0 0.25rem 0;
3561
- }
3562
-
3563
- .cell-background--active {
3564
- border-bottom: 1px solid white;
3565
- margin-top: 1px;
3566
- margin-right: 0.5rem;
3567
- }
3568
-
3569
- .add-variable-button {
3570
- display: inline-block;
3571
- }
3572
-
3573
- @media (max-width: 600px) {
3574
- .add-variable-button {
3575
- display: block;
3576
- width: 100%;
3577
- }
3578
- }
3579
-
3580
- .cell--span0 {
3581
- display: none;
3582
- }
3583
- .cell--span1 {
3584
- grid-column: span 1 / span 1;
3585
- }
3586
- .cell--span2 {
3587
- grid-column: span 2 / span 2;
3588
- }
3589
- .cell--span3 {
3590
- grid-column: span 3 / span 3;
3591
- }
3592
- .cell--span4 {
3593
- grid-column: span 4 / span 4;
3594
- }
3595
-
3596
- /* Responzívne nastavenie počtu stĺpcov */
3597
- @media (min-width: 0px) {
3598
- .grid-container {
3599
- grid-template-columns: repeat(4, minmax(0, 1fr)); /* xs */
3600
- }
3601
- }
3602
-
3603
- @media (min-width: var(--breakpoint-sm, 600px)) {
3604
- .grid-container {
3605
- grid-template-columns: repeat(4, minmax(0, 1fr)); /* sm */
3606
- }
3607
- }
3608
-
3609
- @media (min-width: var(--breakpoint-md, 960px)) {
3610
- .grid-container {
3611
- grid-template-columns: repeat(4, minmax(0, 1fr)); /* md */
3612
- }
3613
- }
3614
-
3615
- @media (min-width: var(--breakpoint-lg, 1280px)) {
3616
- .grid-container {
3617
- grid-template-columns: repeat(4, minmax(0, 1fr)); /* lg */
3618
- }
3619
- }
3620
-
3621
- @media (min-width: var(--breakpoint-xl, 1536px)) {
3622
- .grid-container {
3623
- grid-template-columns: repeat(4, minmax(0, 1fr));
3624
- }
3625
- }
3626
-
3627
- .hidden-cell {
3628
- display: none;
3629
- }
3630
-
3631
- .tweakpane-container {
3632
- min-width: 250px;
3633
- max-width: 300px;
3634
- }
3635
-
3636
- /* Basic Tweakpane in tooltip styling - applying to document to bypass shadow DOM */
3637
- :host([data-tweakpane-loaded]) {
3638
- display: block;
3639
- }
3640
- `,
3641
- ];
3642
- LitCaseVariablesTab.SUPPORTED_CURRENCIES = [
3643
- 'CZK', 'EUR', 'USD', 'GBP', 'CHF', 'PLN', 'HUF',
3644
- 'JPY', 'AUD', 'CAD', 'NOK', 'SEK', 'DKK', 'CNY', 'RUB',
3645
- ];
3646
- __decorate([
3647
- property({ type: Array })
3648
- ], LitCaseVariablesTab.prototype, "rows", void 0);
3649
- __decorate([
3650
- property({ type: Array })
3651
- ], LitCaseVariablesTab.prototype, "variables", void 0);
3652
- __decorate([
3653
- property({ type: Boolean })
3654
- ], LitCaseVariablesTab.prototype, "hideTabWhen", void 0);
3655
- __decorate([
3656
- property({ type: String })
3657
- ], LitCaseVariablesTab.prototype, "userLang", void 0);
3658
- __decorate([
3659
- property({ type: Array })
3660
- ], LitCaseVariablesTab.prototype, "allowedLang", void 0);
3661
- __decorate([
3662
- property({ type: String })
3663
- ], LitCaseVariablesTab.prototype, "dateFormat", void 0);
3664
- __decorate([
3665
- property({ type: Boolean })
3666
- ], LitCaseVariablesTab.prototype, "isLoading", void 0);
3667
- __decorate([
3668
- property({ type: Boolean })
3669
- ], LitCaseVariablesTab.prototype, "enableSettings", void 0);
3670
- __decorate([
3671
- property({ type: String })
3672
- ], LitCaseVariablesTab.prototype, "tabId", void 0);
3673
- __decorate([
3674
- property({ type: Boolean })
3675
- ], LitCaseVariablesTab.prototype, "gridVariables", void 0);
3676
- __decorate([
3677
- property({ type: Function })
3678
- ], LitCaseVariablesTab.prototype, "onSettingsChanged", void 0);
3679
- __decorate([
3680
- property({ type: String })
3681
- ], LitCaseVariablesTab.prototype, "hostURL", void 0);
3682
- __decorate([
3683
- property({ attribute: false })
3684
- ], LitCaseVariablesTab.prototype, "dataGridRef", void 0);
3685
- __decorate([
3686
- property({ attribute: false })
3687
- ], LitCaseVariablesTab.prototype, "dataGridRefs", void 0);
3688
- __decorate([
3689
- state()
3690
- ], LitCaseVariablesTab.prototype, "currentBreakpoint", void 0);
3691
- __decorate([
3692
- state()
3693
- ], LitCaseVariablesTab.prototype, "isOpen", void 0);
3694
- __decorate([
3695
- state()
3696
- ], LitCaseVariablesTab.prototype, "filterText", void 0);
3697
- __decorate([
3698
- state()
3699
- ], LitCaseVariablesTab.prototype, "isMobile", void 0);
3700
- __decorate([
3701
- state()
3702
- ], LitCaseVariablesTab.prototype, "expertModeModalOpen", void 0);
3703
- __decorate([
3704
- state()
3705
- ], LitCaseVariablesTab.prototype, "expertModeCell", void 0);
3706
- __decorate([
3707
- state()
3708
- ], LitCaseVariablesTab.prototype, "expertModeConfig", void 0);
3709
- __decorate([
3710
- state()
3711
- ], LitCaseVariablesTab.prototype, "activeSettingsCell", void 0);
3712
- __decorate([
3713
- state()
3714
- ], LitCaseVariablesTab.prototype, "settingsPopperOpen", void 0);
3715
- if (!window.customElements.get('lit-case-variables-tab')) {
3716
- window.customElements.define('lit-case-variables-tab', LitCaseVariablesTab);
3717
- }
3718
- //# sourceMappingURL=lit-case-variables-tab.js.map