Compare commits

...

434 Commits

Author SHA1 Message Date
Valentin-Gabriel Mihai
7f13fa62d3 Merge branch 'unprotected' into 'master'
update master

See merge request fforesight/shared-ui-libraries/common-ui!15
2024-12-13 13:15:01 +01:00
Dan Percic
4ed7215292 skip quality gate 2024-12-13 13:16:06 +02:00
Dan Percic
1ea220b489 Merge remote-tracking branch 'origin/master' into unprotected 2024-12-13 13:15:39 +02:00
Valentin Mihai
fc8be33dc6 RED-9580 - added getOne method for stats service to call new users stats endpoint and updated data from "delete user dialog" with new backend data 2024-12-13 12:02:51 +02:00
Valentin Mihai
58382ddfee RED-10301 - set app type config vars based existing tenants from local storage or used default config if no tenant was set before 2024-12-11 18:22:28 +02:00
Nicoleta Panaghiu
0d20afcf27 RED-10647: update manual base link depending on application type. 2024-12-11 18:08:21 +02:00
Nicoleta Panaghiu
aa5fc54576 RED-9856: support a smaller avatar size. 2024-12-10 17:02:25 +02:00
Christoph Schabert
e5c4fd7d3c add sonarqube config 2024-12-09 15:27:35 +01:00
Nicoleta Panaghiu
a4e3ed8854 RED-3800: removed debug statements. 2024-12-07 14:07:55 +02:00
Nicoleta Panaghiu
acabdba657 RED-3800: fixed sass update breaking changes. 2024-12-07 14:03:56 +02:00
Nicoleta Panaghiu
0e6e4f7b09 RED-10614: fixed missing injection context error, 2024-12-06 19:22:12 +02:00
Dan Percic
7bb24c4f91 lint 2024-12-05 12:50:45 +02:00
Dan Percic
e21c225ddd updates 2024-12-05 12:49:51 +02:00
Dan Percic
8582f2e6be ng 19 2024-12-05 11:46:15 +02:00
Dan Percic
b929f1d136 Merge branch 'VM/RED-10301' into 'unprotected'
RED-10301 - Use RM/DM UI depending on application type of tenant

See merge request fforesight/shared-ui-libraries/common-ui!14
2024-12-03 13:16:28 +01:00
Valentin Mihai
55b21c4989 lint 2024-12-03 12:37:45 +02:00
Valentin Mihai
911516add2 RED-10301 - Use RM/DM UI depending on application type of tenant 2024-12-03 12:31:24 +02:00
Nicoleta Panaghiu
cbdfcf4d8f RED-10517: fixed selected sorting. 2024-11-27 16:52:17 +02:00
Nicoleta Panaghiu
72e760fff8 RED-9885: scoped right-container transition. 2024-11-27 16:24:51 +02:00
Nicoleta Panaghiu
ae0eebcc6f RED-10509: added disableStopPropagation option for action config. 2024-11-24 13:30:43 +02:00
Timo Bejan
7c6f9fc25e removed logging 2024-11-06 15:49:04 +02:00
Timo Bejan
310cc4bb51 Removed offset and fixed notification polling 2024-11-06 15:39:31 +02:00
Valentin Mihai
99facc0434 RED-10373 - updated details radio option description to can receive number params 2024-11-04 16:55:40 +02:00
Dan Percic
ebaf1709b1 add some overrides & keycloak token$ 2024-11-01 19:20:42 +02:00
Nicoleta Panaghiu
17d2e8c530 RED-10206: added disabled and tooltip directives for button config. 2024-11-01 14:46:19 +02:00
Dan Percic
b9c01a287c lint fixes 2024-10-31 11:28:14 +02:00
Dan Percic
6c7865a8ec fix filters track expression 2024-10-31 11:26:49 +02:00
Nicoleta Panaghiu
9e457a13b4 RED-8277: only show pointer cursor if entity has routerLink. 2024-10-23 16:09:16 +03:00
Nicoleta Panaghiu
fba9b330dd RED-8277: use pointer cursor for table items. 2024-10-22 17:55:52 +03:00
Valentin Mihai
d7ad450ca1 RED-7340 - red border around the page input field should only appear after the user has finished entering the page range 2024-10-22 17:19:48 +03:00
Valentin Mihai
a5d10ad148 RED-7340 - When a user has entered an invalid page range string, display a read border around the input field 2024-10-21 17:52:48 +03:00
Nicoleta Panaghiu
e92bd55cfc RED-3800: some fixes after switching from div to a. 2024-10-17 11:59:38 +03:00
Nicoleta Panaghiu
f36b1fa8e2 RED-8277: use a instead of div. 2024-10-16 15:26:29 +03:00
Nicoleta Panaghiu
3f214d9726 RED-10139: moved formToSignal functions to common.
Future refactoring purposes.
2024-10-16 13:44:53 +03:00
Valentin Mihai
ba85260cc4 RED-7340 - set fix width for extra option input 2024-10-16 11:55:33 +03:00
Nicoleta Panaghiu
e88929f0d4 RED-10180: increased extraOption container width. 2024-10-14 17:25:31 +03:00
Nicoleta Panaghiu
32de775859 RED-10183: set default language in user preference. 2024-10-14 12:48:58 +03:00
Valentin Mihai
3c89b8f7e7 RED-7340 - remove not used imports 2024-10-05 20:48:00 +03:00
Valentin Mihai
34387d49d2 RED-7340 - updated details radio component to have an option input when is needed 2024-09-27 16:41:59 +03:00
Nicoleta Panaghiu
304657d259 RED-9985: prevent filter categories from collapsing upon selection. 2024-09-26 11:51:52 +03:00
Nicoleta Panaghiu
835cb7820e RED-9578: move custom component above the question. 2024-09-25 13:41:37 +03:00
Adina Țeudan
3724a6c0b6 Ignored keys for form changed in dialogs 2024-09-23 22:23:02 +03:00
Nicoleta Panaghiu
c71a4995a6 RED-9578: added support for custom component and reversing buttons. 2024-09-19 14:52:04 +03:00
Valentin Mihai
c644eaeba2 RED-7345 - added force-annotation class 2024-09-16 21:15:52 +03:00
Nicoleta Panaghiu
2faecb44a9 RED-9372: always include scroll-bar mixin. 2024-09-10 11:37:55 +03:00
Nicoleta Panaghiu
81513d34dc RED-9372: fixed table-items moving on hover. 2024-09-05 17:14:22 +03:00
Nicoleta Panaghiu
9bc05f1165 RED-9987: added sendSetPasswordMail flag. 2024-09-03 13:45:40 +03:00
Nicoleta Panaghiu
007e761bd5 RED-9916: fixed button ids. 2024-08-20 13:22:07 +03:00
Nicoleta Panaghiu
0ca5e7e2ad RED-9777: increase dialog help button z-index. 2024-08-14 11:42:31 +03:00
Nicoleta Panaghiu
6547eb2ad5 RED-9777: fixed viewer header alignment + added components view parent. 2024-08-12 11:15:53 +03:00
Adina Țeudan
17943f2e8d HasScrollbar directive with signals 2024-08-09 23:51:51 +03:00
Adina Țeudan
c8b3e3eb3c Revert "RED-9372: fixed table-item elements moving on hover when scrollable."
This reverts commit 845c819392bc5f73a5feb584dc879e965107a9c0.
2024-08-09 23:51:21 +03:00
Nicoleta Panaghiu
c331a61309 RED-9817: removed HTML code for space. 2024-08-09 12:15:50 +03:00
Valentin Mihai
5b51eb85a1 RED-9788 - remove active listing entity service 2024-08-09 08:56:48 +03:00
Valentin Mihai
f079b6c157 RED-9201 - update upload component height 2024-08-08 11:05:01 +03:00
Adina Țeudan
a8b5cfce14 Unordered async guards 2024-08-07 21:30:41 +03:00
Adina Țeudan
85c6d91b2e Some updates 2024-08-07 21:14:40 +03:00
Valentin Mihai
7f8336a64f RED-3800 - Refactoring / Code Cleanup 2024-08-07 20:32:30 +03:00
Valentin Mihai
1155c16f8a RED-3800 - Refactoring / Code Cleanup 2024-08-07 20:29:44 +03:00
Valentin Mihai
66277814c5 RED-9776 - added active entity class 2024-08-04 12:30:14 +03:00
Nicoleta Panaghiu
0fa39cf900 RED-9504: use translate pipe & clean imports. 2024-08-02 11:33:53 +03:00
Nicoleta Panaghiu
28acee573d RED-9731: fixed help button position on resize + small filters fix. 2024-08-01 14:38:32 +03:00
Nicoleta Panaghiu
80fceb45dd RED-9516: fixed filter options alignment. 2024-07-30 17:03:14 +03:00
Nicoleta Panaghiu
fb4b58a496 RED-9433: added auto line-break for checkbox label overflow. 2024-07-30 14:14:13 +03:00
Nicoleta Panaghiu
9bbaecb7e5 RED-9571: fixed initials-avatar not updating in some cases. 2024-07-30 11:12:16 +03:00
Valentin Mihai
9df87dc218 RED-9201 - addded id to file name label 2024-07-26 15:23:45 +03:00
Adina Țeudan
646e16557f Added icons 2024-07-25 18:43:09 +03:00
Nicoleta Panaghiu
98ac49fbc8 RED-9657: fixed help mode links. 2024-07-16 13:32:58 +03:00
Nicoleta Panaghiu
845c819392 RED-9372: fixed table-item elements moving on hover when scrollable. 2024-07-15 16:11:11 +03:00
Adina Țeudan
c33ae3c918 getparamFromDialog fn 2024-07-12 18:51:08 +03:00
Nicoleta Panaghiu
88743acacc RED-9437: fix help mode button placement in dialogs. 2024-07-09 14:48:40 +03:00
Dan Percic
9b1179d99a Signal updates 2024-07-09 12:11:15 +03:00
Valentin Mihai
01c244aa07 RED-9513 - Enter Key Not Working: Unable to Add Terms to Dictionary 2024-07-04 12:52:26 +03:00
Valentin Mihai
ce334dbdeb RED-9362 - Help Mode tooltip missing 2024-06-27 15:32:15 +03:00
Valentin Mihai
1c48cea02e RED-9453 - prevent enter key 2024-06-27 14:53:44 +03:00
Valentin Mihai
ca0db25484 RED-9390 - Workload filter does not remember previous ALL or NONE selection 2024-06-27 13:23:35 +03:00
Dan Percic
f872158c5f Merge branch 'VM/RED-8748' into 'unprotected'
RED-8748 - Integrated component view in DocuMine

See merge request fforesight/shared-ui-libraries/common-ui!12
2024-06-26 14:49:35 +02:00
Valentin Mihai
f24e5dbb82 RED-8748 - added hidden property to filter model 2024-06-25 00:21:16 +03:00
Adina Țeudan
590ebcbae2 Moved fullscreen icons to common-ui 2024-06-20 14:57:38 +03:00
Dan Percic
f3faa6a6cc migrate inputs to signals & remove inputs/index.ts 2024-06-20 11:36:47 +03:00
Dan Percic
d595a22db1 unfuck circular imports 2024-06-19 13:05:12 +03:00
Nicoleta Panaghiu
04ae68891c Merge branch 'unprotected' into RED-9321 2024-06-18 12:45:20 +03:00
Dan Percic
f60ea513ac migrate control flow 2024-06-18 10:22:27 +03:00
Dan Percic
0b64044f57 lint 2024-06-17 21:27:16 +03:00
Dan Percic
0738d8c4ef migrate buttons to signal inputs 2024-06-17 21:26:02 +03:00
Dan Percic
76771023c2 lint 2024-06-17 19:18:01 +03:00
Dan Percic
213cabce7a ng 18 2024-06-17 19:15:43 +03:00
Adina Țeudan
e165ba7500 Clamp filter values 2024-06-17 15:59:51 +03:00
Adina Țeudan
4c0437d7fc Simple popup filter improvements 2024-06-17 15:29:26 +03:00
Adina Țeudan
aec5da15df Simple popup filter 2024-06-17 12:18:25 +03:00
Nicoleta Panaghiu
40c6f881fc RED-9321: add new file to index. 2024-06-14 15:06:29 +03:00
Nicoleta Panaghiu
a764ab1050 RED-9321: refactored help mode module. 2024-06-14 14:59:33 +03:00
Nicoleta Panaghiu
960b434ef6 RED-9321: made some components standalone, removed upload-file module. 2024-06-12 14:33:00 +03:00
Dan Percic
748cce4032 Merge branch 'VM/RED-8748' into 'unprotected'
RED-8748 - Integrated component view in DocuMine

See merge request fforesight/shared-ui-libraries/common-ui!11
2024-06-11 19:53:00 +02:00
Valentin Mihai
4ccde998e0 Merge branch 'unprotected' into VM/RED-8748 2024-06-11 20:17:46 +03:00
Adina Țeudan
04eaca1600 Moved visibility icons to common-ui 2024-06-10 16:22:48 +03:00
Valentin Mihai
e733adf374 Merge branch 'unprotected' into VM/RED-8748 2024-06-10 14:41:24 +03:00
Valentin Mihai
5eef6d403a check for existing help mode button 2024-06-06 18:29:48 +03:00
Adina Țeudan
5b7dd6a55a Added clamp-5 mixin 2024-05-29 21:01:50 +03:00
Valentin Mihai
3a9f36e71b RED-8882 - Help Mode design improvements 2024-05-29 10:52:33 +03:00
Valentin Mihai
a668b23118 Merge branch 'unprotected' into VM/RED-8748 2024-05-22 21:42:37 +03:00
Valentin Mihai
e7fca876bb RED-8882 - made help button smaller 2024-05-22 21:31:22 +03:00
Adina Țeudan
e8f5bc8f2c RED-6959: Added selected items count to table header 2024-05-22 14:37:08 +03:00
Valentin Mihai
1674055943 Merge branch 'unprotected' into VM/RED-8748 2024-05-20 19:56:44 +03:00
Valentin Mihai
4811d301e6 RED-8882 - removed base dialog 2024-05-20 19:06:58 +03:00
Valentin Mihai
174d77d2ea RED-8882 - moved help mode dialog preference into iqser-user-preferences.service 2024-05-20 17:10:25 +03:00
Valentin Mihai
848e46cf6a removed user preferences service from help mode service 2024-05-20 16:27:20 +03:00
Valentin Mihai
b7c8634407 removed base dialog from help mode dialog 2024-05-20 14:31:30 +03:00
Valentin Mihai
5cc09f251e fix build 2024-05-20 10:39:36 +03:00
Valentin Mihai
ee1d7b2445 Merge branch 'VM/RED-8882' into unprotected 2024-05-20 10:35:38 +03:00
Valentin Mihai
9473a7f5bd Merge branch 'unprotected' into VM/RED-8748 2024-05-17 19:09:48 +03:00
Valentin Mihai
217a44c2e0 removed not needed imports 2024-05-17 18:59:48 +03:00
Valentin Mihai
0021307c71 RED-8882 - Help Mode design improvements 2024-05-17 17:50:32 +03:00
Adina Țeudan
6f288516e3 Minor UI fixes, base dialog improvements 2024-05-16 23:31:52 +03:00
Valentin Mihai
252c261621 lint 2024-05-16 18:47:35 +03:00
Valentin Mihai
786d235de0 RED-8882 - Help Mode design improvements 2024-05-16 18:46:25 +03:00
Nicoleta Panaghiu
e737d134ad RED-9097: fixed workflow file attributes not updating. 2024-05-08 13:37:47 +03:00
Valentin Mihai
3b0b6542cd lint 2024-04-30 16:09:34 +03:00
Valentin Mihai
61d4d4f5c6 RED-8748 - updated filter service 2024-04-30 16:06:25 +03:00
Adina Țeudan
12c5c1cb4e Fix 2024-04-29 17:06:07 +03:00
Adina Țeudan
02a9cb49e9 Inputs styles 2024-04-29 17:03:34 +03:00
Adina Țeudan
fb6951a7ba Some styles, paginated get all ids 2024-04-29 16:59:35 +03:00
Adina Țeudan
e7a04dda9e Loading indicator z-index, include ngx-toastr in common styles 2024-04-29 15:18:39 +03:00
Adina Țeudan
696e7f6f6d Paginated entities service updates (sorting) 2024-04-18 00:22:45 +03:00
Adina Țeudan
d4b69a18a9 Deprecated (close) -> (closed) 2024-04-17 21:01:13 +03:00
Adina Țeudan
4cf42c8943 Fixed filter card input padding 2024-04-17 20:32:51 +03:00
Adina Țeudan
fae7d912d2 Fixed filter card input padding 2024-04-17 20:32:19 +03:00
Adina Țeudan
fc06bcc31d Use flex gap instead of margin right in tables 2024-04-11 18:25:01 +03:00
Adina Țeudan
005167487d Enable multiselect checkbox 2024-04-11 15:43:37 +03:00
Adina Țeudan
c12ed0c968 Transform "active" boolean attribute in round-checkbox 2024-04-11 15:29:28 +03:00
Adina Țeudan
cbd9fd055b RED-8731: Updated material & fixed toggle appearance 2024-04-11 15:14:58 +03:00
Adina Țeudan
301ea99abe CSS updates 2024-04-09 00:17:42 +03:00
Adina Țeudan
8a2033740e Moved color-picker to common-ui 2024-04-07 21:32:38 +03:00
Nicoleta Panaghiu
c12b6f4d35 RED-8622: implemented add entity functionality. 2024-04-05 13:07:32 +03:00
Adina Țeudan
e08e28e095 Fixed icon button hover ripple color 2024-04-04 17:08:39 +03:00
Nicoleta Panaghiu
957dc404a8 RED-8817: decreased extra option description opacity. 2024-03-26 15:52:04 +02:00
Nicoleta Panaghiu
0dd210b04f RED-8817: aligned description with option label. 2024-03-25 10:46:12 +02:00
Adina Țeudan
b6813bccf3 Added prevent default to stop propagation directive 2024-03-22 00:17:13 +02:00
Nicoleta Panaghiu
48a5160d6f RED-8817: added description support for extraOption. 2024-03-21 17:01:37 +02:00
Adina Țeudan
d67798bd60 Pagination updates 2024-03-18 17:22:45 +02:00
Nicoleta Panaghiu
292573f3b3 RED-8728: made the required asterisk red. 2024-03-15 15:03:58 +02:00
Adina Țeudan
55dcb40dcd Button styles 2024-03-12 22:13:01 +02:00
Adina Țeudan
ecf9c8912e Upload file component updates 2024-03-07 18:15:32 +02:00
Dan Percic
9b427b0cac add a devInfo method to toaster service 2024-03-07 15:57:11 +02:00
Adina Țeudan
47e371a41e Refactor 2024-03-06 17:25:53 +02:00
Adina Țeudan
9faca532c6 Rename, cleanup 2024-03-06 17:04:38 +02:00
Adina Țeudan
bf628b33a2 Paginated entities service: search support 2024-03-06 17:02:44 +02:00
Nicoleta Panaghiu
3ea4e45b87 RED-8679: added support for custom table item id formats. 2024-03-01 17:07:58 +02:00
Nicoleta Panaghiu
94e0021ed4 RED-8226: added support for raw toastr messages. 2024-02-28 12:56:39 +02:00
Nicoleta Panaghiu
a2d30f0a65 RED-8565: fixed reset password redirect. 2024-02-21 10:08:02 +02:00
Valentin Mihai
7b29cc5f31 RED-6960 - enter should confirm modal if it is changed or if it is not in edit mode 2024-02-20 23:30:21 +02:00
Adina Țeudan
59f2dde9ec Fixed suspicious nullish coalescing 2024-02-20 20:57:28 +02:00
Nicoleta Panaghiu
0010eba410 Merge branch 'VM/RED-7874' into 'unprotected'
RED-7874 - Integrate "user has no roles" message in sign-in page

See merge request fforesight/shared-ui-libraries/common-ui!9
2024-02-20 14:56:43 +01:00
Valentin Mihai
5d31262fb3 RED-7874 - moved 'select-tenant-translations' to common-ui 2024-02-20 15:17:22 +02:00
Valentin Mihai
a37287f13c RED-7874 - Integrate "user has no roles" message in sign-in page 2024-02-20 14:54:23 +02:00
Adina Țeudan
124ecee0bf Remove trailing slash from generic service _getOne (if empty path) 2024-02-19 11:19:41 +02:00
Valentin Mihai
6f255d68b4 fix var name 2024-02-16 17:48:47 +02:00
Valentin Mihai
82b11d2eba RED-8342 - Component Editor not showing all values for a multi-value-component 2024-02-15 13:25:15 +02:00
Valentin Mihai
42cc494e2f RED-8161 - Hard to read Document Versions in Read-Only Mode 2024-02-13 17:19:09 +02:00
Adina Țeudan
67485fdac2 Add loading indicator to orderedAsyncGuards 2024-02-10 11:13:28 +02:00
Valentin Mihai
0885ad776d RED-6960 - Enter should confirm a modal window by default 2024-02-06 20:13:52 +02:00
Adina Țeudan
b4007f3fe5 RED-3800: Lint 2024-02-04 10:38:15 +02:00
Adina Țeudan
37c9d7a65d Moved some icons to common-ui 2024-02-04 10:37:23 +02:00
Dan Percic
7c7cdfda51 fix translation key 2024-02-01 11:12:03 +01:00
Nicoleta Panaghiu
6dbc7a4621 RED-8249: fixed the blurry logo in safari. 2024-02-01 12:10:24 +02:00
Adina Țeudan
dadafa6117 Nevermind 2024-01-31 17:49:23 +02:00
Adina Țeudan
03d4bd5c8b Fixed missing await 2024-01-31 17:05:40 +02:00
Dan Percic
3f23c6d66e move if not logged in guard 2024-01-31 11:51:49 +01:00
Adina Țeudan
6d97f7588b Fix 2024-01-31 12:46:51 +02:00
Adina Țeudan
773171f4af Get tenant details 2024-01-31 11:40:09 +02:00
Nicoleta Panaghiu
9ce0ec27ca RED-8109: decreased focus shadow size. 2024-01-17 10:41:36 +02:00
Nicoleta Panaghiu
bd77067dc7 RED-8164: fixed newly created users cannot login. 2024-01-05 13:06:49 +02:00
Valentin Mihai
da086cdaa6 RED-6960 - Enter should always confirm a modal window 2023-12-20 10:32:18 +02:00
Nicoleta Panaghiu
7f09e57248 RED-7919: fixed calendar icon alignment. 2023-12-14 16:38:14 +02:00
George
c4bdba4683 RED-3800 - fix tenant switch. 2023-12-04 15:35:46 +02:00
George
68dbdfb8e7 RED-8012 - fixed blank page when reloading. 2023-12-04 14:19:04 +02:00
George
986d552e49 RED-3800 - add get tenant function token. 2023-12-04 10:57:35 +02:00
Dan Percic
fa574115aa update path 2023-11-29 17:26:43 +02:00
George
d1c0559099 RED-3800, refactor multitenancy 2023-11-29 17:06:37 +02:00
Dan Percic
59fbd1f78f updates 2023-11-29 17:05:59 +02:00
Dan Percic
2bb459961a updates 2023-11-17 11:43:32 +02:00
Valentin Mihai
87e1c88452 lint 2023-11-13 13:27:08 +02:00
Valentin Mihai
8ba271bb6c RED-6960 - Enter should always confirm a modal window 2023-11-13 13:23:31 +02:00
Valentin Mihai
63df71a6aa DM-536 - added disabled property 2023-11-10 20:06:32 +02:00
Valentin Mihai
4b656564fe DM-536 - drawn an arrow down in circle button component to look like a dropdown button when it's the case 2023-11-10 19:12:00 +02:00
Adina Țeudan
052bf3a5d7 Input with action updates 2023-11-02 21:13:27 +02:00
Adina Țeudan
ecb5fd63ac Paginated entities update 2023-11-02 05:24:09 +02:00
Adina Țeudan
5f0904e6a9 Paginated entities updates 2023-11-01 18:56:22 +02:00
Adina Țeudan
99b2c83d61 Moved pagination component to common ui 2023-11-01 18:32:02 +02:00
Adina Țeudan
a6f8a35765 Renamed roles guards 2023-11-01 00:58:52 +02:00
Adina Țeudan
6eef9aba4b Updated hasRoleGuard 2023-11-01 00:56:59 +02:00
Adina Țeudan
54d60b6c97 Editable input form width 2023-10-31 18:10:22 +02:00
Adina Țeudan
f09ef68442 Fixed infinite loop in tenants service 2023-10-31 16:48:13 +02:00
Dan Percic
85fba4a1dd RED-7619 add config to toaster rawError method 2023-10-30 15:00:54 +02:00
Nicoleta Panaghiu
a6383c1dbc RED-6893: Stop loading after error is handled. 2023-10-20 15:34:51 +03:00
Adina Țeudan
bfaae5adf6 Use disabled instead of canEditInput in dynamic input 2023-10-19 21:58:09 +03:00
Adina Țeudan
df01d0a910 Select: active item color 2023-10-11 11:46:02 +03:00
Adina Țeudan
bd532cd28f Moved switch tenant logic to common-ui 2023-10-05 16:43:25 +03:00
Adina Țeudan
a9d6e5e728 Fixed checkbox & toggle 2023-10-02 15:32:38 +03:00
Nicoleta Panaghiu
14f4f00898 RED-7649: Added raw error toaster. 2023-09-29 14:39:20 +03:00
Nicoleta Panaghiu
6dc910c4ca RED-3800: greyed out disabled select value. 2023-09-25 17:54:17 +03:00
Adina Țeudan
1b76808917 RED-3800: Redo HasScrollbar directive 2023-09-24 12:19:55 +03:00
Adina Țeudan
890cf417a1 RED-3800: Functional composite route guard, skeleton fixes 2023-09-21 18:30:19 +03:00
Dan Percic
2f2ee530b1 update dialog service 2023-09-21 11:13:05 +03:00
Nicoleta Panaghiu
8d4a68f92c RED-7605: Fixed page header overlap. 2023-09-19 11:36:25 +03:00
Dan Percic
6cb63fcf43 remove console.trace 2023-09-15 19:09:30 +03:00
Dan Percic
9fba181eb9 updates 2023-09-15 19:07:19 +03:00
Dan Percic
a4132b82f5 add css class 2023-09-15 13:53:09 +03:00
Valentin Mihai
d1df30b56e updated 'error' and ' success' toast colors to be always red and green, no matter the app 2023-09-11 13:13:07 +03:00
Valentin Mihai
f0c51915b2 Merge branch 'VM/DM-358' into unprotected 2023-09-08 16:07:02 +03:00
Valentin Mihai
4f4123cd5d DM-358 - set some keys depending on user roles 2023-09-08 15:48:27 +03:00
Valentin Mihai
c83eb568db DM-358 - added documine theme for helpers 2023-09-07 23:15:22 +03:00
Valentin Mihai
da0e057721 DM-358 - added all keys 2023-09-07 19:23:36 +03:00
Adina Țeudan
523f2c999e DM-414: Base dialog change detection 2023-09-06 20:31:45 +03:00
Valentin Mihai
d4d593d24a DM-358 - WIP on implementing Help Mode-IDs for DocuMine 2023-09-06 18:53:28 +03:00
Valentin Mihai
c7a15d2a93 DM-358 - WIP on implementing Help Mode-IDs for DocuMine 2023-09-05 17:34:48 +03:00
Dan Percic
60ace01151 Merge branch 'RED-7528' into 'master'
RED-7528: Fixed documents jumping around on drag&drop.

See merge request fforesight/shared-ui-libraries/common-ui!5
2023-09-04 10:00:49 +02:00
Nicoleta Panaghiu
cbbc21609e RED-7528: Fixed documents jumping around on drag&drop. 2023-09-04 10:57:55 +03:00
Timo Bejan
41ca2a28ec Merge branch 'clarifynd-common' into 'master'
Paginated entities service update, mat slider update

See merge request fforesight/shared-ui-libraries/common-ui!4
2023-08-30 23:57:11 +02:00
Adina Țeudan
3cf27e38c1 Paginated entities service update, mat slider update 2023-08-31 00:55:06 +03:00
Dan Percic
21d387912c Merge branch 'de-ce-protected-branch' into 'master'
update color for disabled options

See merge request fforesight/shared-ui-libraries/common-ui!3
2023-08-30 20:23:09 +02:00
Dan Percic
f140f1fc16 lints 2023-08-30 10:10:36 +03:00
Dan Percic
45622e7fd6 update color for disabled options 2023-08-30 09:59:56 +03:00
Dan Percic
c9abddb301 Merge branch 'de-ce-protected-branch' into 'master'
update styles to fix material changes

See merge request fforesight/shared-ui-libraries/common-ui!2
2023-08-28 09:24:58 +02:00
Dan Percic
5a14bf79fa fix indent 2023-08-25 23:27:56 +03:00
Dan Percic
1e4247c247 update styles to fix material changes 2023-08-25 23:22:47 +03:00
Adina Țeudan
6f3833a7f0 Textarea line height based on variable 2023-08-23 23:10:00 +03:00
Adina Țeudan
f5f86d7e60 Circle button variable radius, dark-bg type 2023-08-23 18:43:36 +03:00
Adina Țeudan
1fc2820742 CSS variable for input font size 2023-08-23 18:28:43 +03:00
Adina Țeudan
867037cc0a Update css variables 2023-08-23 18:13:15 +03:00
George
03eb179298 Fix checkboxes jumping up and theming. 2023-08-18 11:51:49 +03:00
Nicoleta Panaghiu
db37aa6257 RED-7214: Fixed filter bugs. 2023-08-18 10:54:28 +03:00
Dan Percic
fed7c6a180 fix some svgs 2023-08-17 16:01:45 +03:00
George
6f05c8fc86 DM-383, theme spinner. 2023-08-17 14:42:55 +03:00
Dan Percic
625603def6 update preferences service 2023-08-17 13:49:52 +03:00
Adina Țeudan
e553ba3272 No wrap on checkboxes label 2023-08-16 02:36:34 +03:00
Dan Percic
ed00a87a7a update styles 2023-08-09 19:49:31 +03:00
Adina Țeudan
a52f55b5d4 Inputs style, paginated updates 2023-08-08 14:15:40 +03:00
Dan Percic
04a69f5e68 remove emails from stored tenants 2023-08-08 11:28:38 +03:00
Dan Percic
c851ab1394 escape html updates 2023-08-07 15:24:06 +03:00
George
5a4ed98aaf RED-7115, use fallback in translation, remove red-ui references. 2023-08-03 17:05:41 +03:00
George
fdaf591751 RED-7115, remove red-ui import in common-ui. 2023-08-03 16:23:25 +03:00
Adina Țeudan
5d8b8ee5cf Moved some icons to common-ui 2023-08-03 16:18:48 +03:00
George
60b0d502ae RED-7115, edit radio button type description. 2023-08-03 14:34:21 +03:00
Adina Țeudan
b1b8ce2e49 Logo size variable 2023-08-02 21:29:08 +03:00
Adina Țeudan
78b74d5a9a Some updates 2023-08-02 20:52:09 +03:00
Adina Țeudan
5d40f20a32 Icons update 2023-08-02 15:56:31 +03:00
Valentin Mihai
3b84e73f80 DM-327 - Adaptable size of Component Edit value fields 2023-07-31 20:18:29 +03:00
George
28badd45cd RED-7069, replace buttons. RED-7279, change notification texts. 2023-07-28 14:01:44 +03:00
Valentin Mihai
bbd0cef55b DM-337 - added translation params for title 2023-07-28 12:35:11 +03:00
Valentin Mihai
0fd87149ae DM-337 - added translation params 2023-07-27 22:05:38 +03:00
George
e1323070c6 RED-7069, add unified edit dialog. 2023-07-27 18:31:50 +03:00
Valentin Mihai
b368fa6fe8 lint 2023-07-24 23:44:46 +03:00
Valentin Mihai
8f4cc53d0f RED-7155 - updated details radio component to can disable a specific option if it's needed 2023-07-24 23:43:38 +03:00
Valentin Mihai
5aa064ac65 DM-337 - escaped non breaking spaces for option description 2023-07-24 18:39:37 +03:00
George
7ff7864767 CM-355, fix changed after check error caused by loading service. 2023-07-24 12:53:00 +03:00
George
d40f839365 RED-7200, rewrite loading service with signals, specifically call stop when needed, remove catch all stop from composite guard postcheck. 2023-07-24 11:33:16 +03:00
Dan Percic
2524896f46 increase last checked offset 2023-07-21 12:55:18 +03:00
Dan Percic
fd4491b2a3 hide focus indicator 2023-07-19 16:09:36 +03:00
Dan Percic
e69f8b3f0d add is_documine env var 2023-07-18 16:43:40 +03:00
Dan Percic
699e74a867 explicitly call close dialog on escape or backdrop 2023-07-18 15:20:29 +03:00
Dan Percic
ae52093b5d fix app name font 2023-07-18 11:47:13 +03:00
George
66662a0314 RED-7116, change redirect so it wouldnt cause a loop, prevent double navigations. 2023-07-18 11:25:27 +03:00
George
d3ac14497d RED-7116, fixed double clicking causing infinite loop. 2023-07-17 17:31:19 +03:00
Dan Percic
dc5f14c696 fix logo 2023-07-17 17:12:55 +03:00
George
83f13eda4f RED-7120, prevent focusing input from closing filters. 2023-07-17 14:25:12 +03:00
Valentin Mihai
a8f5fb2e25 RED-7041 - added disabled property for extra option 2023-07-10 15:19:00 +03:00
Dan Percic
eb58e96d01 update toaster service 2023-07-10 12:57:00 +03:00
Valentin Mihai
a4e425d373 RED-6774 - added hidden property for extra option 2023-07-09 22:48:23 +03:00
Dan Percic
137a62338c Add disable stop propagation 2023-07-07 18:19:43 +03:00
Dan Percic
f33ada72f4 RED-6713 remove hasMultiple from tenantsService 2023-07-07 17:03:11 +03:00
Dan Percic
da18c68a86 redirect to login when keycloak instance null 2023-07-07 13:24:08 +03:00
Dan Percic
30846478ed use oauth url with origin & fix http requests 2023-07-07 12:07:19 +03:00
Dan Percic
f631395f61 add logger 2023-07-06 17:08:42 +03:00
Dan Percic
4ee4842f74 try see login url 2023-07-06 17:03:38 +03:00
Dan Percic
a71e094e55 try fix imports 2023-07-06 14:20:57 +03:00
Dan Percic
8ac4bbfd9f RED-6713 update tenants handling 2023-07-06 13:09:45 +03:00
Valentin Mihai
cc8d040654 RED-6829 - INC15516657: Performance issues 2023-07-04 17:12:42 +03:00
Valentin Mihai
7efa3b7ad2 RED-6801 - added active style to icon button 2023-07-04 16:25:14 +03:00
Adina Țeudan
2d6ee6655c RED-6830: Refactor size format pipe 2023-06-29 16:13:02 +03:00
Adina Țeudan
aa4516286e RED-6830: File size format pipe 2023-06-27 14:58:50 +03:00
Adina Țeudan
02d0089d0d RED-3800: Remove unused directive import 2023-06-27 11:57:50 +03:00
Adina Țeudan
7d2f5c8a77 RED-3800: input with action should propagate clicks in the input 2023-06-27 02:07:57 +03:00
Timo Bejan
8e5376bd4f RED-6686 removed console log 2023-06-27 02:03:29 +03:00
Timo Bejan
3cef35f3bf Merge branch 'RED-6686' into 'master'
Red 6686

See merge request fforesight/shared-ui-libraries/common-ui!1
2023-06-26 23:15:35 +02:00
Timo Bejan
0ae0fa819e Red 6686 2023-06-26 23:15:35 +02:00
Dan Percic
75356e2328 RED-3800: check for null 2023-06-26 17:07:08 +03:00
Dan Percic
d02dadc56a RED-3800: update dialog directive 2023-06-26 15:28:04 +03:00
George
01d06f1c87 RED-6802, fixed the notification not showing up. 2023-06-23 16:04:57 +03:00
Adina Țeudan
7821b5a2f6 RED-3800: CSS fix 2023-06-22 18:49:59 +03:00
George
1a8ab7b4b9 RED-6802, fixed notification not appearing if emitted quickly. 2023-06-21 19:30:52 +03:00
Valentin Mihai
afbfaf2959 RED-6774 - updated details radio component 2023-06-21 00:52:03 +03:00
Dan Percic
88babfd1f6 RED-3800 add dialog components 2023-06-20 14:44:03 +03:00
Dan Percic
2fe65233bc RED-6890 remove comment 2023-06-16 19:08:17 +03:00
Dan Percic
063f019de1 RED-6713: lint fix 2023-06-16 18:29:58 +03:00
Dan Percic
39b4ff1f97 RED-6713: update tenant selection 2023-06-16 18:26:48 +03:00
George
1522d08a32 RED-6802, make disconnect notification appear after 3 seconds of offline time. 2023-06-16 13:11:16 +03:00
George
a4692e8ee3 RED-6763, fix wobbly spinner. 2023-06-09 19:26:38 +03:00
George
58d4ece28d RED-6802, fixed the reconnect notification changing rapidly. 2023-06-09 17:10:15 +03:00
Dan Percic
8ebd5f760d RED-6713: sort tenants 2023-06-02 17:11:29 +03:00
Dan Percic
140ad55646 RED-6713: store tenant with email or username 2023-06-02 12:17:31 +03:00
Adina Țeudan
cfaddff1f7 RED-6343: Shift+click selection in table (anywhere on list item) 2023-06-01 23:57:32 +03:00
Adina Țeudan
31586bca72 RED-5875: Material upgrade - fixes 2023-06-01 21:44:16 +03:00
Adina Țeudan
daf516e9fe RED-5875: Material upgrade - fixes 2023-06-01 13:54:02 +03:00
Adina Țeudan
47080bcd8b RED-5875: Material upgrade - fixes 2023-05-31 23:34:32 +03:00
Adina Țeudan
efa5229391 RED-5875: Material upgrade - tooltip letter spacing 2023-05-31 00:42:21 +03:00
Adina Țeudan
3d9a34b778 RED-5875: Material upgrade - buttons: user button 2023-05-31 00:37:53 +03:00
Adina Țeudan
9e8a1fc12e RED-5875: Material upgrade - buttons: chevron button 2023-05-31 00:08:00 +03:00
Adina Țeudan
a71a09463f RED-5875: Material upgrade - buttons: circle button 2023-05-30 23:27:31 +03:00
Adina Țeudan
4714856531 RED-5875: Material upgrade - buttons: icon button 2023-05-30 21:47:41 +03:00
Adina Țeudan
b50bd54227 RED-5875: Material upgrade - slide toggle 2023-05-30 16:01:29 +03:00
Adina Țeudan
b50019b01c RED-5875: Material upgrade - progress bar 2023-05-29 23:21:35 +03:00
Adina Țeudan
95b528deb6 RED-5875: Material upgrade - spinner 2023-05-29 22:18:55 +03:00
Adina Țeudan
2ec18cc91d RED-5875: Material upgrade - slider 2023-05-29 22:08:04 +03:00
Adina Țeudan
e884d8a513 RED-5875: Material upgrade - checkbox 2023-05-29 21:24:06 +03:00
Dan Percic
226ce2e73a RED-6713 remove spacer if not logged out 2023-05-25 21:03:32 +03:00
Dan Percic
0b9db19708 RED-6713 lint fix 2023-05-25 20:59:09 +03:00
Dan Percic
3590385c9c RED-6713: fix headings & dont remove tenant on logout 2023-05-25 20:49:49 +03:00
Dan Percic
6b70e92a0e RED-6713: add login hint 2023-05-24 18:43:35 +03:00
Dan Percic
a06124ab4e RED-6713: remove stored tenant on logout 2023-05-24 14:25:24 +03:00
Dan Percic
5449cacc2b RED-6713: lint 2023-05-21 18:55:46 +03:00
Dan Percic
223fbe688c RED-6713: show previous signed in domains 2023-05-21 18:53:40 +03:00
Dan Percic
721d3e3b1a RED-3800: fix 2023-05-18 19:41:44 +03:00
Dan Percic
d0551742ec RED-6713 remove tenants context holder 2023-05-18 12:19:53 +03:00
Dan Percic
dbfde290c8 RED-3800: change eslintrc file type 2023-05-17 13:56:46 +03:00
Dan Percic
2868ebe506 RED-5875: migrate menus 2023-05-17 13:36:16 +03:00
Dan Percic
ba4d7f6d05 ng 16 2023-05-17 01:38:33 +03:00
Dan Percic
e46649c656 RED-3800: update eslint 2023-05-17 00:08:32 +03:00
Dan Percic
4c87375ee5 RED-3800: add marker 2023-05-17 00:08:32 +03:00
George
cc90d9a561 RED-6604: refactoring 2023-04-21 15:50:50 +03:00
Valentin Mihai
b024e3ef0b updated table content 'help-mode' class name to 'help-mode-active' 2023-04-20 18:37:05 +03:00
George
77a84882cc RED-6558, fixed download notifications redirect. 2023-04-20 18:23:44 +03:00
George
7e4e51f82c Fix thrown error in edit modals. 2023-04-12 18:08:58 +03:00
Valentin Mihai
b57998fcb4 Merge branch 'VM/6510' 2023-04-10 18:25:54 +03:00
Valentin Mihai
8158e22991 Merge branch 'master' into VM/6510 2023-04-10 18:03:27 +03:00
Valentin Mihai
d8eff726c9 RED-6150: lint 2023-04-10 18:02:11 +03:00
Valentin Mihai
5c442ced9f RED-6510 - WIP on "Lazy load help mode elements" 2023-04-10 17:53:30 +03:00
Nicoleta Panaghiu
75c851bd52 RED-6428: Created help mode link for Edit file attribute. 2023-04-10 12:44:34 +03:00
Valentin Mihai
4aef8c608d Merge branch 'master' into VM/6510 2023-04-08 22:49:35 +03:00
Valentin Mihai
6deb24dc41 RED-6510 - WIP on "Lazy load help mode elements" 2023-04-08 22:48:49 +03:00
Dan Percic
03084293b2 catch tenant errors 2023-04-08 20:17:01 +03:00
Dan Percic
1d354ac868 remove APP_INITIALIZER, update tenants handling 2023-04-08 19:04:28 +03:00
Valentin Mihai
8a61630a37 RED-6510 - WIP on "Lazy load help mode elements" 2023-04-06 14:59:24 +03:00
Timo Bejan
6c63f851cf RED-6523 - ui multitenancy links 2023-04-05 16:20:48 +03:00
Timo Bejan
97470bb723 Pull request #24: RED-6523 - multitenancy
Merge in SL/common-ui from RED-6523 to master

* commit '3c3ae597c1c533ba9d5728ee633e8f60b80d5720':
  RED-6523 - multitenancy
2023-04-04 15:04:41 +02:00
Timo Bejan
3c3ae597c1 RED-6523 - multitenancy 2023-04-04 16:04:04 +03:00
Dan Percic
b4364900b5 tenants debugging 2023-04-03 16:22:24 +03:00
Dan Percic
efabd98719 clean imports 2023-04-03 16:03:42 +03:00
Valentin Mihai
9bb7619907 Merge branch 'RED-6523' 2023-03-31 15:52:05 +03:00
Dan Percic
86b52a643a lint fix 2023-03-31 13:39:35 +03:00
Dan Percic
5c24018d12 fix routerLinks for multi tenancy 2023-03-31 13:38:39 +03:00
Timo Bejan
00d55e470f RED-6523 - multitenancy 2023-03-31 12:30:39 +03:00
Valentin Mihai
3c3badfca1 lint 2023-03-30 20:39:51 +03:00
Valentin Mihai
79b688b218 RED-6453 - Value for attribute in file list cannot be set 2023-03-30 20:31:19 +03:00
Dan Percic
dbe1e9fae1 check for routerLink when using stop propagation 2023-03-30 14:06:31 +03:00
Nicoleta Panaghiu
bb562d63fd RED-6436: Corrected tooltips positions. 2023-03-28 15:21:22 +03:00
Valentin Mihai
ba21886e27 RED-6420 - No refreshes anymore for new user notifications 2023-03-27 23:38:30 +03:00
Dan Percic
b4156074eb stricter rules 2023-03-26 14:50:45 +03:00
Dan Percic
4f58a6603e delete stored route when attached 2023-03-26 14:49:32 +03:00
Valentin Mihai
e1f818c109 lint 2023-03-22 21:12:47 +02:00
Valentin Mihai
4c9db877cd (no branch):
RED-6370 - added disabled property to dynamic input component
2023-03-22 21:11:41 +02:00
Adina Țeudan
9eecf327c2 Full opacity option for .small-label 2023-03-22 12:37:49 +02:00
Dan Percic
03421b43d9 fix checkboxes 2023-03-20 19:11:30 +02:00
Dan Percic
a60a9d828e stop propagation if action observed 2023-03-20 15:29:25 +02:00
Dan Percic
a1d11cd9eb update confirmation dialog component 2023-03-20 00:02:47 +02:00
Dan Percic
cb8a0ddcf9 some fixes 2023-03-19 16:03:16 +02:00
Dan Percic
edf2e5cc93 update jest config 2023-03-19 15:53:44 +02:00
Dan Percic
39e862b129 update permissions paths/directives 2023-03-19 14:33:40 +02:00
Dan Percic
1a4e79529b standalone permissions 2023-03-19 13:58:34 +02:00
Dan Percic
c9c2f3e0f9 make inputs standalone 2023-03-19 12:01:43 +02:00
Dan Percic
0057b3c4b0 standalone scrollbar directive 2023-03-19 10:09:06 +02:00
Dan Percic
a2e40583e3 use stopPropagation & preventDefault 2023-03-18 02:11:12 +02:00
Dan Percic
655874989b add stopPropagation & preventDefault directives 2023-03-17 23:56:47 +02:00
Dan Percic
8ac6f58319 standalone shared components 2023-03-17 22:56:01 +02:00
Dan Percic
87b19da9a9 update pipes 2023-03-16 23:47:30 +02:00
Dan Percic
e1ebbb9ab2 make empty-state standalone 2023-03-16 23:19:42 +02:00
Dan Percic
8650dd472f remove IqserIconsModule 2023-03-16 22:02:39 +02:00
Dan Percic
e67bfdf659 Merge branch 'release-3.988.0' 2023-03-16 19:26:55 +02:00
Dan Percic
9124c1f284 check also form value changes when using autocomplete 2023-03-16 19:25:03 +02:00
Dan Percic
b8f9e6a46d RED-3800: remove buttons module 2023-03-15 23:24:00 +02:00
Dan Percic
08d06803e0 RED-3800: make some components standalone 2023-03-15 21:34:49 +02:00
George
223080763f RED-6255, fix autocomplete not marking the form as valid for modals (colors mentioned in the ticket). 2023-03-14 14:36:24 +02:00
George
d09078e44c RED-6154, add search input id to page header component. 2023-03-06 15:27:09 +02:00
Dan Percic
e21ca5059e update last changed before the request 2023-03-06 11:22:20 +02:00
Dan Percic
33fb6b0525 catch caches undefined error 2023-03-02 12:48:22 +02:00
George
1fda6967b1 RED-6154, add ids to file upload input, radio detail groups and empty state button. 2023-03-01 15:15:34 +02:00
Adina Țeudan
03eedf10c8 RED-5875: Upgrade to new Angular Material components (chips) 2023-02-28 23:31:47 +02:00
Dan Percic
7ca5db9cee check caches for undefined 2023-02-27 15:52:03 +02:00
Valentin Mihai
2761140489 lint 2023-02-26 19:43:25 +02:00
Valentin Mihai
cdfd0b59b4 RED-5445: emit event when date picker is opened/closed 2023-02-26 19:38:53 +02:00
George
d9a92992c3 RED-6154, remove getter from template, replace label based ids with generics. 2023-02-23 21:45:57 +02:00
George
d956c5f68d RED-6154, add unique ids to page header and table header inputs. 2023-02-22 21:20:12 +02:00
Adina Țeudan
a2bde131ad RED-3800: Remove (click) from iqser-buttons 2023-02-21 20:43:29 +02:00
Adina Țeudan
daa92eeccf RED-5875: Upgrade to new Angular Material components (select) 2023-02-21 19:49:55 +02:00
Adina Țeudan
8cf17cb855 RED-5875: Upgrade to new Angular Material components (select) 2023-02-21 19:31:20 +02:00
George
66f13e9ef0 RED-6122: Add new comparator for form values. 2023-02-16 19:55:30 +02:00
Valentin Mihai
42988aa9ed lint 2023-02-15 16:37:57 +02:00
Valentin Mihai
b91586c470 RED-5445 - Adding value to file attribute in file list 2023-02-15 16:37:12 +02:00
Dan Percic
9891c2bf67 add isToday function 2023-02-10 19:07:05 +02:00
Dan Percic
b8c3b76e56 rename function any to some to be consistent with the array method 2023-02-09 22:13:37 +02:00
Adina Țeudan
7c1c031429 Cleanup imports 2023-02-09 20:16:48 +02:00
Adina Țeudan
6ed1cacc4d RED-5875: Upgrade to new Angular Material components (tooltip) 2023-02-09 20:16:05 +02:00
Adina Țeudan
e71a11b4a1 RED-5875: Upgrade to new Angular Material components (tooltip) 2023-02-09 18:49:37 +02:00
Adina Țeudan
36e5f65beb Merge remote-tracking branch 'origin/master' into RED-6037 2023-02-08 19:19:46 +02:00
Adina Țeudan
c19636567f Disable-ish shift click text selection 2023-02-08 19:18:11 +02:00
George
2cd389c73d RED-6129, add ids to inputs. 2023-02-08 17:25:51 +02:00
Dan Percic
3a5c098128 add function 2023-02-08 17:24:20 +02:00
Adina Țeudan
baad3e1dac Shift click selection logic 2023-02-08 17:20:13 +02:00
Adina Țeudan
610a88570b Move sortedDisplayedEntities from ListingComponent to ListingService 2023-02-08 16:22:58 +02:00
Dan Percic
fe9a50d367 update ids & remove on push 2023-02-08 14:46:23 +02:00
Dan Percic
40517f145e add escapeHtml function & custom translate parser 2023-02-07 23:24:28 +02:00
Adina Țeudan
65da4e2d53 Export 2023-02-07 16:14:27 +02:00
Dan Percic
c217b5f07f add AsControl type 2023-02-06 15:10:19 +02:00
Valentin Mihai
a6d59a82b9 lint 2023-02-02 12:06:22 +02:00
Valentin Mihai
76ee8afbc2 moved all help mode service constants in a separate file, updated service properties, added logic to use helper elements over the webviewer iframe 2023-02-02 11:59:07 +02:00
Valentin Mihai
484ff6eed5 removed comment 2023-01-31 16:48:46 +02:00
Adina Țeudan
3d07e09e11 Parameters for menu css & user button 2023-01-31 08:41:37 +02:00
Adina Țeudan
40e640bc62 Inputs module imports 2023-01-27 14:58:44 +02:00
Adina Țeudan
d45fd37af4 RED-5875: Upgrade to new Angular Material components (dialog) 2023-01-27 14:48:21 +02:00
Valentin Mihai
35386f60e0 lint 2023-01-25 22:07:59 +02:00
Valentin Mihai
eb84e5f348 added dynamic input component 2023-01-25 22:01:28 +02:00
Adina Țeudan
1bef72a321 Text button type 2023-01-21 09:57:51 +02:00
Adina Țeudan
5208063f48 Top bar height as css variable 2023-01-20 12:04:02 +02:00
Adina Țeudan
e96093a81b Top bar height as css variable 2023-01-20 11:44:48 +02:00
Adina Țeudan
ab8d164205 RED-6015: Only show skeleton for logged in users 2023-01-19 07:47:40 +02:00
Adina Țeudan
b0bea9fefc RED-6033: Show error message on update profile (400 & 409) 2023-01-19 07:32:18 +02:00
Timo Bejan
1f9bace6a6 RED-3800 commit hash fix 2023-01-18 13:40:53 +08:00
Adina Țeudan
d7b80af5bb Lint 2023-01-18 07:15:47 +02:00
Adina Țeudan
a2bb0360d7 App name CSS vars 2023-01-18 07:04:09 +02:00
Timo Bejan
03d63fc78e RED-5700 - idp hint on login link 2023-01-18 12:52:43 +08:00
Adina Țeudan
2b98f871ed CSS cleanup 2023-01-18 06:51:56 +02:00
Adina Țeudan
36317aae75 Show toast on my profile update error 2023-01-17 07:45:32 +02:00
Adina Țeudan
f98109ca45 Fix event propagation on disabled circle button 2023-01-17 06:18:03 +02:00
Timo Bejan
c91f923bfa RED-5950 logout error on session expired 2023-01-15 14:40:41 +08:00
330 changed files with 6890 additions and 3262 deletions

269
.eslintrc.js Normal file
View File

@ -0,0 +1,269 @@
module.exports = {
root: true,
env: {
browser: true,
},
globals: {
NodeJS: true,
},
ignorePatterns: ['!**/*', 'jest.config.ts'],
overrides: [
{
files: ['*.ts'],
extends: ['plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
rules: {
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/explicit-member-accessibility': [
'error',
{
accessibility: 'no-public',
},
],
'@typescript-eslint/unbound-method': 'error',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'memberLike',
modifiers: ['private'],
format: ['camelCase'],
leadingUnderscore: 'allow',
},
{
selector: 'memberLike',
modifiers: ['protected'],
format: ['camelCase'],
leadingUnderscore: 'allow',
},
{
selector: 'memberLike',
modifiers: ['private'],
format: ['UPPER_CASE', 'camelCase'],
leadingUnderscore: 'allow',
},
],
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/lines-between-class-members': 'off',
'@typescript-eslint/member-ordering': [
'warn',
{
default: [
// Index signature
'signature',
'call-signature',
// Fields
'#private-static-field',
'private-static-field',
'protected-static-field',
'public-static-field',
'#private-instance-field',
'private-instance-field',
'protected-instance-field',
'public-instance-field',
'private-decorated-field',
'protected-decorated-field',
'public-decorated-field',
'protected-abstract-field',
'public-abstract-field',
'#private-field',
'private-field',
'protected-field',
'public-field',
'static-field',
'instance-field',
'abstract-field',
'decorated-field',
'field',
// Static initialization
'static-initialization',
// Constructors
'public-constructor',
'protected-constructor',
'private-constructor',
'constructor',
// Getters
'public-static-get',
'protected-static-get',
'private-static-get',
'#private-static-get',
'public-decorated-get',
'protected-decorated-get',
'private-decorated-get',
'public-instance-get',
'protected-instance-get',
'private-instance-get',
'#private-instance-get',
'public-abstract-get',
'protected-abstract-get',
'public-get',
'protected-get',
'private-get',
'#private-get',
'static-get',
'instance-get',
'abstract-get',
'decorated-get',
'get',
// Setters
'public-static-set',
'protected-static-set',
'private-static-set',
'#private-static-set',
'public-decorated-set',
'protected-decorated-set',
'private-decorated-set',
'public-instance-set',
'protected-instance-set',
'private-instance-set',
'#private-instance-set',
'public-abstract-set',
'protected-abstract-set',
'public-set',
'protected-set',
'private-set',
'#private-set',
'static-set',
'instance-set',
'abstract-set',
'decorated-set',
'set',
// Methods
'public-static-method',
'protected-static-method',
'private-static-method',
'#private-static-method',
'public-decorated-method',
'protected-decorated-method',
'private-decorated-method',
'public-instance-method',
'protected-instance-method',
'private-instance-method',
'#private-instance-method',
'public-abstract-method',
'protected-abstract-method',
'public-method',
'protected-method',
'private-method',
'#private-method',
'static-method',
'instance-method',
'abstract-method',
'decorated-method',
'method',
],
},
],
},
},
{
files: ['*.ts'],
extends: [
'eslint:recommended',
'plugin:@angular-eslint/recommended',
'plugin:@angular-eslint/template/process-inline-templates',
'plugin:prettier/recommended',
'plugin:rxjs/recommended',
],
rules: {
'rxjs/no-ignored-subscription': 'error',
'@angular-eslint/prefer-standalone': 'off',
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'iqser',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'iqser',
style: 'kebab-case',
},
],
'@angular-eslint/prefer-on-push-component-change-detection': 'error',
'@angular-eslint/use-lifecycle-interface': 'error',
'@angular-eslint/no-input-prefix': 'error',
'@angular-eslint/no-input-rename': 'error',
'@angular-eslint/no-output-on-prefix': 'error',
'@angular-eslint/no-output-rename': 'error',
'@angular-eslint/prefer-output-readonly': 'error',
'no-underscore-dangle': 'off',
'no-param-reassign': 'error',
'no-dupe-class-members': 'off',
'no-redeclare': 'off',
'no-unused-vars': 'off',
'consistent-return': 'off',
},
},
{
files: ['*.html'],
extends: ['plugin:@angular-eslint/template/recommended'],
},
{
files: ['*.html'],
excludedFiles: ['*inline-template-*.component.html'],
extends: ['plugin:prettier/recommended'],
rules: {
'prettier/prettier': [
'error',
{
parser: 'angular',
},
],
},
},
{
files: ['**/*.spec.ts'],
env: {
node: true,
jest: true,
},
rules: {
'@angular-eslint/prefer-on-push-component-change-detection': 'off',
},
},
],
};

View File

@ -1,68 +0,0 @@
{
"env": {
"browser": true,
"node": true
},
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["**/*.ts"],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "iqser",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "iqser",
"style": "kebab-case"
}
],
"@angular-eslint/prefer-on-push-component-change-detection": "error",
"@angular-eslint/use-lifecycle-interface": "error",
"@angular-eslint/no-input-prefix": "error",
"@angular-eslint/no-input-rename": "error",
"@angular-eslint/no-output-on-prefix": "error",
"@angular-eslint/no-output-rename": "error",
"@angular-eslint/prefer-output-readonly": "error",
"@typescript-eslint/unbound-method": "error",
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "memberLike",
"modifiers": ["private"],
"format": ["camelCase"],
"leadingUnderscore": "allow"
},
{
"selector": "memberLike",
"modifiers": ["protected"],
"format": ["camelCase"],
"leadingUnderscore": "allow"
},
{
"selector": "memberLike",
"modifiers": ["private"],
"format": ["UPPER_CASE", "camelCase"],
"leadingUnderscore": "allow"
}
],
"no-underscore-dangle": "off",
"no-param-reassign": "error",
"no-dupe-class-members": "off",
"no-redeclare": "off",
"no-unused-vars": "off",
"consistent-return": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/lines-between-class-members": "off"
}
}
]
}

19
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,19 @@
sonarqube:
stage: test
image:
name: sonarsource/sonar-scanner-cli:11.1
entrypoint:
- ''
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
GIT_DEPTH: '0'
cache:
key: "${CI_JOB_NAME}"
paths:
- ".sonar/cache"
script:
- sonar-scanner
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
- if: "$CI_COMMIT_BRANCH =~ /^release/"

View File

@ -77,6 +77,7 @@ Update `styles` and `stylePreprocessorOptions` in `angular.json`:
}
```
Add theming classes (`light` or `dark`) to the `<body>` element.
Configure light and dark theme variables in `styles.scss`.

26
jest.config.ts Normal file
View File

@ -0,0 +1,26 @@
import type { Config } from 'jest';
import { defaults } from 'jest-config';
export default {
...defaults,
displayName: 'common-ui',
preset: 'jest-preset-angular',
setupFilesAfterEnv: ['./setup-jest.ts'],
coverageDirectory: '../../coverage/libs/common-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: './tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
testEnvironment: 'jest-environment-jsdom',
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
} as Config;

1
setup-jest.ts Normal file
View File

@ -0,0 +1 @@
import 'jest-preset-angular/setup-jest';

2
sonar-project.properties Normal file
View File

@ -0,0 +1,2 @@
sonar.projectKey=common-ui
sonar.qualitygate.wait=false

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="12px" viewBox="0 0 12 12" width="12px" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="User-Management" stroke="none" stroke-width="1">
<g id="01.2-Bulk-Actions" transform="translate(-876.000000, -468.000000)">
<rect fill="#FFFFFF" height="900" width="1440" x="0" y="0"></rect>
<polygon fill="#FFFFFF" id="Rectangle" points="201 449 1087 449 1087 499 201 499"></polygon>
<g fill="#DD4D50" id="Group-19" transform="translate(876.000000, 171.000000)">
<path
d="M6.00002308,297 C9.30875217,297 12.0000462,299.690683 12.0000462,302.999359 C12.0000462,306.309082 9.30898674,309 6.00002308,309 C2.69105942,309 2.27373675e-13,306.309082 2.27373675e-13,302.999359 C2.27373675e-13,299.690683 2.69129399,297 6.00002308,297 Z M6.00002308,298.846154 C3.71080282,298.846154 1.84615385,300.71038 1.84615385,302.999359 C1.84615385,305.289458 3.71064029,307.153846 6.00002308,307.153846 C8.28940586,307.153846 10.1538923,305.289458 10.1538923,302.999359 C10.1538923,300.71038 8.28924333,298.846154 6.00002308,298.846154 Z M6.89921108,303.700197 L6.89921108,305.678219 L5.10100111,305.678219 L5.10100111,303.700197 L6.89921108,303.700197 Z M6.89921108,300.103957 L6.89921108,302.801137 L5.10100111,302.801137 L5.10100111,300.103957 L6.89921108,300.103957 Z"
id="Combined-Shape"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="100px" version="1.1" viewBox="0 0 100 100" width="100px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="calendar" stroke="none" stroke-width="1">
<path
d="M35,70 L35,80 L25,80 L25,70 L35,70 Z M55,70 L55,80 L45,80 L45,70 L55,70 Z M35,55 L35,65 L25,65 L25,55 L35,55 Z M55,55 L55,65 L45,65 L45,55 L55,55 Z M75,55 L75,65 L65,65 L65,55 L75,55 Z M55,40 L55,50 L45,50 L45,40 L55,40 Z M75,40 L75,50 L65,50 L65,40 L75,40 Z"
fill="currentColor" fill-rule="nonzero" id="Combined-Shape"></path>
<path
d="M90,0 L10,0 C4.5,0 0,4.5 0,10 L0,90 C0,95.5 4.5,100 10,100 L90,100 C95.5,100 100,95.5 100,90 L100,10 C100,4.5 95.5,0 90,0 Z M10,90 L10,10 L90,10 L90,90 L10,90 Z"
fill="currentColor" fill-rule="nonzero" id="Shape"></path>
<path
d="M90,20 L10,20 C4.5,20 0,24.5 0,30 L0,90 C0,95.5 4.5,100 10,100 L90,100 C95.5,100 100,95.5 100,90 L100,30 C100,24.5 95.5,20 90,20 Z M10,90 L10,30 L90,30 L90,90 L10,90 Z"
fill="currentColor" fill-rule="nonzero" id="Shape"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 7.4L0 1.4L1.4 0L6 4.6L10.6 0L12 1.4L6 7.4Z" fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 184 B

View File

@ -0,0 +1,3 @@
<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 2.8L1.4 7.4L0 6L6 0L12 6L10.6 7.4L6 2.8Z" fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 182 B

View File

@ -0,0 +1,15 @@
<svg id="Capa_1" style="enable-background:new 0 0 464.736 464.736;" viewBox="0 0 464.736 464.736"
x="0px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg" y="0px">
<g>
<path d="M446.598,18.143c-24.183-24.184-63.393-24.191-87.592-0.008l-16.717,16.717c-8.98-8.979-23.525-8.979-32.504,0
c-8.981,8.972-8.981,23.533,0,32.505l5.416,5.419L134.613,253.377h-0.016l-62.685,62.691c-4.982,4.982-7.919,11.646-8.235,18.684
l-0.15,3.344c0,0.016,0,0.03,0,0.046l-2.529,56.704c-0.104,2.633,0.883,5.185,2.739,7.048c1.751,1.759,4.145,2.738,6.63,2.738
c0.135,0,0.269,0,0.42-0.008l30.064-1.331h0.016l18.318-0.815l8.318-0.366c9.203-0.412,17.944-4.259,24.469-10.776l240.898-240.891
l4.506,4.505c4.49,4.488,10.372,6.733,16.252,6.733c5.881,0,11.764-2.245,16.253-6.733c8.98-8.973,8.98-23.534,0-32.505
l16.716-16.718C470.782,81.544,470.782,42.334,446.598,18.143z M272.639,227.33l-84.6,15.96l137.998-138.004l34.332,34.316
L272.639,227.33z" fill="currentColor" />
<path d="M64.5,423.872c-35.617,0-64.5,9.145-64.5,20.435c0,11.284,28.883,20.428,64.5,20.428s64.486-9.143,64.486-20.428
C128.986,433.016,100.117,423.872,64.5,423.872z" fill="currentColor" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -3,11 +3,11 @@
<svg id="Layer_1" style="enable-background:new 0 0 14 14;" version="1.1" viewBox="0 0 14 14" x="0px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg" y="0px">
<style type="text/css">
.st0 {
#csv-svg .st0 {
fill: currentColor;
}
</style>
<g id="Styleguide">
<g id="csv-svg">
<g id="Export-just-icons" transform="translate(-979.000000, -252.000000)">
<g id="Group" transform="translate(979.000000, 252.000000)">
<g id="list-view">

Before

Width:  |  Height:  |  Size: 806 B

After

Width:  |  Height:  |  Size: 812 B

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="100px" version="1.1" viewBox="0 0 100 100" width="100px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="minimize" stroke="none" stroke-width="1">
<path
d="M40,60 L40,90 L30,90 L30,77 L7,100 L0,93 L23,70 L10,70 L10,60 L40,60 Z M90,60 L90,70 L77,70 L100,93 L93,100 L70,77 L70,90 L60,90 L60,60 L90,60 Z M93,0 L100,7 L77,30 L90,30 L90,40 L60,40 L60,10 L70,10 L70,23 L93,0 Z M7,0 L30,23 L30,10 L40,10 L40,40 L10,40 L10,30 L23,30 L0,7 L7,0 Z"
fill="currentColor" fill-rule="nonzero" id="Combined-Shape"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 642 B

View File

@ -0,0 +1,3 @@
<svg width="18" height="12" viewBox="0 0 18 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 12V10H11V12H7ZM3 7V5H15V7H3ZM0 2V0H18V2H0Z" fill="currentColor" />
</svg>

After

Width:  |  Height:  |  Size: 186 B

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="100px" version="1.1" viewBox="0 0 100 100" width="100px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="full-screen" stroke="none" stroke-width="1">
<path
d="M36.5,56.5 L43.5,63.5 L17,90 L30,90 L30,100 L0,100 L0,70 L10,70 L10,83 L36.5,56.5 Z M63.5,56.5 L90,83 L90,70 L100,70 L100,100 L70,100 L70,90 L83,90 L56.5,63.5 L63.5,56.5 Z M100,0 L100,30 L90,30 L90,17 L63.5,43.5 L56.5,36.5 L83,10 L70,10 L70,0 L100,0 Z M30,0 L30,10 L17,10 L43.5,36.5 L36.5,43.5 L10,17 L10,30 L0,30 L0,0 L30,0 Z"
fill="currentColor" fill-rule="nonzero" id="Combined-Shape"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 691 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="20px" version="1.1" viewBox="0 0 20 20" width="20px" xmlns="http://www.w3.org/2000/svg"
>
<g fill="none" fill-rule="evenodd" id="Help-Mode" stroke="none" stroke-width="1">
<g fill="currentColor" fill-rule="nonzero" id="01.-Help-button" transform="translate(-1408.000000, -645.000000)">
<g id="help-button" transform="translate(1294.000000, 635.000000)">
<g id="help" transform="translate(114.000000, 10.000000)">
<path
d="M10,0 C15.5,1.01033361e-15 20,4.5 20,10 C20,15.5 15.5,20 10,20 C4.5,20 3.55271368e-15,15.5 3.55271368e-15,10 C7.10542736e-15,4.5 4.5,-1.01033361e-15 10,0 Z M10,2 C5.6,2 2,5.6 2,10 C2,14.4 5.6,18 10,18 C14.4,18 18,14.4 18,10 C18,5.6 14.4,2 10,2 Z M10.86,12.9 L10.86,14.9 L8.86,14.9 L8.86,12.9 L10.86,12.9 Z M9.86,4.9 C11.56,4.9 12.86,6.2 12.86,7.9 C12.86,8.8 12.36,9.7 11.66,10.3 C11.3830769,10.4846154 10.9357396,10.839645 10.8685571,11.4437415 L10.86,11.6 L10.86,11.9 L8.86,11.9 L8.86,11.6 C8.86,10.5 9.46,9.4 10.46,8.7 C10.76,8.5 10.86,8.2 10.86,7.9 C10.86,7.3 10.46,6.9 9.86,6.9 C9.30285714,6.9 8.91816327,7.24489796 8.86604956,7.77456268 L8.86,7.9 L6.86,7.9 C6.86,6.2 8.16,4.9 9.86,4.9 Z"></path>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -3,11 +3,11 @@
<svg id="Layer_1" style="enable-background:new 0 0 14 14;" version="1.1" viewBox="0 0 14 14" x="0px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg" y="0px">
<style type="text/css">
.st0 {
#lanes-svg .st0 {
fill: currentColor;
}
</style>
<g id="Styleguide">
<g id="lanes-svg">
<g id="Export-just-icons" transform="translate(-979.000000, -294.000000)">
<g id="Group" transform="translate(979.000000, 294.000000)">
<g id="lanes-view">

Before

Width:  |  Height:  |  Size: 726 B

After

Width:  |  Height:  |  Size: 736 B

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Annotations-Jump-to-" stroke="none" stroke-width="1">
<g id="03.Jump-to-first-annotation" transform="translate(-1109.000000, -181.000000)">
<rect height="750" width="1440" x="0" y="0"></rect>
<rect height="32" id="Rectangle" width="60" x="1087" y="173"></rect>
<g fill="currentColor" fill-rule="nonzero" id="right_white"
transform="translate(1109.000000, 181.000000)">
<polygon id="Path"
points="12 6.24 10.88 5.12 8 8 7.2 7.2 5.12 5.12 4 6.24 8 10.24"
transform="translate(8.000000, 7.680000) scale(1, -1) translate(-8.000000, -7.680000) "></polygon>
<polygon id="Path"
points="12 10.4 10.88 9.28 8 12.16 7.2 11.36 5.12 9.28 4 10.4 8 14.4"
transform="translate(8.000000, 11.840000) scale(1, -1) translate(-8.000000, -11.840000) "></polygon>
<polygon id="Path"
points="14.4 1.92 1.6 1.92 1.22124533e-14 1.92 1.22124533e-14 3.52 16 3.52 16 1.92"
transform="translate(8.000000, 2.720000) scale(1, -1) translate(-8.000000, -2.720000) "></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Annotations-Jump-to-" stroke="none" stroke-width="1">
<g id="03.Jump-to-first-annotation" transform="translate(-1109.000000, -726.000000)">
<rect height="750" width="1440" x="0" y="0"></rect>
<rect height="32" id="Rectangle" width="60" x="1087" y="718"></rect>
<g fill="currentColor"
fill-rule="nonzero"
id="right_white"
transform="translate(1117.000000, 734.000000) scale(1, -1) translate(-1117.000000, -734.000000) translate(1109.000000, 726.000000)">
<polygon id="Path"
points="12 6.24 10.88 5.12 8 8 7.2 7.2 5.12 5.12 4 6.24 8 10.24"
transform="translate(8.000000, 7.680000) scale(1, -1) translate(-8.000000, -7.680000) "></polygon>
<polygon id="Path"
points="12 10.4 10.88 9.28 8 12.16 7.2 11.36 5.12 9.28 4 10.4 8 14.4"
transform="translate(8.000000, 11.840000) scale(1, -1) translate(-8.000000, -11.840000) "></polygon>
<polygon id="Path"
points="14.4 1.92 1.6 1.92 1.22124533e-14 1.92 1.22124533e-14 3.52 16 3.52 16 1.92"
transform="translate(8.000000, 2.720000) scale(1, -1) translate(-8.000000, -2.720000) "></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="14px" version="1.1" viewBox="0 0 14 14" width="14px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Annotations-Jump-to-" stroke="none" stroke-width="1">
<g id="01.Jump-to-first-last-annotation" transform="translate(-1222.000000, -486.000000)">
<rect height="750" width="1440" x="0" y="0"></rect>
<rect height="544" id="Rectangle" width="282" x="1148" y="206"></rect>
<g id="Page-05" transform="translate(1147.000000, 173.000000)"></g>
<g id="Group" transform="translate(1212.000000, 476.000000)">
<rect height="34" id="Rectangle" rx="17" width="151" x="0" y="0"></rect>
<g fill="currentColor" fill-rule="nonzero" id="collapse"
transform="translate(10.000000, 10.000000)">
<g id="Group" transform="translate(1.400000, 3.360000)">
<polygon id="Path"
points="9.1 0.98 8.12 0 5.6 2.52 4.9 1.82 3.08 0 2.1 0.98 5.6 4.48"></polygon>
<polygon id="Path"
points="9.8 5.88 1.4 5.88 7.34412531e-15 5.88 7.34412531e-15 7.28 11.2 7.28 11.2 5.88"
transform="translate(5.600000, 6.580000) scale(1, -1) translate(-5.600000, -6.580000) "></polygon>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="14px" version="1.1" viewBox="0 0 14 14" width="14px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Annotations-Jump-to-" stroke="none" stroke-width="1">
<g id="01.Jump-to-first-last-annotation" transform="translate(-1222.000000, -444.000000)">
<rect height="750" width="1440" x="0" y="0"></rect>
<rect height="544" id="Rectangle" width="282" x="1148" y="206"></rect>
<g id="Page-05" transform="translate(1147.000000, 173.000000)"></g>
<g id="Group" transform="translate(1212.000000, 434.000000)">
<rect height="34" id="Rectangle" rx="17" width="151" x="0" y="0"></rect>
<g fill="currentColor" fill-rule="nonzero" id="collapse"
transform="translate(10.000000, 10.000000)">
<g id="Group" transform="translate(1.400000, 3.360000)">
<polygon id="Path"
points="9.1 3.78 8.12 2.8 5.6 5.32 4.9 4.62 3.08 2.8 2.1 3.78 5.6 7.28"
transform="translate(5.600000, 5.040000) scale(1, -1) translate(-5.600000, -5.040000) "></polygon>
<polygon id="Path"
points="9.8 0 1.4 0 7.34412531e-15 0 7.34412531e-15 1.4 11.2 1.4 11.2 0"
transform="translate(5.600000, 0.700000) scale(1, -1) translate(-5.600000, -0.700000) "></polygon>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="14px" version="1.1" viewBox="0 0 14 14" width="14px" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Styleguide" stroke="none" stroke-width="1">
<g fill="currentColor" id="Styleguide-Actions" transform="translate(-979.000000, -630.000000)">
<g id="reference" transform="translate(969.000000, 620.000000)">
<g id="status" transform="translate(10.000000, 10.000000)">
<path
d="M1.4,9.8 L1.4,12.6 L4.2,12.6 L4.2,14 L0,14 L0,9.8 L1.4,9.8 Z M14,9.8 L14,14 L9.8,14 L9.8,12.6 L12.6,12.6 L12.6,9.8 L14,9.8 Z M4.2,0 L4.2,1.4 L1.4,1.4 L1.4,4.2 L0,4.2 L0,0 L4.2,0 Z M14,0 L14,4.2 L12.6,4.2 L12.6,1.4 L9.8,1.4 L9.8,0 L14,0 Z"
fill-rule="nonzero" id="OCR"></path>
<path d="M4.2,0 L0,0 L0,4.2 L4.2,4.2 L4.2,0 Z M2.8,1.4 L2.8,2.8 L1.4,2.8 L1.4,1.4 L2.8,1.4 Z" fill-rule="nonzero"
id="Path"></path>
<path d="M4.2,9.8 L0,9.8 L0,14 L4.2,14 L4.2,9.8 Z M2.8,11.2 L2.8,12.6 L1.4,12.6 L1.4,11.2 L2.8,11.2 Z" fill-rule="nonzero"
id="Path"></path>
<path d="M14,0 L9.8,0 L9.8,4.2 L14,4.2 L14,0 Z M12.6,1.4 L12.6,2.8 L11.2,2.8 L11.2,1.4 L12.6,1.4 Z" fill-rule="nonzero"
id="Path"></path>
<path d="M14,9.8 L9.8,9.8 L9.8,14 L14,14 L14,9.8 Z M12.6,11.2 L12.6,12.6 L11.2,12.6 L11.2,11.2 L12.6,11.2 Z" fill-rule="nonzero"
id="Path"></path>
<rect height="1.4" id="Rectangle" width="5.6" x="4.2" y="1.4"></rect>
<rect height="1.4" id="Rectangle" width="5.6" x="4.2" y="11.2"></rect>
<rect height="5.6" id="Rectangle" width="1.4" x="11.2" y="4.2"></rect>
<rect height="5.6" id="Rectangle" width="1.4" x="1.4" y="4.2"></rect>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="100px" version="1.1" viewBox="0 0 100 100" width="100px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="false_positive" stroke="none" stroke-width="1">
<path
d="M70.0540046,8 L70.0540046,13 L18.1476125,13 C13.6557132,13 9.16381388,16.5 8.16561403,21 L0.180015248,61 C-0.319084676,64 0.180015248,67 2.17641494,69.5 C4.17281464,71.5 7.16741418,73 10.1620137,73 L23.1386117,73 C18.6467124,78.5 18.1476125,86 22.1404119,91.5 C24.6359115,95.5 29.1278108,98 34.1188101,98 C38.1116095,98 42.1044088,96.5 44.5999085,93.5 L65.0630053,73 L70.0540046,73 L70.0540046,78 L100,78 L100,8 L70.0540046,8 Z M61.0702059,63 L37.6125095,86.5 C36.6143097,87.5 35.6161098,88 34.1188101,88 C32.6215103,88 31.1242105,87 30.1260107,86 C28.6287109,84 29.1278108,81 31.1242105,79 L47.0954081,63 L10.1620137,63 L18.1476125,23 L70.0540046,23 L70.0540046,63 L61.0702059,63 Z M90.0180015,68 L80.036003,68 L80.036003,18 L90.0180015,18 L90.0180015,68 Z"
fill="currentColor" fill-rule="nonzero" id="Shape"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="100px" version="1.1" viewBox="0 0 100 100" width="100px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="false_negative" stroke="none" stroke-width="1">
<path
d="M29.9459954,92 L29.9459954,87 L81.8523875,87 C86.3442868,87 90.8361861,83.5 91.834386,79 L99.8199848,39 C100.319085,36 99.8199848,33 97.8235851,30.5 C95.8271854,28.5 92.8325858,27 89.8379863,27 L76.8613883,27 C81.3532876,21.5 81.8523875,14 77.8595881,8.5 C75.3640885,4.5 70.8721892,2 65.8811899,2 C61.8883905,2 57.8955912,3.5 55.4000915,6.5 L34.9369947,27 L29.9459954,27 L29.9459954,22 L0,22 L0,92 L29.9459954,92 Z M38.9297941,37 L62.3874905,13.5 C63.3856903,12.5 64.3838902,12 65.8811899,12 C67.3784897,12 68.8757895,13 69.8739893,14 C71.3712891,16 70.8721892,19 68.8757895,21 L52.9045919,37 L89.8379863,37 L81.8523875,77 L29.9459954,77 L29.9459954,37 L38.9297941,37 Z M9.98199848,32 L19.963997,32 L19.963997,82 L9.98199848,82 L9.98199848,32 Z"
fill="currentColor" fill-rule="nonzero" id="Shape"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,5 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" fill="none" />
<path
d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" />
</svg>

After

Width:  |  Height:  |  Size: 704 B

View File

@ -0,0 +1,5 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none" />
<path
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
</svg>

After

Width:  |  Height:  |  Size: 369 B

View File

@ -43,14 +43,31 @@ body {
--iqser-pink-1: #f125de;
--iqser-helpmode-primary: green;
--iqser-font-size: 13px;
--iqser-inputs-font-size: 13px;
--iqser-button-radius: 17px;
--iqser-button-font-size: 13px;
--iqser-button-height: 34px;
--iqser-font-family: 'some placeholder value that should be overridden when configuring a theme';
--iqser-app-name-font-family: 'some placeholder value that should be overridden when configuring a theme';
--iqser-app-name-font-size: 18px;
--iqser-logo-size: 28px;
--iqser-app-name-color: black;
--iqser-top-bar-height: 61px;
--iqser-menu-margin-top: 10px;
--iqser-menu-border-radius: 8px;
--iqser-menu-padding-top: 8px;
--iqser-menu-padding-bottom: 24px;
--iqser-menu-min-height: 24px;
--iqser-menu-item-margin: 0 8px 2px 8px;
--iqser-inputs-height: 36px;
--iqser-textarea-padding-y: 7px;
--iqser-font-family: Inter, sans-serif;
--iqser-app-name-font-family: Inter, sans-serif;
--iqser-circle-button-radius: 50%;
--iqser-side-nav-item-radius: 20px;
--iqser-dot-overlay-background: rgba(var(--iqser-primary-rgb), 0.1);
--iqser-chevron-button-bg: transparent;
}
$required-variables: 'iqser-font-family' 'iqser-primary';
$required-variables: 'iqser-primary';
@mixin checkRequiredVariables($args, $theme) {
@each $var in $required-variables {

View File

@ -1,11 +1,12 @@
@use 'common-mixins';
.mat-autocomplete-panel {
.mat-mdc-autocomplete-panel {
@include common-mixins.scroll-bar;
background-color: var(--iqser-background);
border-radius: 8px;
.mat-option {
.mat-mdc-option {
font-size: inherit;
}
}

View File

@ -1,10 +1,11 @@
@use 'common-mixins';
.top-bar {
height: 61px;
height: var(--iqser-top-bar-height);
width: 100vw;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
border-bottom: 1px solid var(--iqser-separator);
box-sizing: border-box;
@ -14,24 +15,6 @@
margin-right: 50px;
}
.logo {
@include common-mixins.clear-a;
display: flex;
align-items: center;
}
.app-name {
font-family: var(--iqser-app-name-font-family);
margin-left: 10px;
height: 20px;
font-size: var(--iqser-font-size);
font-weight: 800;
letter-spacing: 0;
line-height: 20px;
white-space: nowrap;
color: var(--iqser-text);
}
.menu {
display: flex;
align-items: center;
@ -49,6 +32,7 @@
.buttons {
display: flex;
margin-right: 8px;
align-items: center;
> *:not(:last-child) {
margin-right: 14px;
@ -56,18 +40,28 @@
}
}
.logo {
@include common-mixins.clear-a;
display: flex;
align-items: center;
}
.app-name {
font-family: var(--iqser-app-name-font-family);
font-size: var(--iqser-app-name-font-size);
color: var(--iqser-app-name-color);
font-weight: normal;
white-space: nowrap;
}
.dev-mode {
background-color: var(--iqser-primary);
color: var(--iqser-white);
font-size: 22px;
line-height: 16px;
text-align: center;
position: fixed;
top: 0;
z-index: 100;
right: 0;
height: 61px;
word-break: break-all;
height: var(--iqser-top-bar-height);
writing-mode: vertical-rl;
text-orientation: upright;
display: flex;
justify-content: center;
align-items: center;

View File

@ -13,13 +13,13 @@
min-width: 16px;
}
.dropdown-breadcrumb {
.dropdown-breadcrumb .mdc-button {
font-weight: 600;
color: var(--iqser-primary);
--mdc-text-button-label-text-color: var(--iqser-primary);
}
&[aria-expanded='true'] .mat-button-wrapper > span {
color: var(--iqser-text);
}
.dropdown-breadcrumb[aria-expanded='true'] .mdc-button {
--mdc-text-button-label-text-color: var(--iqser-text);
}
}

View File

@ -1,108 +1,20 @@
.mat-button,
.mat-flat-button {
border-radius: var(--iqser-button-radius) !important;
font-size: var(--iqser-button-font-size) !important;
height: var(--iqser-button-height);
display: flex !important;
align-items: center;
.mat-button-wrapper {
display: flex;
align-items: center;
line-height: 34px;
transition: opacity 0.2s;
width: 100%;
> *:not(:last-child) {
margin-right: 6px;
}
> span {
margin: auto;
}
@mixin iconSize14 {
.mat-icon {
width: 14px;
height: 14px;
font-size: 14px;
}
}
.mat-button,
.mat-icon-button,
.mat-flat-button {
&.mat-button-disabled .mat-button-wrapper {
color: var(--iqser-text);
opacity: 0.3;
@mixin labelNoWrap {
.mdc-button__label {
white-space: nowrap;
}
}
.mat-flat-button.mat-primary,
.mat-button.primary {
padding: 0 14px;
transition: background-color 0.2s, color 0.2s;
background-color: var(--iqser-primary);
&.mat-button-disabled {
background-color: var(--iqser-primary);
.mat-button-wrapper {
color: var(--iqser-white);
opacity: 0.5;
}
}
&:not(.mat-button-disabled):hover {
background-color: var(--iqser-primary-2);
}
}
iqser-icon-button,
iqser-chevron-button,
.user-button,
iqser-circle-button {
position: relative;
display: flex;
button {
font-weight: 400 !important;
transition: background-color 0.2s;
&.overlay {
background: rgba(var(--iqser-primary-rgb), 0.1);
}
&:not(.overlay):not(.mat-button-disabled):not(.primary):not(.dark-bg):not(.warn):not(.help):hover {
background-color: var(--iqser-btn-bg);
}
&.primary {
font-weight: 500 !important;
background-color: var(--iqser-primary);
color: var(--iqser-white);
&:hover {
background-color: var(--iqser-primary-2);
}
}
&.dark-bg:hover {
background-color: var(--iqser-btn-bg-hover);
}
&.help:hover {
background-color: var(--iqser-helpmode-primary);
color: var(--iqser-grey-1);
}
}
.dot {
top: 1px;
left: 1px;
}
}
iqser-chevron-button,
iqser-circle-button,
iqser-icon-button {
@mixin ariaExpanded {
&[aria-expanded='true'] {
button {
.mat-mdc-button-base {
background: rgba(var(--iqser-primary-rgb), 0.1);
&.primary {
@ -115,3 +27,184 @@ iqser-icon-button {
}
}
}
@mixin dotOverlay {
position: relative;
.dot {
top: 1px;
left: 1px;
}
.overlay {
background: var(--iqser-dot-overlay-background);
}
}
@mixin buttonShape {
.mat-mdc-button {
--mdc-text-button-container-shape: var(--iqser-button-radius);
height: var(--iqser-button-height);
.mat-mdc-button-touch-target {
height: var(--iqser-button-height);
}
}
}
iqser-icon-button {
@include buttonShape;
@include ariaExpanded;
@include dotOverlay;
@include labelNoWrap;
display: block;
.mdc-button.mat-mdc-button {
@include iconSize14;
--mdc-text-button-label-text-color: var(--iqser-text);
padding: 0 14px;
width: 100%;
&:hover:not([disabled]) {
.mat-mdc-button-persistent-ripple::before {
background-color: #000;
opacity: 0.04;
}
}
&[disabled] {
--mdc-text-button-disabled-label-text-color: rgba(var(--iqser-text-rgb), 0.3);
}
.mat-icon {
margin-right: 6px;
}
&.has-icon {
padding: 0 14px 0 10px;
}
&.primary {
--mdc-text-button-label-text-color: var(--iqser-white);
--mdc-text-button-disabled-label-text-color: rgba(255, 255, 255, 0.5);
background: var(--iqser-primary);
font-weight: 500;
}
&.dark {
background: var(--iqser-btn-bg);
}
&.active {
background: #fcebeb;
font-weight: 600;
color: var(--iqser-primary);
cursor: default;
pointer-events: none;
}
}
}
iqser-circle-button {
@include ariaExpanded;
@include dotOverlay;
display: block;
.mat-mdc-icon-button {
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--iqser-circle-button-radius);
.mat-mdc-button-touch-target {
width: var(--circle-button-size);
height: var(--circle-button-size);
}
.mat-mdc-button-persistent-ripple {
border-radius: var(--iqser-circle-button-radius);
}
&.mat-mdc-button-base {
height: var(--circle-button-size);
width: var(--circle-button-size);
padding: 0;
}
svg {
vertical-align: bottom;
width: inherit;
height: inherit;
}
&.primary {
--mdc-icon-button-icon-color: var(--iqser-white);
&[disabled] {
--mdc-icon-button-disabled-icon-color: rgba(255, 255, 255, 0.5);
}
background: var(--iqser-primary);
}
&.dark-bg {
background: var(--iqser-btn-bg);
}
&.warn:not([disabled]) {
--mdc-icon-button-icon-color: var(--iqser-accent);
background-color: var(--iqser-warn);
}
mat-icon {
width: var(--circle-button-icon-size);
height: var(--circle-button-icon-size);
line-height: var(--circle-button-icon-size);
margin: 0 !important;
svg {
line-height: var(--circle-button-icon-size);
}
}
}
}
iqser-chevron-button {
@include buttonShape;
@include ariaExpanded;
@include labelNoWrap;
display: block;
.mat-mdc-button {
@include iconSize14;
background-color: var(--iqser-chevron-button-bg);
&:not([disabled]) {
--mdc-text-button-label-text-color: var(--iqser-text);
}
}
@include dotOverlay;
}
iqser-user-button {
@include buttonShape;
@include ariaExpanded;
@include dotOverlay;
display: block;
.mdc-button.mat-mdc-button {
--mdc-text-button-label-text-color: var(--iqser-text);
@include iconSize14;
padding: 0 10px 0 5px;
}
}

View File

@ -1,39 +1,76 @@
.mat-checkbox .mat-checkbox-frame {
border: 1px solid var(--iqser-grey-5);
$checkbox-size: 16px;
$ripple-size: 26px;
.mat-mdc-checkbox .mdc-checkbox {
flex: 0 0 $checkbox-size;
width: $checkbox-size;
height: $checkbox-size;
margin-top: 4px;
}
.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background,
.mat-checkbox-checked.mat-accent .mat-checkbox-background {
margin-top: 1px;
margin-left: 1px;
width: 18px;
height: 18px;
}
.mat-mdc-checkbox,
.mdc-checkbox {
--mdc-checkbox-state-layer-size: $checkbox-size;
--mdc-checkbox-unselected-icon-color: var(--iqser-grey-5);
--mdc-checkbox-unselected-hover-icon-color: var(--iqser-grey-5);
--mdc-checkbox-unselected-pressed-icon-color: var(--iqser-grey-5);
--mdc-checkbox-unselected-focus-icon-color: var(--iqser-grey-5);
--mdc-checkbox-disabled-selected-icon-color: var(--iqser-primary);
--mdc-checkbox-disabled-unselected-icon-color: var(--iqser-grey-5);
--mdc-checkbox-selected-focus-icon-color: var(--iqser-primary);
--mdc-checkbox-selected-hover-icon-color: var(--iqser-primary);
--mdc-checkbox-selected-icon-color: var(--iqser-primary);
--mdc-checkbox-selected-pressed-icon-color: var(--iqser-primary);
--mdc-checkbox-selected-focus-state-layer-color: var(--iqser-primary);
--mdc-checkbox-selected-hover-state-layer-color: var(--iqser-primary);
--mdc-checkbox-selected-pressed-state-layer-color: var(--iqser-primary);
.mat-checkbox-layout {
align-items: center !important;
.mdc-form-field {
align-items: start;
.mat-checkbox-inner-container {
margin-left: 0;
}
.mat-checkbox-label {
font-size: var(--iqser-font-size);
color: var(--iqser-text);
display: flex;
align-items: center;
> *:not(:last-child) {
margin-right: 8px;
& > label {
padding-left: 8px;
line-height: 24px;
white-space: normal;
}
}
.mdc-checkbox__ripple {
--mdc-checkbox-unselected-focus-state-layer-color: var(--iqser-alt-background);
width: $ripple-size;
height: $ripple-size;
left: calc(($checkbox-size - $ripple-size) / 2);
top: calc(($checkbox-size - $ripple-size) / 2);
}
.mdc-checkbox__background {
border-width: 1px;
width: $checkbox-size;
height: $checkbox-size;
top: 0 !important;
left: 0 !important;
}
.mat-mdc-checkbox-touch-target {
height: $ripple-size;
width: $ripple-size;
transform: translate(-50%, -50%);
}
}
.mat-checkbox.error .mat-checkbox-label {
color: var(--iqser-primary);
.mdc-checkbox label {
font-size: var(--iqser-font-size);
color: var(--iqser-text);
display: flex;
align-items: center;
> *:not(:last-child) {
margin-right: 8px;
}
}
.mat-checkbox-disabled {
.mat-mdc-checkbox-disabled .mdc-checkbox {
opacity: 0.5;
}

View File

@ -0,0 +1,78 @@
@use 'common-mixins';
mat-chip-listbox {
box-sizing: border-box;
height: calc(var(--height) - 40px);
overflow-x: hidden;
overflow-y: auto;
@include common-mixins.scroll-bar();
.mat-mdc-chip-set {
height: 100%;
align-items: flex-start;
flex-direction: column;
flex-wrap: nowrap;
}
}
.mat-mdc-standard-chip,
.mat-mdc-standard-chip.mat-primary.mat-mdc-chip-selected {
--mdc-chip-label-text-color: var(--iqser-text);
}
.mdc-evolution-chip-set mat-chip-option.mat-mdc-standard-chip {
background-color: var(--iqser-background);
border-radius: 4px;
margin: 0 0 2px 0;
padding: 0 12px;
transition: background-color 0.2s;
cursor: pointer;
.selected-mark {
display: none;
}
&:hover {
background-color: var(--iqser-not-disabled-table-item);
}
&.mat-mdc-chip-selected {
background-color: var(--iqser-btn-bg);
.mdc-evolution-chip__text-label {
color: #212121;
}
.selected-mark {
display: block;
}
}
}
.mdc-evolution-chip__action {
width: 100%;
}
.mdc-evolution-chip__text-label {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.mdc-evolution-chip__graphic {
display: none !important;
}
.mat-mdc-chip-list--disabled {
pointer-events: none;
color: var(--iqser-grey-5);
}
.mdc-evolution-chip-set__chips {
margin-left: 0 !important;
}
.mat-mdc-chip-focus-overlay {
display: none;
}

View File

@ -18,9 +18,18 @@
&.large {
height: 32px;
width: 32px;
min-width: 32px;
font-size: var(--iqser-font-size);
}
&.extra-small {
height: 16px;
width: 16px;
min-width: 16px;
font-size: 10px;
font-weight: lighter;
}
&.gray-dark {
background-color: var(--iqser-user-avatar-1);
color: var(--iqser-text);

View File

@ -1,9 +1,10 @@
@use 'common-mixins';
.mat-dialog-container {
.mat-mdc-dialog-container .mdc-dialog__surface {
--mdc-dialog-container-color: var(--iqser-background);
display: flex !important;
flex-direction: row;
color: var(--iqser-text);
background-color: var(--iqser-background);
padding: 0 !important;
border-radius: 8px !important;
@include common-mixins.scroll-bar;
@ -14,6 +15,10 @@
}
}
.use-backslash-n-as-line-break {
white-space: pre-line !important;
}
.dialog {
position: relative;
min-height: 80px;
@ -36,6 +41,20 @@
flex-direction: column;
flex: 1;
padding: 24px 32px 40px;
&.redaction {
.selected-text {
font-weight: bold;
padding-bottom: 8px;
}
}
&.redaction,
&.force-annotation {
iqser-details-radio {
padding-top: 20px;
}
}
}
.dialog-actions {
@ -56,3 +75,7 @@
margin-left: auto;
}
}
.large-form-dialog .dialog > form {
display: contents;
}

View File

@ -0,0 +1,59 @@
.iqser-upload-file {
.upload-area,
.file-area {
display: flex;
align-items: center;
border-radius: 8px;
width: 100%;
box-sizing: border-box;
background: var(--iqser-alt-background);
height: 68px;
&.drag-over {
background-color: var(--iqser-file-drop-drag-over);
}
}
.upload-area {
gap: 16px;
cursor: pointer;
padding: 0 32px;
mat-icon,
div {
opacity: 0.5;
transition: 0.1s;
}
div {
font-size: 16px;
font-weight: 500;
}
}
.file-area {
gap: 10px;
mat-icon:first-child {
opacity: 0.5;
margin-left: 16px;
}
mat-icon:last-child {
margin-left: auto;
margin-right: 16px;
cursor: pointer;
}
mat-icon {
transform: scale(0.7);
}
p {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
max-width: 490px;
}
}
}

View File

@ -7,11 +7,11 @@
.full-page-section {
opacity: 0.7;
background: var(--iqser-background);
z-index: 900;
z-index: 1001;
}
.full-page-content {
z-index: 1000;
z-index: 1002;
justify-content: center;
align-items: center;
flex-direction: column;

View File

@ -9,4 +9,9 @@
background: rgba(92, 229, 148, 0.5);
box-shadow: 0 0 0 2px var(--iqser-helpmode-primary) inset;
cursor: help;
&.documine-theme {
background: rgba(253, 189, 0, 0.5);
box-shadow: 0 0 0 2px var(--iqser-yellow-2) inset;
}
}

View File

@ -1,9 +1,20 @@
@use 'common-mixins' as mixins;
form .iqser-input-group:not(first-of-type) {
form .iqser-input-group:not(first-of-type),
iqser-dynamic-input:not(first-of-type) {
margin-top: 14px;
}
iqser-dynamic-input {
display: flex;
}
.mat-mdc-text-field-wrapper.mdc-text-field--outlined .mat-mdc-form-field-infix {
padding-top: 0;
padding-bottom: 0;
min-height: var(--iqser-inputs-height);
}
.iqser-input-group {
display: flex;
flex-direction: column;
@ -48,25 +59,6 @@ form .iqser-input-group:not(first-of-type) {
}
}
.mat-form-field-underline {
display: none;
}
.mat-form-field-wrapper,
.mat-form-field-infix {
padding-bottom: 0;
}
.mat-form-field-label {
opacity: 0.7 !important;
color: var(--iqser-text) !important;
transform: translateY(-1.34em) !important;
}
.mat-form-field-required-marker {
display: none;
}
&:first-child {
margin-top: 0;
}
@ -75,8 +67,8 @@ form .iqser-input-group:not(first-of-type) {
width: 14px;
height: 14px;
position: absolute;
top: 10px;
right: 10px;
top: calc((var(--iqser-inputs-height) - 14px) / 2 - 1px);
right: calc((var(--iqser-inputs-height) - 14px) / 2 - 1px);
}
.slider-row {
@ -91,27 +83,84 @@ form .iqser-input-group:not(first-of-type) {
color: var(--iqser-white);
}
input,
textarea,
mat-select {
.mat-mdc-text-field-wrapper {
padding: 0;
}
.mdc-text-field {
background: var(--iqser-background);
}
input:not([type='checkbox']),
textarea {
box-sizing: border-box;
padding-left: 11px;
padding-right: 11px;
min-height: var(--iqser-inputs-height);
line-height: 32px;
padding-left: calc((var(--iqser-inputs-height) - 14px) / 2);
padding-right: calc((var(--iqser-inputs-height) - 14px) / 2);
}
.mat-mdc-form-field-subscript-wrapper {
display: none;
}
.mdc-text-field--outlined {
--mdc-outlined-text-field-focus-outline-width: 1px;
--mdc-shape-small: 8px;
--mdc-outlined-text-field-container-shape: 8px;
border-bottom-left-radius: var(--mdc-shape-small);
border-bottom-right-radius: var(--mdc-shape-small);
}
.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused) {
.mdc-notched-outline__leading,
.mdc-notched-outline__trailing {
border-color: var(--iqser-inputs-outline);
}
&:hover {
.mdc-notched-outline__leading,
.mdc-notched-outline__trailing {
border-color: var(--iqser-inputs-outline);
}
}
}
.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused {
.mdc-notched-outline__leading,
.mdc-notched-outline__trailing {
border-color: var(--iqser-text);
}
&:hover {
.mdc-notched-outline__leading,
.mdc-notched-outline__trailing {
border-color: var(--iqser-text);
}
}
}
.mdc-text-field--focused .mdc-notched-outline__leading,
.mdc-text-field--focused .mdc-notched-outline__notch,
.mdc-text-field--focused .mdc-notched-outline__trailing {
border-width: 1px;
}
input,
textarea {
border: 1px solid var(--iqser-inputs-outline);
font-family: var(--iqser-font-family);
font-size: var(--iqser-font-size);
font-size: var(--iqser-inputs-font-size);
background-color: var(--iqser-background);
color: var(--iqser-text);
border-radius: 8px;
outline: none;
margin-top: 3px;
min-height: 36px;
&.with-icon {
padding-right: 34px;
}
&:focus:not(:disabled):not(.mat-select-disabled) {
&:focus:not(:disabled) {
border-color: var(--iqser-text);
}
@ -127,16 +176,17 @@ form .iqser-input-group:not(first-of-type) {
border-color: var(--iqser-primary);
}
}
}
&:disabled,
&.mat-select-disabled {
background-color: var(--iqser-alt-background);
color: rgba(var(--iqser-text-rgb), 0.3);
}
input:disabled,
textarea:disabled,
.mdc-text-field--disabled {
background-color: var(--iqser-alt-background);
color: rgba(var(--iqser-text-rgb), 0.3);
}
textarea {
line-height: 18px;
line-height: calc(var(--iqser-inputs-font-size) * 1.4);
}
.hex-color-input {
@ -144,30 +194,10 @@ form .iqser-input-group:not(first-of-type) {
max-width: 150px;
}
mat-select,
.mat-select.mat-select-disabled {
.mat-select-trigger {
height: 32px;
}
.mat-select-placeholder {
color: rgba(var(--iqser-text-rgb), 0.4);
}
.mat-select-value {
vertical-align: middle;
color: var(--iqser-text);
}
.mat-select-arrow {
color: rgba(var(--iqser-text-rgb), 0.7);
}
}
textarea {
resize: vertical;
padding-top: 7px;
padding-bottom: 7px;
padding-top: var(--iqser-textarea-padding-y);
padding-bottom: var(--iqser-textarea-padding-y);
@include mixins.scroll-bar;
&.has-scrollbar {
@ -176,12 +206,12 @@ form .iqser-input-group:not(first-of-type) {
}
}
label:not(.mat-slide-toggle-label):not(.mat-radio-label):not(.details-radio-label) {
> label:not(.mat-slide-toggle-label):not(.mat-radio-label):not(.details-radio-label) {
opacity: 0.7;
font-size: 11px;
letter-spacing: 0;
line-height: 14px;
margin-bottom: 2px;
margin-bottom: 5px;
color: var(--iqser-text);
&.mat-checkbox-layout {
@ -192,7 +222,7 @@ form .iqser-input-group:not(first-of-type) {
&.required label:after {
content: ' *';
color: var(--iqser-primary);
color: var(--iqser-red-1);
}
&.datepicker-wrapper {
@ -209,7 +239,7 @@ form .iqser-input-group:not(first-of-type) {
.mat-datepicker-toggle {
position: absolute;
right: 0;
bottom: 1px;
bottom: -4px;
color: var(--iqser-accent);
&.mat-datepicker-toggle-active {
@ -226,6 +256,18 @@ form .iqser-input-group:not(first-of-type) {
width: 14px;
}
button.cdk-focused,
button:hover {
span {
&::before {
width: 32px;
height: 32px;
top: 10px;
left: 8px;
}
}
}
.mat-mdc-icon-button svg {
width: unset;
height: unset;

View File

@ -86,17 +86,13 @@ section.settings {
}
.fullscreen {
.page-header {
position: absolute;
top: 0;
}
.content-inner {
height: calc(100% - 50px);
}
.overlay-shadow {
top: 50px;
.right-container {
transform: translateY(61px);
height: calc(100% - 61px);
}
}
@ -106,7 +102,7 @@ section.settings {
right: 0;
bottom: 0;
width: 100%;
height: calc(100vh - 111px);
height: calc(100vh - var(--iqser-top-bar-height) - 50px);
transition: height ease-in-out 0.2s;
}
@ -149,7 +145,6 @@ section.settings {
justify-content: center;
align-items: center;
// TODO: Shouldn't use `redaction-` here
iqser-initials-avatar .username {
display: none;
}
@ -161,11 +156,15 @@ section.settings {
box-sizing: border-box;
background: var(--iqser-background);
overflow: hidden;
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
&.with-transition {
transition:
width ease-in-out 0.2s,
min-width ease-in-out 0.2s;
}
@include common-mixins.scroll-bar;
&:hover {
overflow-y: auto;
@include common-mixins.scroll-bar;
}
.collapsed-wrapper {
@ -214,6 +213,11 @@ section.settings {
display: flex !important;
}
.flex-column {
@extend .flex;
flex-direction: column;
}
.flex-end {
display: flex;
justify-content: flex-end;
@ -252,12 +256,12 @@ section.settings {
cursor: pointer;
}
.fit-content {
width: fit-content;
.cursor-default {
cursor: default;
}
.d-flex {
display: flex;
.fit-content {
width: fit-content;
}
.cdk-overlay-container {

View File

@ -7,22 +7,33 @@
0%,
20% {
color: rgba(var(--iqser-accent-rgb), 0);
text-shadow: 0.25em 0 0 rgba(var(--iqser-accent-rgb), 0), 0.5em 0 0 rgba(var(--iqser-accent-rgb), 0);
text-shadow:
0.25em 0 0 rgba(var(--iqser-accent-rgb), 0),
0.5em 0 0 rgba(var(--iqser-accent-rgb), 0);
}
40% {
color: #283241;
text-shadow: 0.25em 0 0 rgba(var(--iqser-accent-rgb), 0), 0.5em 0 0 rgba(var(--iqser-accent-rgb), 0);
color: var(--iqser-accent);
text-shadow:
0.25em 0 0 rgba(var(--iqser-accent-rgb), 0),
0.5em 0 0 rgba(var(--iqser-accent-rgb), 0);
}
60% {
text-shadow: 0.25em 0 0 #283241, 0.5em 0 0 rgba(var(--iqser-accent-rgb), 0);
text-shadow:
0.25em 0 0 var(--iqser-accent),
0.5em 0 0 rgba(var(--iqser-accent-rgb), 0);
}
80%,
100% {
text-shadow: 0.25em 0 0 #283241, 0.5em 0 0 #283241;
text-shadow:
0.25em 0 0 var(--iqser-accent),
0.5em 0 0 var(--iqser-accent);
}
}
.spinning-icon {
display: flex;
align-items: center;
justify-content: center;
animation-name: spin;
animation-duration: 5000ms;
animation-iteration-count: infinite;

View File

@ -1,32 +1,32 @@
@use 'common-mixins';
.mat-menu-panel {
border-radius: 8px !important;
.mat-mdc-menu-panel {
border-radius: var(--iqser-menu-border-radius) !important;
max-width: none !important;
min-width: 180px !important;
margin-top: 10px;
background-color: var(--iqser-popup-background);
margin-top: var(--iqser-menu-margin-top);
min-height: unset !important;
@include common-mixins.scroll-bar;
@include common-mixins.drop-shadow;
.mat-menu-content:not(:empty) {
padding-top: 8px;
padding-bottom: 24px;
.mat-mdc-menu-content:not(:empty) {
padding-top: var(--iqser-menu-padding-top);
padding-bottom: var(--iqser-menu-padding-bottom);
}
&.padding-bottom-0 .mat-menu-content:not(:empty) {
&.padding-bottom-0 .mat-mdc-menu-content:not(:empty) {
padding-bottom: 0;
}
&.padding-bottom-8 .mat-menu-content:not(:empty) {
&.padding-bottom-8 .mat-mdc-menu-content:not(:empty) {
padding-bottom: 8px;
}
.mat-menu-item {
.mat-mdc-menu-item {
font-size: var(--iqser-font-size);
color: var(--iqser-text);
padding: 0 26px 0 8px;
margin: 0 8px 2px 8px;
padding: 0 26px 0 8px !important;
margin: var(--iqser-menu-item-margin);
border-radius: 4px;
width: -webkit-fill-available;
@ -35,6 +35,7 @@
mat-icon {
color: var(--iqser-text);
width: 14px;
}
.dot {
@ -57,7 +58,7 @@
}
&.padding-left {
padding-left: 56px;
padding-left: 56px !important;
}
&:last-of-type {
@ -76,13 +77,17 @@
}
&.active {
font-weight: 600;
> span {
font-weight: 600;
}
padding-right: 8px;
justify-content: space-between;
.checkmark {
display: flex;
margin-left: 16px;
order: 1;
}
}
}

View File

@ -1,20 +1,18 @@
.mat-progress-bar {
height: 6px;
.mat-mdc-progress-bar {
--mdc-linear-progress-track-height: 6px;
--mdc-linear-progress-active-indicator-color: var(--iqser-white);
border-radius: 3px;
.mat-progress-bar-buffer {
.mdc-linear-progress__buffer-bar {
background-color: var(--iqser-grey-5);
}
&.white {
.mat-progress-bar-fill::after {
background-color: var(--iqser-white);
}
&.primary {
--mdc-linear-progress-active-indicator-color: var(--iqser-primary);
}
&.green {
.mat-progress-bar-fill::after {
background-color: var(--iqser-green-2);
}
--mdc-linear-progress-active-indicator-color: var(--iqser-green-2);
}
}

View File

@ -0,0 +1,3 @@
.mat-mdc-progress-spinner {
--mdc-circular-progress-active-indicator-color: var(--iqser-primary);
}

View File

@ -1,8 +0,0 @@
.mat-radio-group {
display: flex;
flex-direction: column;
}
.mat-radio-button:not(:last-child) {
margin-bottom: 8px;
}

View File

@ -1,42 +1,59 @@
@use 'common-mixins';
.mat-select-panel {
.mat-mdc-select {
padding: 0 calc(var(--iqser-inputs-height) - 25px);
box-sizing: border-box;
--mat-select-trigger-text-line-height: var(--iqser-inputs-height);
--mat-select-trigger-text-size: var(--iqser-inputs-font-size);
}
.mat-mdc-select-panel {
padding: 7px 0 !important;
min-width: fit-content;
background-color: var(--iqser-background);
@include common-mixins.scroll-bar;
@include common-mixins.drop-shadow;
--mat-option-selected-state-label-text-color: var(--iqser-primary);
}
.mat-option {
margin: 1px 8px;
border-radius: 4px;
color: var(--iqser-text);
.mat-mdc-select-arrow-wrapper {
margin-left: 10px;
}
&:hover:not(.mat-option-disabled),
&:focus:not(.mat-option-disabled) {
background-color: var(--iqser-btn-bg);
}
.mat-mdc-option {
margin: 1px 8px;
border-radius: 4px;
color: var(--iqser-text);
&.mat-selected:not(.mat-option-multiple) {
background-color: rgba(var(--iqser-primary-rgb), 0.2);
}
&:hover:not(.mdc-list-item--disabled, .mdc-list-item--selected),
&:focus:not(.mdc-list-item--disabled, .mdc-list-item--selected) {
background-color: var(--iqser-btn-bg);
}
&.mat-option-disabled {
color: rgba(var(--iqser-text-rgb), 0.7);
}
&.mdc-list-item--selected {
background-color: rgba(var(--iqser-primary-rgb), 0.2) !important;
color: var(--iqser-primary);
}
&.mdc-list-item--disabled {
color: rgba(var(--iqser-text-rgb), 0.7);
}
> span {
width: 100%;
}
}
.mat-form-field.no-label .mat-form-field-infix {
padding: 0 !important;
border-top: 0 !important;
.mat-mdc-select-value {
color: var(--iqser-text);
}
mat-select {
.mat-select-value,
.mat-select-arrow {
color: var(--iqser-text);
}
.mat-select-arrow {
margin-left: 6px;
.mat-form-field-disabled {
.mat-mdc-select-value {
color: var(--iqser-grey-3);
}
}
.mat-mdc-option:not(.mat-mdc-option-multiple) .mat-mdc-option-pseudo-checkbox {
display: none;
}

View File

@ -19,7 +19,7 @@ iqser-side-nav {
.item {
margin-bottom: 4px;
border-radius: 20px;
border-radius: var(--iqser-side-nav-item-radius);
padding: 9px 16px;
cursor: pointer;
transition: background-color 0.2s;

View File

@ -1,48 +1,26 @@
.mat-slider-horizontal {
width: 140px;
height: 32px !important;
.mat-mdc-slider .mdc-slider__track .mdc-slider__track--inactive {
--mdc-slider-inactive-track-color: var(--iqser-toggle-bg);
opacity: 1;
}
.mat-slider-wrapper {
left: 0 !important;
top: 16px !important;
}
.mat-mdc-slider.mdc-slider {
margin-left: 0;
margin-right: 0;
.mat-slider-track-wrapper,
.mat-slider-track-fill {
height: 6px !important;
border-radius: 3px;
}
--mdc-slider-handle-width: 16px;
--mdc-slider-handle-height: 16px;
--mdc-slider-handle-elevation: none;
--mdc-slider-disabled-handle-color: var(--iqser-primary);
--mdc-slider-disabled-active-track-color: var(--iqser-grey-5);
--mdc-slider-disabled-inactive-track-color: var(--iqser-grey-7);
--mdc-slider-label-container-color: var(--iqser-accent);
--mat-mdc-slider-value-indicator-opacity: 1;
// For disabled state
.mat-slider-track-fill {
background-color: var(--iqser-primary);
}
.mat-slider-track-background {
height: 4px !important;
margin-top: 1px;
border-radius: 3px;
background-color: var(--iqser-toggle-bg) !important;
}
.mat-slider-focus-ring {
display: none;
&.mdc-slider--disabled {
opacity: 1;
}
}
.mat-slider-thumb {
width: 16px !important;
height: 16px !important;
border-width: 0 !important;
background-color: var(--iqser-primary) !important;
}
.mat-slider:not(.mat-slider-disabled):not(.mat-slider-sliding) .mat-slider-thumb {
transform: scale(1) !important;
}
.mat-slider-horizontal.mat-slider-disabled {
.mat-slider-thumb {
transform: translateX(-3px);
}
.mdc-slider__value-indicator-text {
white-space: nowrap;
}

View File

@ -1,3 +1,5 @@
@use 'ngx-toastr/toastr';
@use 'common-utilities';
@use 'common-inputs';
@use 'common-buttons';
@ -12,12 +14,14 @@
@use 'common-breadcrumbs';
@use 'common-autocomplete';
@use 'common-checkbox';
@use 'common-chips';
@use 'common-controls';
@use 'common-help-mode';
@use 'common-list';
@use 'common-loading';
@use 'common-media-queries';
@use 'common-progress-bar';
@use 'common-progress-spinner';
@use 'common-select';
@use 'common-slider';
@use 'common-tabs';
@ -26,7 +30,7 @@
@use 'common-toggle-button';
@use 'common-tooltips';
@use 'common-file-drop';
@use 'common-file-upload';
@use 'common-side-nav';
@use 'common-radio';
@use 'common-color-picker';
@use 'common-skeleton';

View File

@ -8,10 +8,7 @@
flex: 1;
align-items: center;
justify-content: flex-end;
> *:not(:last-child) {
margin-right: 10px;
}
gap: 10px;
}
.header-item {
@ -23,6 +20,7 @@
border-bottom: 1px solid var(--iqser-separator);
box-sizing: border-box;
padding: 0 24px;
gap: 10px;
.header-title {
display: flex;
@ -34,17 +32,10 @@
padding: 0 24px 0 10px;
}
> *:not(:last-child) {
margin-right: 10px;
}
.actions {
display: flex;
align-items: center;
justify-content: flex-end;
> *:not(:last-child) {
margin-right: 16px;
}
gap: 16px;
}
}

View File

@ -1,5 +1,10 @@
@use 'common-mixins' as mixins;
* {
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
}
.text-muted {
opacity: 0.7;
}
@ -73,6 +78,12 @@ pre {
line-height: 24px;
}
.heading-md {
font-size: 18px;
font-weight: 600;
line-height: 22px;
}
.heading {
font-size: 16px;
line-height: 20px;
@ -97,7 +108,10 @@ pre {
font-size: 11px;
line-height: 14px;
font-weight: initial;
@extend .text-muted;
&:not(.full-opacity) {
@extend .text-muted;
}
}
.link-action {
@ -135,6 +149,10 @@ pre {
@include mixins.line-clamp(4);
}
.clamp-5 {
@include mixins.line-clamp(5);
}
.no-wrap {
white-space: nowrap;
}

View File

@ -84,11 +84,11 @@ $toast-width: 400px;
}
.toast-success {
background-color: var(--iqser-green-2);
background-color: #5ce594;
}
.toast-error {
background-color: var(--iqser-red-1);
background-color: #dd4d50;
color: var(--iqser-white);
}

View File

@ -1,41 +1,59 @@
.mat-slide-toggle {
.mat-slide-toggle-bar {
height: 16px !important;
width: 30px !important;
border-radius: 16px !important;
background-color: var(--iqser-toggle-bg);
.mat-mdc-slide-toggle {
.mdc-switch {
--mdc-switch-handle-elevation: none;
--mdc-switch-handle-elevation-shadow: none;
--mdc-switch-selected-track-color: var(--iqser-primary);
--mdc-switch-selected-hover-track-color: var(--iqser-primary);
--mdc-switch-selected-pressed-track-color: var(--iqser-primary);
--mdc-switch-selected-focus-track-color: var(--iqser-primary);
--mdc-switch-disabled-selected-track-color: var(--iqser-primary);
--mdc-switch-unselected-track-color: var(--iqser-toggle-bg);
--mdc-switch-unselected-hover-track-color: var(--iqser-toggle-bg);
--mdc-switch-unselected-pressed-track-color: var(--iqser-toggle-bg);
--mdc-switch-unselected-focus-track-color: var(--iqser-toggle-bg);
--mdc-switch-disabled-unselected-track-color: var(--iqser-toggle-bg);
--mdc-switch-selected-handle-color: var(--iqser-background);
--mdc-switch-selected-pressed-handle-color: var(--iqser-background);
--mdc-switch-selected-hover-handle-color: var(--iqser-background);
--mdc-switch-selected-focus-handle-color: var(--iqser-background);
--mdc-switch-disabled-selected-handle-color: var(--iqser-background);
--mdc-switch-unselected-handle-color: var(--iqser-alt-background);
--mdc-switch-unselected-pressed-handle-color: var(--iqser-alt-background);
--mdc-switch-unselected-hover-handle-color: var(--iqser-alt-background);
--mdc-switch-unselected-focus-handle-color: var(--iqser-alt-background);
--mdc-switch-disabled-unselected-handle-color: var(--iqser-alt-background);
--mdc-switch-disabled-track-opacity: 0.38;
--mdc-switch-track-width: 30px;
--mdc-switch-track-height: 16px;
--mdc-switch-track-shape: 8px;
--mat-switch-with-icon-handle-size: 12px;
--mdc-switch-handle-shape: 6px;
--mat-switch-unselected-with-icon-handle-horizontal-margin: 0 2px;
--mat-switch-unselected-pressed-handle-horizontal-margin: 0 2px;
--mat-switch-selected-with-icon-handle-horizontal-margin: 0 6px;
--mat-switch-selected-pressed-handle-horizontal-margin: 0 6px;
--mat-switch-selected-handle-horizontal-margin: 0 6px;
--mat-switch-unselected-handle-size: 12px;
--mat-switch-selected-handle-size: 12px;
--mat-switch-pressed-handle-size: 12px;
}
.mat-slide-toggle-thumb-container {
top: 2px !important;
left: 2px !important;
height: 12px !important;
width: 12px !important;
.mdc-form-field > label {
margin-left: 8px;
padding-left: 0;
}
.mat-slide-toggle-thumb {
height: 12px !important;
width: 12px !important;
box-shadow: none;
background-color: var(--iqser-alt-background);
}
.mat-ripple {
.mdc-switch__icons,
.mdc-switch__ripple {
display: none;
}
&.mat-primary.mat-checked {
.mat-slide-toggle-bar {
background-color: var(--iqser-primary);
border: 1px solid var(--iqser-background);
}
.mat-slide-toggle-thumb {
background-color: var(--iqser-background);
}
.mat-slide-toggle-thumb-container {
transform: translate3d(14px, 0, 0);
}
}
}

View File

@ -1,29 +1,34 @@
.mat-tooltip {
background-color: var(--iqser-accent);
border-radius: 3px !important;
padding: 10px;
margin: 12px !important;
font-size: 11px;
line-height: 14px;
color: var(--iqser-white) !important;
position: relative;
overflow: visible !important;
text-align: center;
.mdc-tooltip {
.mdc-tooltip__surface {
--mdc-plain-tooltip-supporting-text-tracking: 0;
&.multiline {
white-space: pre-line;
text-align: left;
background-color: var(--iqser-accent);
border-radius: 3px !important;
padding: 10px;
margin: 12px !important;
font-size: 11px;
line-height: 14px;
color: var(--iqser-white) !important;
position: relative;
overflow: visible !important;
text-align: center !important;
max-width: unset;
}
&.break-all {
word-break: break-all;
}
&.small {
&.small .mdc-tooltip__surface {
max-width: 160px;
}
&.warn {
&.multiline .mdc-tooltip__surface {
white-space: pre-line;
text-align: left !important;
}
&.break-all .mdc-tooltip__surface {
word-break: break-all;
}
&.warn .mdc-tooltip__surface {
background-color: var(--iqser-yellow-2);
color: var(--iqser-accent) !important;
}

View File

@ -1,3 +1,5 @@
@use 'sass:string';
@use 'sass:list';
/* Margins, paddings */
$start: 0;
@ -7,19 +9,19 @@ $values: '';
$sides: (top, bottom, left, right);
@for $i from $start + 1 through $end {
$values: append($values, $i, comma);
$values: set-nth($values, 1, $start);
$values: list.append($values, $i, comma);
$values: list.set-nth($values, 1, $start);
}
// TODO: Check if !important can be avoided
@each $space in $values {
@each $side in $sides {
.m#{str-slice($side, 0, 1)}-#{$space} {
.m#{string.slice($side, 0, 1)}-#{$space} {
margin-#{$side}: #{$space}px !important;
}
.p#{str-slice($side, 0, 1)}-#{$space} {
.p#{string.slice($side, 0, 1)}-#{$space} {
padding-#{$side}: #{$space}px !important;
}
}

View File

@ -1,24 +1,15 @@
export * from './lib/common-ui.module';
export * from './lib/buttons';
export * from './lib/dialog';
export * from './lib/form';
export * from './lib/listing';
export * from './lib/filtering';
export * from './lib/help-mode';
export * from './lib/icons';
export * from './lib/inputs';
export * from './lib/utils';
export * from './lib/sorting';
export * from './lib/services';
export * from './lib/shared';
export * from './lib/loading';
export * from './lib/error';
export * from './lib/search';
export * from './lib/upload-file';
export * from './lib/empty-states';
export * from './lib/scrollbar';
export * from './lib/caching';
export * from './lib/users';
export * from './lib/translations';
export * from './lib/standalone';
export * from './lib/pipes';
export * from './lib/permissions';
export * from './lib/directives';

View File

@ -1,19 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { ChevronButtonComponent } from './chevron-button/chevron-button.component';
import { CircleButtonComponent } from './circle-button/circle-button.component';
import { IconButtonComponent } from './icon-button/icon-button.component';
import { IqserIconsModule } from '../icons';
const matModules = [MatButtonModule, MatTooltipModule];
const components = [ChevronButtonComponent, CircleButtonComponent, IconButtonComponent];
@NgModule({
declarations: [...components],
imports: [CommonModule, IqserIconsModule, TranslateModule, ...matModules],
exports: [...components],
})
export class IqserButtonsModule {}

View File

@ -1,6 +1,8 @@
<button [class.overlay]="showDot" [class.primary]="primary" [disabled]="disabled" mat-button>
<span>{{ label }}</span>
<mat-icon class="chevron-icon" svgIcon="iqser:arrow-down"></mat-icon>
<button [class.overlay]="showDot()" [class.primary]="primary()" [disabled]="disabled()" [id]="buttonId()" mat-button>
<span>{{ label() }}</span>
<mat-icon class="chevron-icon" iconPositionEnd svgIcon="iqser:arrow-down"></mat-icon>
</button>
<div *ngIf="showDot" class="dot"></div>
@if (showDot()) {
<div class="dot"></div>
}

View File

@ -1,5 +1,5 @@
button {
padding: 0 10px 0 14px;
--mat-text-button-with-icon-horizontal-padding: 10px 0 14px;
mat-icon {
width: 14px;

View File

@ -1,14 +1,19 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { booleanAttribute, ChangeDetectionStrategy, Component, input } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { randomString } from '../../utils';
@Component({
selector: 'iqser-chevron-button [label]',
selector: 'iqser-chevron-button',
templateUrl: './chevron-button.component.html',
styleUrls: ['./chevron-button.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [MatIconModule, MatButtonModule],
})
export class ChevronButtonComponent {
@Input() label!: string;
@Input() showDot = false;
@Input() primary = false;
@Input() disabled = false;
readonly label = input.required<string>();
readonly showDot = input(false, { transform: booleanAttribute });
readonly primary = input(false, { transform: booleanAttribute });
readonly disabled = input(false, { transform: booleanAttribute });
readonly buttonId = input(`${randomString()}-chevron-button`);
}

View File

@ -0,0 +1 @@
export * from './chevron-button.component';

View File

@ -1,18 +1,25 @@
<div [matTooltipClass]="tooltipClass" [matTooltipPosition]="tooltipPosition" [matTooltip]="tooltip">
<div [matTooltipClass]="tooltipClass()" [matTooltipPosition]="tooltipPosition()" [matTooltip]="tooltip()">
<button
(click)="performAction($event)"
[class.dark-bg]="type === circleButtonTypes.dark"
[class.overlay]="showDot"
[class.primary]="type === circleButtonTypes.primary"
[class.warn]="type === circleButtonTypes.warn"
[class.help]="type === circleButtonTypes.help"
[class.grey-selected]="greySelected"
[disabled]="disabled"
[type]="isSubmit ? 'submit' : 'button'"
[id]="id ? id : buttonId"
[class.dark-bg]="type() === _circleButtonTypes.dark"
[class.grey-selected]="greySelected()"
[class.overlay]="showDot()"
[class.primary]="type() === _circleButtonTypes.primary"
[class.warn]="type() === _circleButtonTypes.warn"
[disabled]="disabled()"
[id]="buttonId()"
[iqserStopPropagation]="action.observed && !_hasRouterLink"
[type]="isSubmit() ? 'submit' : 'button'"
mat-icon-button
>
<mat-icon [svgIcon]="icon"></mat-icon>
<mat-icon [svgIcon]="icon()"></mat-icon>
</button>
<div *ngIf="showDot" class="dot"></div>
@if (showDot()) {
<div class="dot"></div>
}
@if (dropdownButton()) {
<div [class.disabled]="disabled()" class="arrow-down"></div>
}
</div>

View File

@ -1,47 +1,16 @@
:host {
height: var(--size);
width: var(--size);
align-items: center;
:host > div {
width: var(--circle-button-size);
height: var(--circle-button-size);
button {
height: var(--size);
width: var(--size);
line-height: var(--size);
.arrow-down {
border: 5px solid transparent;
border-top-color: black;
position: fixed;
margin-left: 12px;
margin-top: -8px;
mat-icon {
width: var(--iconSize);
height: var(--iconSize);
line-height: var(--iconSize);
margin: 0;
svg {
line-height: var(--iconSize);
}
&.disabled {
border-top-color: var(--iqser-grey-3);
}
&.mat-button-disabled {
cursor: not-allowed;
&:not(.primary):not(.warn):not(.dark-bg):hover {
background-color: var(--iqser-btn-bg);
}
}
&.primary.mat-button-disabled {
color: #ffffff80;
}
&.warn:not([disabled]) {
background-color: var(--iqser-warn);
color: var(--iqser-accent);
&:hover {
background-color: var(--iqser-warn);
}
}
}
&.primary mat-icon {
color: var(--iqser-primary);
}
}

View File

@ -1,53 +1,62 @@
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { MatLegacyTooltip as MatTooltip } from '@angular/material/legacy-tooltip';
import {
booleanAttribute,
ChangeDetectionStrategy,
Component,
effect,
ElementRef,
EventEmitter,
inject,
input,
numberAttribute,
Output,
viewChild,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip';
import { RouterLink } from '@angular/router';
import { StopPropagationDirective } from '../../directives';
import { IqserTooltipPosition, IqserTooltipPositions, randomString } from '../../utils';
import { CircleButtonType, CircleButtonTypes } from '../types/circle-button.type';
import { IqserTooltipPosition, IqserTooltipPositions } from '../../utils';
@Component({
selector: 'iqser-circle-button [icon]',
selector: 'iqser-circle-button',
templateUrl: './circle-button.component.html',
styleUrls: ['./circle-button.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [MatTooltipModule, MatIconModule, MatButtonModule, StopPropagationDirective],
})
export class CircleButtonComponent implements OnInit {
readonly circleButtonTypes = CircleButtonTypes;
@Input() id!: string;
@Input() icon!: string;
@Input() tooltip?: string;
@Input() tooltipClass?: string;
@Input() showDot = false;
@Input() tooltipPosition: IqserTooltipPosition = IqserTooltipPositions.above;
@Input() disabled = false;
@Input() type: CircleButtonType = CircleButtonTypes.default;
@Input() greySelected = false;
@Input() helpModeButton = false;
@Input() removeTooltip = false;
@Input() isSubmit = false;
@Input() size = 34;
@Input() iconSize = 14;
export class CircleButtonComponent {
readonly #elementRef = inject(ElementRef<HTMLElement>);
protected readonly _matTooltip = viewChild.required(MatTooltip);
protected readonly _circleButtonTypes = CircleButtonTypes;
protected readonly _hasRouterLink = !!inject(RouterLink, { optional: true, host: true });
readonly buttonId = input(`${randomString()}-circle-button`);
readonly icon = input.required<string>();
readonly tooltip = input('');
readonly tooltipClass = input('');
readonly showDot = input(false, { transform: booleanAttribute });
readonly tooltipPosition = input<IqserTooltipPosition>(IqserTooltipPositions.above);
readonly disabled = input(false, { transform: booleanAttribute });
readonly type = input<CircleButtonType>(CircleButtonTypes.default);
readonly greySelected = input(false, { transform: booleanAttribute });
readonly removeTooltip = input(false, { transform: booleanAttribute });
readonly isSubmit = input(false, { transform: booleanAttribute });
readonly dropdownButton = input(false, { transform: booleanAttribute });
readonly size = input(34, { transform: numberAttribute });
readonly iconSize = input(14, { transform: numberAttribute });
@Output() readonly action = new EventEmitter<MouseEvent>();
@ViewChild(MatTooltip) private readonly _matTooltip!: MatTooltip;
constructor(private readonly _elementRef: ElementRef) {}
get buttonId(): string {
return `${Math.random().toString(36).substring(2, 9)}-button`;
constructor() {
effect(() => {
this.#elementRef.nativeElement.style.setProperty('--circle-button-size', `${this.size()}px`);
this.#elementRef.nativeElement.style.setProperty('--circle-button-icon-size', `${this.iconSize()}px`);
});
}
ngOnInit(): void {
(this._elementRef.nativeElement as HTMLElement).style.setProperty('--size', `${this.size}px`);
(this._elementRef.nativeElement as HTMLElement).style.setProperty('--iconSize', `${this.iconSize}px`);
}
performAction($event: MouseEvent): void {
if (this.disabled) {
return;
}
if (this.removeTooltip) {
this._matTooltip.hide();
performAction($event: MouseEvent) {
if (this.removeTooltip()) {
this._matTooltip().hide();
// Timeout to allow tooltip to disappear first,
// useful when removing an item from the list without a confirmation dialog
setTimeout(() => this.action.emit($event));

View File

@ -1,12 +1,19 @@
<button
(click)="!disabled && action.emit($event)"
[disabled]="disabled"
[id]="id ? id : buttonId"
[ngClass]="classes"
[type]="submit ? 'submit' : 'button'"
(click)="!disabled() && emitAction($event)"
[disabled]="disabled()"
[id]="buttonId()"
[iqserStopPropagation]="action.observed && !_hasRouterLink"
[ngClass]="_classes()"
[type]="submit() ? 'submit' : 'button'"
mat-button
>
<mat-icon *ngIf="icon" [svgIcon]="icon"></mat-icon>
<span>{{ label }}</span>
@if (icon(); as icon) {
<mat-icon [svgIcon]="icon"></mat-icon>
}
<span>{{ label() }}</span>
</button>
<div *ngIf="showDot" class="dot"></div>
@if (showDot()) {
<div class="dot"></div>
}

View File

@ -1,20 +0,0 @@
button {
padding: 0 14px;
width: 100%;
&.has-icon {
padding: 0 14px 0 10px;
}
&.dark {
background-color: var(--iqser-btn-bg);
&:not(.mat-button-disabled):hover {
background-color: var(--iqser-btn-bg-hover);
}
}
mat-icon {
width: 14px;
}
}

View File

@ -1,34 +1,42 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { NgClass } from '@angular/common';
import { booleanAttribute, ChangeDetectionStrategy, Component, computed, EventEmitter, inject, input, Output } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { RouterLink } from '@angular/router';
import { StopPropagationDirective } from '../../directives';
import { randomString } from '../../utils';
import { IconButtonType, IconButtonTypes } from '../types/icon-button.type';
@Component({
selector: 'iqser-icon-button [label]',
selector: 'iqser-icon-button',
templateUrl: './icon-button.component.html',
styleUrls: ['./icon-button.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgClass, MatButtonModule, MatIconModule, StopPropagationDirective],
})
export class IconButtonComponent {
readonly iconButtonTypes = IconButtonTypes;
readonly buttonId: string;
@Input() label!: string;
@Input() id?: string;
@Input() icon?: string;
@Input() showDot = false;
@Input() disabled = false;
@Input() submit = false;
@Input() type: IconButtonType = IconButtonTypes.default;
protected readonly _hasRouterLink = !!inject(RouterLink, { optional: true, host: true });
readonly label = input.required<string>();
readonly buttonId = input(`${randomString()}-icon-button`);
readonly icon = input<string>();
readonly showDot = input(false, { transform: booleanAttribute });
readonly active = input(false, { transform: booleanAttribute });
readonly disabled = input(false, { transform: booleanAttribute });
readonly submit = input(false, { transform: booleanAttribute });
readonly type = input<IconButtonType>(IconButtonTypes.default);
protected readonly _classes = computed(() => {
return {
overlay: this.showDot(),
[this.type()]: true,
'has-icon': !!this.icon(),
active: this.active(),
};
});
@Output() readonly action = new EventEmitter<MouseEvent>();
constructor() {
this.buttonId = `${Math.random().toString(36).substring(2, 9)}-button`;
}
get classes(): Record<string, boolean> {
return {
overlay: this.showDot,
[this.type]: true,
'has-icon': !!this.icon,
};
emitAction($event: MouseEvent) {
const activeElement = document.activeElement as HTMLElement;
if (activeElement.tagName?.toLowerCase() === 'button') {
this.action.emit($event);
}
}
}

View File

@ -1,8 +1,5 @@
export * from './buttons.module';
export * from './types/icon-button.type';
export * from './types/circle-button.type';
export * from './icon-button/icon-button.component';
export * from './circle-button/circle-button.component';
export * from './chevron-button/chevron-button.component';

View File

@ -3,7 +3,6 @@ export const CircleButtonTypes = {
primary: 'primary',
warn: 'warn',
dark: 'dark',
help: 'help',
} as const;
export type CircleButtonType = keyof typeof CircleButtonTypes;

View File

@ -2,7 +2,6 @@ export const IconButtonTypes = {
default: 'default',
dark: 'dark',
primary: 'primary',
help: 'help',
} as const;
export type IconButtonType = keyof typeof IconButtonTypes;

View File

@ -12,7 +12,11 @@ export class CacheApiService {
}
get cachesAvailable(): boolean {
return !!window.caches;
try {
return !!caches;
} catch (e) {
return false;
}
}
cacheRequest(request: HttpRequest<any>, httpResponse: HttpResponse<any>) {
@ -20,6 +24,10 @@ export class CacheApiService {
return;
}
if (!this.cachesAvailable) {
return;
}
const url = this._buildUrl(request);
for (const dynCache of this._dynamicCaches) {
for (const cacheUrl of dynCache.urls) {
@ -35,6 +43,10 @@ export class CacheApiService {
async wipeCaches(logoutDependant = false) {
// console.log('[CACHE-API] delete app level cache ');
if (!this.cachesAvailable) {
return;
}
await caches.delete(APP_LEVEL_CACHE);
for (const cache of this._dynamicCaches) {
@ -234,6 +246,10 @@ export class CacheApiService {
private async _handleCacheExpiration(dynCache: DynamicCache, now: number) {
// console.log('[CACHE-API] checking cache expiration');
if (!this.cachesAvailable) {
return;
}
const cache = await caches.open(dynCache.name);
let keys = await cache.keys();
// removed expired;

View File

@ -6,20 +6,21 @@ export const APP_LEVEL_CACHE = 'app-level-cache';
export const DYNAMIC_CACHES = new InjectionToken<DynamicCaches>('dynamic-caches');
export async function wipeAllCaches() {
console.log('get caches keys');
const keys = await caches.keys();
const keys = (await caches?.keys()) ?? [];
for (const cache of keys) {
await wipeCache(cache);
}
}
export function wipeCache(cacheName: string) {
console.log('delete cache: ', cacheName);
return caches.delete(cacheName);
return caches?.delete(cacheName);
}
export async function wipeCacheEntry(cacheName: string, entry: string) {
console.log('open cache: ', cacheName);
if (!caches) {
return;
}
const cache = await caches.open(cacheName);
return cache.delete(entry, { ignoreSearch: false });
}

View File

@ -1,4 +1,4 @@
import { List } from '../utils';
import { List } from '../utils/types/iqser-types';
export interface DynamicCache {
readonly urls: List;

View File

@ -1,72 +1,61 @@
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { inject, ModuleWithProviders, NgModule, Optional, Provider, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatIconModule } from '@angular/material/icon';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core';
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
import { SortByPipe } from './sorting';
import { CommonUiOptions, IqserAppConfig, ModuleWithOptions } from './utils';
import { HiddenActionComponent, ToastComponent } from './shared';
import { CommonUiOptions, IqserAppConfig, ModuleOptions } from './utils';
import { ConnectionStatusComponent, FullPageErrorComponent } from './error';
import { IqserListingModule } from './listing';
import { IqserFiltersModule } from './filtering';
import { IqserInputsModule } from './inputs';
import { IqserIconsModule } from './icons';
import { IqserButtonsModule } from './buttons';
import { IqserScrollbarModule } from './scrollbar';
import { IqserEmptyStatesModule } from './empty-states';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatLegacyDialogModule as MatDialogModule } from '@angular/material/legacy-dialog';
import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
import { MatLegacyProgressBarModule as MatProgressBarModule } from '@angular/material/legacy-progress-bar';
import { ConfirmationDialogComponent } from './dialog';
import { MatLegacyTooltipModule as MatTooltipModule } from '@angular/material/legacy-tooltip';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ApiPathInterceptor, DefaultUserPreferenceService, IqserConfigService, IqserUserPreferenceService } from './services';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { IqserSkeletonModule } from './skeleton/skeleton.module';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { MatDialogModule } from '@angular/material/dialog';
import { CircleButtonComponent, IconButtonComponent } from './buttons';
import { DomSanitizer } from '@angular/platform-browser';
import { ICONS } from './utils/constants';
import { StopPropagationDirective } from './directives';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
const matModules = [
MatIconModule,
MatProgressSpinnerModule,
MatButtonModule,
MatDialogModule,
MatCheckboxModule,
MatTooltipModule,
MatProgressBarModule,
];
const modules = [
IqserIconsModule,
IqserButtonsModule,
IqserListingModule,
IqserFiltersModule,
IqserInputsModule,
IqserScrollbarModule,
IqserEmptyStatesModule,
IqserSkeletonModule,
HttpClientModule,
];
const components = [ConnectionStatusComponent, FullPageErrorComponent, HiddenActionComponent, ConfirmationDialogComponent, ToastComponent];
const pipes = [SortByPipe];
const matModules = [MatIconModule, MatButtonModule, MatDialogModule, MatCheckboxModule, MatTooltipModule, MatProgressBarModule];
const components = [ConnectionStatusComponent, FullPageErrorComponent];
@NgModule({
declarations: [...components, ...pipes],
imports: [CommonModule, ...matModules, ...modules, FormsModule, ReactiveFormsModule, TranslateModule],
exports: [...components, ...pipes, ...modules],
declarations: [...components],
imports: [
CommonModule,
...matModules,
FormsModule,
ReactiveFormsModule,
TranslateModule,
IconButtonComponent,
CircleButtonComponent,
StopPropagationDirective,
],
exports: [...components],
providers: [
{
provide: HTTP_INTERCEPTORS,
multi: true,
useClass: ApiPathInterceptor,
},
{ provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: { appearance: 'outline' } },
],
})
export class CommonUiModule extends ModuleWithOptions {
export class CommonUiModule {
constructor(@Optional() @SkipSelf() parentModule?: CommonUiModule) {
super();
if (parentModule) {
throw new Error('CommonUiModule is already loaded. Import it in the AppModule only!');
}
const iconRegistry = inject(MatIconRegistry);
const sanitizer = inject(DomSanitizer);
ICONS.forEach(icon => {
const url = sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/${icon}.svg`);
iconRegistry.addSvgIconInNamespace('iqser', icon, url);
});
}
static forRoot<
@ -74,7 +63,7 @@ export class CommonUiModule extends ModuleWithOptions {
Config extends IqserConfigService<AppConfig>,
AppConfig extends IqserAppConfig = IqserAppConfig,
>(options: CommonUiOptions<UserPreference, Config, AppConfig>): ModuleWithProviders<CommonUiModule> {
const userPreferenceService = this._getService(
const userPreferenceService = ModuleOptions.getService(
IqserUserPreferenceService,
DefaultUserPreferenceService,
options.existingUserPreferenceService,
@ -84,11 +73,11 @@ export class CommonUiModule extends ModuleWithOptions {
return {
ngModule: CommonUiModule,
providers: [userPreferenceService, configServiceProviders],
providers: [userPreferenceService, ...configServiceProviders],
};
}
private static _getConfigServiceProviders(configServiceFactory: () => unknown, configService?: unknown) {
private static _getConfigServiceProviders(configServiceFactory: () => unknown, configService?: unknown): Provider[] {
if (configService) {
return [
{

View File

@ -1,36 +1,42 @@
import { Directive, HostListener, inject, OnDestroy } from '@angular/core';
import { MatLegacyDialog as MatDialog, MatLegacyDialogRef as MatDialogRef } from '@angular/material/legacy-dialog';
import { AfterViewInit, Directive, HostListener, inject, OnDestroy, signal } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { hasFormChanged, IqserEventTarget } from '../utils';
import { ConfirmOptions } from '.';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { debounceTime, firstValueFrom, fromEvent, merge, of, Subscription } from 'rxjs';
import { tap } from 'rxjs/operators';
import { IconButtonTypes } from '../buttons/types/icon-button.type';
import { LoadingService } from '../loading/loading.service';
import { Toaster } from '../services/toaster.service';
import { hasFormChanged } from '../utils/functions';
import { IqserEventTarget } from '../utils/types/events.type';
import { ConfirmationDialogService } from './confirmation-dialog.service';
import { firstValueFrom } from 'rxjs';
import { LoadingService } from '../loading';
import { Toaster } from '../services';
import { IconButtonTypes } from '../buttons';
import { ConfirmOptions } from './confirmation-dialog/confirmation-dialog.component';
const TARGET_NODE = 'mat-dialog-container';
const DIALOG_CONTAINER = 'mat-dialog-container';
const TEXT_INPUT = 'text';
export interface SaveOptions {
closeAfterSave?: boolean;
nextAction?: boolean;
addMembers?: boolean;
}
@Directive()
export abstract class BaseDialogComponent implements OnDestroy {
readonly iconButtonTypes = IconButtonTypes;
form?: UntypedFormGroup;
initialFormValue!: Record<string, string>;
export abstract class BaseDialogComponent implements AfterViewInit, OnDestroy {
readonly #confirmationDialogService = inject(ConfirmationDialogService);
readonly #dialog = inject(MatDialog);
protected readonly _hasErrors = signal(true);
protected readonly _formBuilder = inject(UntypedFormBuilder);
protected readonly _loadingService = inject(LoadingService);
protected readonly _toaster = inject(Toaster);
readonly #confirmationDialogService = inject(ConfirmationDialogService);
readonly #dialog = inject(MatDialog);
#backdropClickSubscription = this._dialogRef.backdropClick().subscribe(() => {
this.close();
});
protected readonly _subscriptions = new Subscription();
readonly iconButtonTypes = IconButtonTypes;
form?: UntypedFormGroup;
initialFormValue!: Record<string, string>;
protected constructor(protected readonly _dialogRef: MatDialogRef<BaseDialogComponent>, private readonly _isInEditMode?: boolean) {}
protected constructor(
protected readonly _dialogRef: MatDialogRef<BaseDialogComponent>,
private readonly _isInEditMode = false,
) {}
get valid(): boolean {
return !this.form || this.form.valid;
@ -41,36 +47,59 @@ export abstract class BaseDialogComponent implements OnDestroy {
}
get disabled(): boolean {
return !this.valid || !this.changed;
return !this.valid || !this.changed || this._hasErrors();
}
ngAfterViewInit() {
this._subscriptions.add(this._dialogRef.backdropClick().subscribe(() => this.close()));
const valueChanges = this.form?.valueChanges ?? of(null);
const events = [fromEvent(window, 'keyup'), fromEvent(window, 'input'), valueChanges];
this._hasErrors.set(!!document.getElementsByClassName('ng-invalid')[0]);
const events$ = merge(...events).pipe(
debounceTime(10),
tap(() => {
this._hasErrors.set(!!document.getElementsByClassName('ng-invalid')[0]);
}),
);
this._subscriptions.add(events$.subscribe());
}
abstract save(options?: SaveOptions): void;
ngOnDestroy(): void {
this.#backdropClickSubscription.unsubscribe();
this._subscriptions.unsubscribe();
}
close(): void {
if (this._isInEditMode && this.changed) {
this._openConfirmDialog().then(result => {
if (result in ConfirmOptions) {
if (result === ConfirmOptions.CONFIRM) {
this.save({ closeAfterSave: true });
} else {
this._dialogRef.close();
}
}
});
} else {
this._dialogRef.close();
close() {
if (!this._isInEditMode || !this.changed) {
return this._dialogRef.close();
}
this._openConfirmDialog().then(result => {
if (result) {
if (result === ConfirmOptions.CONFIRM) {
this.save({ closeAfterSave: true });
} else {
this._dialogRef.close();
}
}
});
}
@HostListener('window:keydown.Enter', ['$event'])
onEnter(event: KeyboardEvent): void {
event?.stopImmediatePropagation();
const node = (event.target as IqserEventTarget).localName?.trim()?.toLowerCase();
if (this.valid && !this.disabled && this.changed && node === TARGET_NODE) {
const target = event.target as IqserEventTarget;
const isDialogSelected = target.localName?.trim()?.toLowerCase() === DIALOG_CONTAINER;
const isTextInputSelected = target.type?.trim()?.toLowerCase() === TEXT_INPUT;
if (
this.valid &&
!this.disabled &&
(this.changed || !this._isInEditMode) &&
this.#dialog.openDialogs.length === 1 &&
(isDialogSelected || isTextInputSelected)
) {
event?.stopImmediatePropagation();
this.save();
}
}
@ -83,7 +112,7 @@ export abstract class BaseDialogComponent implements OnDestroy {
}
protected _openConfirmDialog() {
const dialogRef = this.#confirmationDialogService.openDialog({ disableConfirm: !this.valid });
const dialogRef = this.#confirmationDialogService.open({ disableConfirm: !this.valid });
return firstValueFrom(dialogRef.afterClosed());
}
}

View File

@ -1,41 +1,35 @@
import { Injectable } from '@angular/core';
import { MatLegacyDialog as MatDialog, MatLegacyDialogRef as MatDialogRef } from '@angular/material/legacy-dialog';
import { inject, Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DialogConfig, DialogService } from '../services';
import { ConfirmationDialogComponent, ConfirmationDialogInput, TitleColors } from '.';
type DialogType = 'confirm';
import {
ConfirmationDialogComponent,
ConfirmOption,
IConfirmationDialogData,
TitleColors,
} from './confirmation-dialog/confirmation-dialog.component';
import { defaultDialogConfig } from './dialog.service';
@Injectable({
providedIn: 'root',
})
export class ConfirmationDialogService extends DialogService<DialogType> {
protected readonly _config: DialogConfig<DialogType> = {
confirm: {
component: ConfirmationDialogComponent,
dialogConfig: { disableClose: false },
},
};
export class ConfirmationDialogService {
readonly #dialog = inject(MatDialog);
constructor(protected readonly _dialog: MatDialog) {
super(_dialog);
}
open(data?: { disableConfirm: boolean }) {
const dialogData: IConfirmationDialogData = {
title: _('confirmation-dialog.unsaved-changes.title'),
question: _('confirmation-dialog.unsaved-changes.question'),
details: _('confirmation-dialog.unsaved-changes.details'),
confirmationText: _('confirmation-dialog.unsaved-changes.confirmation-text'),
discardChangesText: _('confirmation-dialog.unsaved-changes.discard-changes-text'),
disableConfirm: data?.disableConfirm,
titleColor: TitleColors.WARN,
};
// @ts-ignore
openDialog(data?: { disableConfirm: boolean; [key: string]: unknown }): MatDialogRef<unknown> {
return super.openDialog(
'confirm',
// @ts-ignore
undefined,
new ConfirmationDialogInput({
title: _('confirmation-dialog.unsaved-changes.title'),
question: _('confirmation-dialog.unsaved-changes.question'),
details: _('confirmation-dialog.unsaved-changes.details'),
confirmationText: _('confirmation-dialog.unsaved-changes.confirmation-text'),
discardChangesText: _('confirmation-dialog.unsaved-changes.discard-changes-text'),
disableConfirm: data?.disableConfirm,
titleColor: TitleColors.WARN,
}),
);
return this.#dialog.open<ConfirmationDialogComponent, IConfirmationDialogData, ConfirmOption>(ConfirmationDialogComponent, {
...defaultDialogConfig,
disableClose: false,
data: dialogData,
});
}
}

View File

@ -1,60 +1,83 @@
<section class="dialog">
<div [class.warn]="isDeleteAction" class="dialog-header heading-l">
{{ config.title }}
</div>
<div [class.warn]="isDeleteAction" [innerHTML]="config.title" class="dialog-header heading-l"></div>
<div *ngIf="showToast && config.toastMessage" class="inline-dialog-toast toast-error">
<div [translate]="config.toastMessage"></div>
<a (click)="showToast = false" class="toast-close-button">
<mat-icon svgIcon="iqser:close"></mat-icon>
</a>
</div>
@if (showToast && config.toastMessage) {
<div class="inline-dialog-toast toast-error">
<div [translate]="config.toastMessage"></div>
<a (click)="showToast = false" class="toast-close-button">
<mat-icon svgIcon="iqser:close"></mat-icon>
</a>
</div>
}
<div class="dialog-content">
@if (config.component) {
<ng-container #detailsComponent></ng-container>
}
<p [class.heading]="isDeleteAction" [innerHTML]="config.question" class="mt-0 mb-8"></p>
<p *ngIf="config.details" [innerHTML]="config.details" class="mt-0"></p>
@if (config.details) {
<p [innerHTML]="config.details" class="mt-0"></p>
}
<div *ngIf="config.requireInput" class="iqser-input-group required w-300 mt-24">
<label>{{ inputLabel }}</label>
<input [(ngModel)]="inputValue" id="confirmation-input" />
</div>
@if (config.requireInput) {
<div class="iqser-input-group required w-300 mt-24">
<label>{{ inputLabel }}</label>
<input [(ngModel)]="inputValue" id="confirmation-input" />
</div>
}
<div *ngIf="config.checkboxes.length > 0" class="mt-24 checkboxes-wrapper">
<ng-container *ngFor="let checkbox of config.checkboxes">
<mat-checkbox [(ngModel)]="checkbox.value" [class.error]="!checkbox.value && showToast" color="primary">
{{ checkbox.label | translate : config.translateParams }}
</mat-checkbox>
<ng-container *ngTemplateOutlet="checkbox.extraContent; context: { data: checkbox.extraContentData }"></ng-container>
</ng-container>
</div>
@if (config.checkboxes.length > 0) {
<div class="mt-24 checkboxes-wrapper">
@for (checkbox of config.checkboxes; track checkbox) {
<mat-checkbox [(ngModel)]="checkbox.value" [class.error]="!checkbox.value && showToast" color="primary">
{{ checkbox.label | translate: config.translateParams }}
</mat-checkbox>
<ng-container *ngTemplateOutlet="checkbox.extraContent; context: { data: checkbox.extraContentData }"></ng-container>
}
</div>
}
</div>
<div class="dialog-actions">
<iqser-icon-button
(action)="confirm(confirmOption)"
[disabled]="(config.requireInput && confirmationDoesNotMatch()) || config.disableConfirm"
[label]="config.confirmationText"
[type]="iconButtonTypes.primary"
id="confirm"
>
</iqser-icon-button>
<div class="dialog-actions" [class.reverse]="config.cancelButtonPrimary">
@if (!config.cancelButtonPrimary) {
<iqser-icon-button
(action)="confirm(confirmOption)"
[disabled]="(config.requireInput && confirmationDoesNotMatch()) || config.disableConfirm"
[label]="config.confirmationText"
[type]="iconButtonTypes.primary"
buttonId="confirm"
></iqser-icon-button>
} @else {
<div (click)="confirm(confirmOption)" class="all-caps-label cancel no-uppercase" id="confirm">
{{ config.confirmationText }}
</div>
}
<iqser-icon-button
(click)="confirm(ConfirmOptions.SECOND_CONFIRM)"
*ngIf="config.alternativeConfirmationText"
[disabled]="config.requireInput && confirmationDoesNotMatch()"
[label]="config.alternativeConfirmationText"
[type]="iconButtonTypes.primary"
>
</iqser-icon-button>
@if (config.alternativeConfirmationText) {
<iqser-icon-button
(action)="confirm(confirmOptions.CONFIRM_WITH_ACTION)"
[disabled]="config.requireInput && confirmationDoesNotMatch()"
[label]="config.alternativeConfirmationText"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
}
<div (click)="confirm(ConfirmOptions.DISCARD_CHANGES)" *ngIf="config.discardChangesText" class="all-caps-label cancel">
{{ config.discardChangesText }}
</div>
@if (config.discardChangesText) {
<div (click)="confirm(confirmOptions.DISCARD_CHANGES)" class="all-caps-label cancel">
{{ config.discardChangesText }}
</div>
}
<div (click)="deny()" *ngIf="!config.discardChangesText" class="all-caps-label cancel">
{{ config.denyText }}
</div>
@if (!config.discardChangesText) {
@if (config.cancelButtonPrimary) {
<iqser-icon-button (click)="deny()" [label]="config.denyText" [type]="iconButtonTypes.primary"></iqser-icon-button>
} @else {
<div (click)="deny()" class="all-caps-label cancel">
{{ config.denyText }}
</div>
}
}
</div>
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>

View File

@ -6,3 +6,12 @@
display: flex;
flex-direction: column;
}
.reverse {
flex-direction: row-reverse;
justify-content: flex-end;
}
.no-uppercase {
text-transform: unset;
}

View File

@ -1,21 +1,39 @@
import { ChangeDetectionStrategy, Component, HostListener, Inject, TemplateRef } from '@angular/core';
import { MAT_LEGACY_DIALOG_DATA as MAT_DIALOG_DATA, MatLegacyDialogRef as MatDialogRef } from '@angular/material/legacy-dialog';
import { TranslateService } from '@ngx-translate/core';
import { NgTemplateOutlet } from '@angular/common';
import {
AfterViewInit,
ChangeDetectionStrategy,
Component,
HostListener,
inject,
TemplateRef,
Type,
viewChild,
ViewContainerRef,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IconButtonTypes } from '../../buttons';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { CircleButtonComponent, IconButtonTypes } from '../../buttons';
import { IconButtonComponent } from '../../buttons';
import { ValuesOf } from '../../utils';
export type TitleColor = 'default' | 'warn';
export const TitleColors = {
DEFAULT: 'default',
WARN: 'warn',
} as const;
export enum TitleColors {
DEFAULT = 'default',
WARN = 'warn',
}
export type TitleColor = ValuesOf<typeof TitleColors>;
export enum ConfirmOptions {
CONFIRM = 1,
SECOND_CONFIRM = 2,
DISCARD_CHANGES = 3,
}
export const ConfirmOptions = {
CONFIRM: 1,
CONFIRM_WITH_ACTION: 2,
DISCARD_CHANGES: 3,
} as const;
export type ConfirmOption = ValuesOf<typeof ConfirmOptions>;
interface CheckBox {
value: boolean;
@ -24,76 +42,75 @@ interface CheckBox {
extraContentData?: Record<string, unknown>;
}
interface IConfirmationDialogInput {
title?: string;
titleColor?: TitleColor;
question?: string;
details?: string;
confirmationText?: string;
alternativeConfirmationText?: string;
discardChangesText?: string;
requireInput?: boolean;
disableConfirm?: boolean;
denyText?: string;
translateParams?: Record<string, unknown>;
checkboxes?: CheckBox[];
checkboxesValidation?: boolean;
toastMessage?: string;
interface InternalConfirmationDialogData {
readonly title: string;
readonly titleColor: TitleColor;
readonly question: string;
readonly details: string;
readonly confirmationText: string;
readonly alternativeConfirmationText?: string;
readonly discardChangesText?: string;
readonly requireInput: boolean;
readonly disableConfirm: boolean;
readonly denyText: string;
readonly translateParams?: Record<string, unknown>;
readonly checkboxes: CheckBox[];
readonly checkboxesValidation: boolean;
readonly toastMessage?: string;
readonly component?: Type<unknown>;
readonly componentInputs?: { [key: string]: unknown };
readonly cancelButtonPrimary?: boolean;
}
export class ConfirmationDialogInput implements IConfirmationDialogInput {
title: string;
titleColor: TitleColor;
question: string;
details: string;
confirmationText: string;
alternativeConfirmationText?: string;
discardChangesText?: string;
requireInput: boolean;
disableConfirm: boolean;
denyText: string;
translateParams: Record<string, unknown>;
checkboxes: CheckBox[];
checkboxesValidation: boolean;
toastMessage?: string;
export type IConfirmationDialogData = Partial<InternalConfirmationDialogData>;
constructor(options?: IConfirmationDialogInput) {
this.title = options?.title || _('common.confirmation-dialog.title');
this.titleColor = options?.titleColor || TitleColors.DEFAULT;
this.question = options?.question || _('common.confirmation-dialog.description');
this.details = options?.details || '';
this.confirmationText = options?.confirmationText || _('common.confirmation-dialog.confirm');
this.alternativeConfirmationText = options?.alternativeConfirmationText;
this.discardChangesText = options?.discardChangesText;
this.requireInput = options?.requireInput || false;
this.disableConfirm = options?.disableConfirm || false;
this.denyText = options?.denyText || _('common.confirmation-dialog.deny');
this.translateParams = options?.translateParams || {};
this.checkboxes = options?.checkboxes || [];
this.checkboxesValidation = typeof options?.checkboxesValidation === 'boolean' ? options.checkboxesValidation : true;
this.toastMessage = options?.toastMessage;
}
function getConfig(options?: IConfirmationDialogData): InternalConfirmationDialogData {
return {
...options,
title: options?.title ?? _('common.confirmation-dialog.title'),
titleColor: options?.titleColor ?? TitleColors.DEFAULT,
question: options?.question ?? _('common.confirmation-dialog.description'),
details: options?.details ?? '',
confirmationText: options?.confirmationText ?? _('common.confirmation-dialog.confirm'),
requireInput: options?.requireInput ?? false,
disableConfirm: options?.disableConfirm ?? false,
denyText: options?.denyText ?? _('common.confirmation-dialog.deny'),
checkboxes: options?.checkboxes ?? [],
checkboxesValidation: typeof options?.checkboxesValidation === 'boolean' ? options.checkboxesValidation : true,
component: options?.component,
componentInputs: options?.componentInputs,
cancelButtonPrimary: options?.cancelButtonPrimary ?? false,
};
}
@Component({
templateUrl: './confirmation-dialog.component.html',
styleUrls: ['./confirmation-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
MatIconModule,
FormsModule,
MatCheckboxModule,
TranslateModule,
NgTemplateOutlet,
IconButtonComponent,
CircleButtonComponent,
MatDialogModule,
],
})
export class ConfirmationDialogComponent {
config: ConfirmationDialogInput;
export class ConfirmationDialogComponent implements AfterViewInit {
readonly config = getConfig(inject(MAT_DIALOG_DATA));
inputValue = '';
showToast = false;
readonly inputLabel: string;
readonly ConfirmOptions = ConfirmOptions;
readonly confirmOptions = ConfirmOptions;
readonly iconButtonTypes = IconButtonTypes;
readonly detailsComponentRef = viewChild.required('detailsComponent', { read: ViewContainerRef });
constructor(
private readonly _dialogRef: MatDialogRef<ConfirmationDialogComponent>,
private readonly _dialogRef: MatDialogRef<ConfirmationDialogComponent, ConfirmOption>,
private readonly _translateService: TranslateService,
@Inject(MAT_DIALOG_DATA) private readonly _confirmationDialogInput: ConfirmationDialogInput,
) {
this.config = _confirmationDialogInput ?? new ConfirmationDialogInput();
this.translate(this.config);
this.inputLabel = `${this._translateService.instant('confirmation-dialog.input-label')} '${this.config.confirmationText || ''}'`;
}
@ -103,32 +120,38 @@ export class ConfirmationDialogComponent {
}
get isDeleteAction(): boolean {
return this.config?.titleColor === TitleColors.WARN;
return this.config.titleColor === TitleColors.WARN;
}
get confirmOption(): ConfirmOptions {
get confirmOption(): ConfirmOption {
if (!this.config.checkboxesValidation && this.config.checkboxes[0]?.value) {
return ConfirmOptions.SECOND_CONFIRM;
return ConfirmOptions.CONFIRM_WITH_ACTION;
}
return ConfirmOptions.CONFIRM;
}
@HostListener('window:keyup.enter')
onKeyupEnter(): void {
if (this.config.requireInput && !this.confirmationDoesNotMatch()) {
this.confirm(1);
@HostListener('window:keyup.enter', ['$event'])
onKeyupEnter(event: KeyboardEvent): void {
event?.stopImmediatePropagation();
if (!this.config.requireInput || !this.confirmationDoesNotMatch()) {
if (!this.config.cancelButtonPrimary) this.confirm(ConfirmOptions.CONFIRM);
else this.deny();
}
}
confirmationDoesNotMatch(): boolean {
return this.inputValue.toLowerCase() !== this.config.confirmationText?.toLowerCase();
ngAfterViewInit() {
this.#initializeDetailsComponent();
}
deny(): void {
confirmationDoesNotMatch(): boolean {
return this.inputValue.toLowerCase() !== this.config.confirmationText.toLowerCase();
}
deny() {
this._dialogRef.close();
}
confirm(option: ConfirmOptions): void {
confirm(option: ConfirmOption) {
if (this.uncheckedBoxes) {
this.showToast = true;
} else {
@ -136,8 +159,8 @@ export class ConfirmationDialogComponent {
}
}
translate(obj: ConfirmationDialogInput): void {
const translateKeys: (keyof typeof obj)[] = [
translate(obj: InternalConfirmationDialogData) {
const translateKeys: (keyof InternalConfirmationDialogData)[] = [
'title',
'question',
'details',
@ -147,12 +170,20 @@ export class ConfirmationDialogComponent {
'denyText',
];
translateKeys
.filter(key => !!obj[key])
.forEach(key => {
Object.assign(obj, {
[key]: this._translateService.instant(obj[key] as string, this.config.translateParams) as string,
});
});
const filtered = translateKeys.filter(key => !!obj[key]);
filtered.forEach(key => {
const value = this._translateService.instant(obj[key] as string, this.config.translateParams);
Object.assign(obj, { [key]: value });
});
}
#initializeDetailsComponent() {
if (!this.config.component) return;
const component = this.detailsComponentRef().createComponent(this.config.component);
if (this.config.componentInputs) {
for (const [key, value] of Object.entries(this.config.componentInputs)) {
(component.instance as any)[key] = value;
}
}
}
}

View File

@ -1,12 +1,8 @@
import { Injectable } from '@angular/core';
import {
MatLegacyDialog as MatDialog,
MatLegacyDialogConfig as MatDialogConfig,
MatLegacyDialogRef as MatDialogRef,
} from '@angular/material/legacy-dialog';
import { ComponentType } from '@angular/cdk/portal';
import { mergeMap } from 'rxjs/operators';
import { Injectable, Type } from '@angular/core';
import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material/dialog';
import { from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
export const largeDialogConfig: MatDialogConfig = {
width: '90vw',
@ -40,14 +36,12 @@ export abstract class DialogService<T extends string> {
openDialog(
type: T,
$event: MouseEvent,
data: unknown,
data?: unknown,
cb?: (...params: unknown[]) => Promise<unknown> | void,
finallyCb?: (...params: unknown[]) => void | Promise<unknown>,
): MatDialogRef<unknown> {
const config = this._config[type];
$event?.stopPropagation();
const ref = this._dialog.open(config.component, {
...defaultDialogConfig,
...(config.dialogConfig || {}),
@ -70,4 +64,34 @@ export abstract class DialogService<T extends string> {
return ref;
}
open(
type: Type<unknown>,
data?: unknown,
config?: object,
cb?: (...params: unknown[]) => Promise<unknown> | void,
finallyCb?: (...params: unknown[]) => void | Promise<unknown>,
): MatDialogRef<unknown> {
const ref = this._dialog.open(type, {
...defaultDialogConfig,
...(config || {}),
data,
});
const fn = async (result: unknown) => {
if (result && cb) {
await cb(result);
}
if (finallyCb) {
await finallyCb(result);
}
};
ref.afterClosed()
.pipe(mergeMap(result => from(fn(result))))
.subscribe();
return ref;
}
}

View File

@ -1,3 +1,6 @@
export * from './base-dialog.component';
export * from './confirmation-dialog.service';
export * from './confirmation-dialog/confirmation-dialog.component';
export * from './dialog.service';
export * from './iqser-dialog-component.directive';
export * from './iqser-dialog.service';

View File

@ -0,0 +1,82 @@
import { Directive, HostListener, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { IconButtonTypes } from '../buttons';
import { hasFormChanged, IqserEventTarget } from '../utils';
const DIALOG_CONTAINER = 'mat-dialog-container';
const DATA_TYPE_SYMBOL = Symbol.for('DATA_TYPE');
const RETURN_TYPE_SYMBOL = Symbol.for('RETURN_TYPE');
export type DATA_TYPE = typeof DATA_TYPE_SYMBOL;
export type RETURN_TYPE = typeof RETURN_TYPE_SYMBOL;
@Directive()
export abstract class IqserDialogComponent<ComponentType, DataType = null, ReturnType = void> {
readonly [DATA_TYPE_SYMBOL]!: DataType;
readonly [RETURN_TYPE_SYMBOL]!: ReturnType;
readonly iconButtonTypes = IconButtonTypes;
readonly dialogRef = inject(MatDialogRef<ComponentType, ReturnType>);
readonly data = inject<DataType>(MAT_DIALOG_DATA);
readonly dialog = inject(MatDialog);
readonly form?: FormGroup;
readonly ignoredKeys: string[] = [];
initialFormValue: Record<string, unknown> = {};
constructor(private readonly _editMode = false) {
this.dialogRef
.backdropClick()
.pipe(takeUntilDestroyed())
// eslint-disable-next-line rxjs/no-ignored-subscription
.subscribe(() => this.dialogRef.close());
}
get valid(): boolean {
return !this.form || this.form.valid;
}
get changed(): boolean {
return !this.form || hasFormChanged(this.form, this.initialFormValue, this.ignoredKeys);
}
get disabled(): boolean {
return !this.valid || !this.changed;
}
@HostListener('window:keydown.Escape', ['$event'])
onEscape(): void {
if (this.dialog.openDialogs.length === 1) {
this.dialogRef.close();
}
}
@HostListener('window:keydown.Enter', ['$event'])
onEnter(event: KeyboardEvent): void {
event?.stopImmediatePropagation();
if (this.onEnterValidator(event)) {
this.close();
}
}
onEnterValidator(event: KeyboardEvent) {
const targetElement = (event.target as IqserEventTarget).localName?.trim()?.toLowerCase();
const canClose = targetElement === DIALOG_CONTAINER && this.valid;
if (this._editMode) {
return canClose && this.changed;
}
return canClose;
}
close(dialogResult?: ReturnType) {
this.dialogRef.close(dialogResult);
}
}
/**
* This is for testing only
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class DialogComponentImpl extends IqserDialogComponent<DialogComponentImpl, unknown, unknown> {}

View File

@ -0,0 +1,44 @@
import { ComponentType } from '@angular/cdk/portal';
import { inject, Injectable } from '@angular/core';
import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material/dialog';
import { firstValueFrom } from 'rxjs';
import { defaultDialogConfig, largeDialogConfig } from './dialog.service';
import { DATA_TYPE, IqserDialogComponent, RETURN_TYPE } from './iqser-dialog-component.directive';
@Injectable({
providedIn: 'root',
})
export class IqserDialog {
protected readonly _dialog = inject(MatDialog);
open<
Component extends IqserDialogComponent<Component, Component[DATA_TYPE], Component[RETURN_TYPE]>,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Data extends Component[DATA_TYPE] = Component[DATA_TYPE],
Return extends Component[RETURN_TYPE] = Component[RETURN_TYPE],
>(dialog: ComponentType<Component>, config?: MatDialogConfig<Component[DATA_TYPE]>) {
const ref = this._dialog.open<Component, Component[DATA_TYPE], Return>(dialog, config);
return {
...ref,
result() {
return firstValueFrom(ref.afterClosed());
},
} as MatDialogRef<Component, Return> & { result(): Promise<Return> };
}
openLarge<
Component extends IqserDialogComponent<Component, Component[DATA_TYPE], Component[RETURN_TYPE]>,
Data extends Component[DATA_TYPE] = Component[DATA_TYPE],
Return extends Component[RETURN_TYPE] = Component[RETURN_TYPE],
>(dialog: ComponentType<Component>, config?: MatDialogConfig<Component[DATA_TYPE]>) {
return this.open<Component, Data, Return>(dialog, { ...largeDialogConfig, ...config });
}
openDefault<
Component extends IqserDialogComponent<Component, Component[DATA_TYPE], Component[RETURN_TYPE]>,
Data extends Component[DATA_TYPE] = Component[DATA_TYPE],
Return extends Component[RETURN_TYPE] = Component[RETURN_TYPE],
>(dialog: ComponentType<Component>, config?: MatDialogConfig<Component[DATA_TYPE]>) {
return this.open<Component, Data, Return>(dialog, { ...defaultDialogConfig, ...config });
}
}

View File

@ -0,0 +1,8 @@
import { booleanAttribute, Directive, input } from '@angular/core';
@Directive({
selector: '[iqserDisableStopPropagation]',
})
export class DisableStopPropagationDirective {
readonly iqserDisableStopPropagation = input(true, { transform: booleanAttribute });
}

View File

@ -0,0 +1,38 @@
import { Directive, ElementRef, OnDestroy, OnInit, signal } from '@angular/core';
@Directive({
selector: '[iqserHasScrollbar]',
host: {
'[class]': '_class()',
},
})
export class HasScrollbarDirective implements OnInit, OnDestroy {
private readonly _resizeObserver: ResizeObserver;
protected readonly _class = signal('');
constructor(protected readonly _elementRef: ElementRef) {
this._resizeObserver = new ResizeObserver(() => {
this.process();
});
this._resizeObserver.observe(this._elementRef.nativeElement);
}
private get _hasScrollbar() {
const element = this._elementRef?.nativeElement as HTMLElement;
return element.clientHeight < element.scrollHeight;
}
ngOnInit() {
setTimeout(() => this.process(), 0);
}
process() {
const newClass = this._hasScrollbar ? 'has-scrollbar' : '';
this._class.set(newClass);
}
ngOnDestroy() {
this._resizeObserver.unobserve(this._elementRef.nativeElement);
}
}

View File

@ -0,0 +1,45 @@
import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core';
@Directive({
selector: '[iqserHiddenAction]',
})
export class HiddenActionDirective {
@Input() requiredClicks = 4;
@Output() readonly iqserHiddenAction = new EventEmitter();
#dPressed = false;
#clickCount = 0;
#clickCountTimeout?: ReturnType<typeof setTimeout>;
@HostListener('window:keydown.D')
onKeydownD() {
this.#dPressed = true;
}
@HostListener('window:keyup.D')
onKeyupD() {
this.#dPressed = false;
}
@HostListener('click', ['$event'])
countActions($event: MouseEvent): void {
if (!this.#dPressed) {
this.#clickCount = 0;
clearTimeout(this.#clickCountTimeout);
return;
}
$event.stopPropagation();
$event.preventDefault();
this.#clickCount += 1;
if (this.#clickCount === this.requiredClicks) {
this.#clickCount = 0;
this.iqserHiddenAction.emit();
}
clearTimeout(this.#clickCountTimeout);
this.#clickCountTimeout = setTimeout(() => (this.#clickCount = 0), 1000);
}
}

View File

@ -0,0 +1,6 @@
export * from './hidden-action.directive';
export * from './stop-propagation.directive';
export * from './disable-stop-propagation.directive';
export * from './prevent-default.directive';
export * from './has-scrollbar.directive';
export * from './sync-width.directive';

View File

@ -0,0 +1,18 @@
import { booleanAttribute, Directive, HostListener, inject, Input } from '@angular/core';
import { NGXLogger } from 'ngx-logger';
@Directive({
selector: '[iqserPreventDefault]',
})
export class PreventDefaultDirective {
readonly #logger = inject(NGXLogger);
@Input({ transform: booleanAttribute }) iqserPreventDefault = true;
@HostListener('click', ['$event'])
onClick($event: Event) {
if (this.iqserPreventDefault) {
this.#logger.info('[CLICK] iqserPreventDefault');
$event.preventDefault();
}
}
}

View File

@ -0,0 +1,26 @@
import { booleanAttribute, Directive, HostListener, inject, Input } from '@angular/core';
import { NGXLogger } from 'ngx-logger';
import { DisableStopPropagationDirective } from './disable-stop-propagation.directive';
@Directive({
selector: '[iqserStopPropagation]',
})
export class StopPropagationDirective {
readonly #disableStopPropagation = inject(DisableStopPropagationDirective, { optional: true });
readonly #logger = inject(NGXLogger);
@Input({ transform: booleanAttribute }) iqserStopPropagation = true;
@HostListener('click', ['$event'])
onClick($event: Event) {
if (this.#disableStopPropagation?.iqserDisableStopPropagation()) {
this.#logger.info('[CLICK] iqserStopPropagation is disabled by iqserDisableStopPropagation');
return;
}
if (this.iqserStopPropagation) {
this.#logger.info('[CLICK] iqserStopPropagation');
$event.preventDefault();
$event.stopPropagation();
}
}
}

View File

@ -5,24 +5,24 @@ import { Directive, ElementRef, HostListener, Input, OnDestroy } from '@angular/
})
export class SyncWidthDirective implements OnDestroy {
@Input() iqserSyncWidth!: string;
private readonly _interval: number;
readonly #interval: number;
constructor(private readonly _elementRef: ElementRef) {
this._interval = window.setInterval(() => {
this._matchWidth();
this.#interval = window.setInterval(() => {
this.#matchWidth();
}, 1000);
}
ngOnDestroy(): void {
window.clearInterval(this._interval);
ngOnDestroy() {
window.clearInterval(this.#interval);
}
@HostListener('window:resize')
onResize(): void {
this._matchWidth();
onResize() {
this.#matchWidth();
}
private _matchWidth() {
#matchWidth() {
const headerItems = (this._elementRef.nativeElement as HTMLElement).children;
const tableRows = (this._elementRef.nativeElement as HTMLElement).parentElement?.parentElement?.getElementsByClassName(
this.iqserSyncWidth,
@ -34,7 +34,7 @@ export class SyncWidthDirective implements OnDestroy {
(this._elementRef.nativeElement as HTMLElement).setAttribute('synced', 'true');
const { columns, length } = this._sampleRow(tableRows);
const { columns, length } = this.#sampleRow(tableRows);
if (!columns) {
return;
}
@ -57,7 +57,7 @@ export class SyncWidthDirective implements OnDestroy {
}
}
private _sampleRow(tableRows: HTMLCollectionOf<Element>): {
#sampleRow(tableRows: HTMLCollectionOf<Element>): {
columns?: Element[];
length: number;
} {

View File

@ -0,0 +1,22 @@
<div [ngStyle]="styles()" class="empty-state">
@if (icon(); as icon) {
<mat-icon [svgIcon]="icon"></mat-icon>
}
<div class="ng-content-wrapper heading-l">
<ng-content></ng-content>
</div>
<div [innerHTML]="text()" class="heading-l"></div>
@if (showButton() && this.action.observed) {
<iqser-icon-button
(action)="action.emit()"
[buttonId]="buttonId()"
[icon]="buttonIcon()"
[attr.help-mode-key]="helpModeKey()"
[label]="buttonLabel()"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
}
</div>

View File

@ -0,0 +1,42 @@
import { NgStyle } from '@angular/common';
import {
booleanAttribute,
ChangeDetectionStrategy,
Component,
computed,
EventEmitter,
input,
numberAttribute,
Output,
} from '@angular/core';
import { MatIconModule } from '@angular/material/icon';
import { IconButtonComponent } from '../buttons/icon-button/icon-button.component';
import { IconButtonTypes } from '../buttons/types/icon-button.type';
import { randomString } from '../utils/functions';
@Component({
selector: 'iqser-empty-state',
templateUrl: './empty-state.component.html',
styleUrls: ['./empty-state.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgStyle, MatIconModule, IconButtonComponent],
})
export class EmptyStateComponent {
protected readonly iconButtonTypes = IconButtonTypes;
readonly text = input.required<string>();
readonly icon = input<string>();
readonly showButton = input(true, { transform: booleanAttribute });
readonly buttonIcon = input('iqser:plus');
readonly buttonLabel = input<string>();
readonly buttonId = input(`${randomString()}-icon-button`);
readonly horizontalPadding = input(100, { transform: numberAttribute });
readonly verticalPadding = input(120, { transform: numberAttribute });
protected readonly styles = computed(() => ({
'padding-top': this.verticalPadding() + 'px',
'padding-left': this.horizontalPadding() + 'px',
'padding-right': this.horizontalPadding() + 'px',
}));
readonly helpModeKey = input<string>();
@Output() readonly action = new EventEmitter();
}

View File

@ -0,0 +1 @@
export * from './empty-state.component';

View File

@ -1,16 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { IqserIconsModule } from '../icons';
import { EmptyStateComponent } from './empty-state/empty-state.component';
import { IqserButtonsModule } from '../buttons';
import { IqserHelpModeModule } from '../help-mode';
const modules = [IqserIconsModule, IqserButtonsModule, IqserHelpModeModule];
const components = [EmptyStateComponent];
@NgModule({
declarations: [...components],
imports: [CommonModule, ...modules],
exports: [...components],
})
export class IqserEmptyStatesModule {}

View File

@ -1,22 +0,0 @@
<div
[ngStyle]="{
'padding-top': verticalPadding + 'px',
'padding-left': horizontalPadding + 'px',
'padding-right': horizontalPadding + 'px'
}"
class="empty-state"
>
<mat-icon *ngIf="icon" [svgIcon]="icon"></mat-icon>
<div class="ng-content-wrapper heading-l">
<ng-content></ng-content>
</div>
<div class="heading-l" [innerHTML]="text"></div>
<iqser-icon-button
(action)="action.emit()"
*ngIf="showButton"
[icon]="buttonIcon"
[label]="buttonLabel"
[type]="iconButtonTypes.primary"
[iqserHelpMode]="helpModeKey"
></iqser-icon-button>
</div>

View File

@ -1,26 +0,0 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { IconButtonTypes } from '../../buttons';
@Component({
selector: 'iqser-empty-state [text]',
templateUrl: './empty-state.component.html',
styleUrls: ['./empty-state.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class EmptyStateComponent implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
@Input() text!: string;
@Input() icon?: string;
@Input() showButton = true;
@Input() buttonIcon = 'iqser:plus';
@Input() buttonLabel?: string;
@Input() horizontalPadding = 100;
@Input() verticalPadding = 120;
@Input() helpModeKey?: string;
@Output() readonly action = new EventEmitter();
ngOnInit(): void {
this.showButton = this.showButton && this.action.observers.length > 0;
}
}

View File

@ -1,2 +0,0 @@
export * from './empty-state.module';
export * from './empty-state/empty-state.component';

View File

@ -1,8 +1,5 @@
<div
*ngIf="errorService.connectionStatus$ | async as status"
[@animateOpenClose]="status"
[ngClass]="status"
class="indicator flex-align-items-center"
>
<span [translate]="connectionStatusTranslations[status]"></span>
</div>
@if (connectionStatus(); as status) {
<div [@animateOpenClose]="status" [ngClass]="status" class="indicator flex-align-items-center">
<span [translate]="connectionStatusTranslations[status]"></span>
</div>
}

Some files were not shown because too many files have changed in this diff Show More