@truedat/core 8.14.3 → 8.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/core",
3
- "version": "8.14.3",
3
+ "version": "8.14.4",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -57,7 +57,7 @@
57
57
  "@testing-library/jest-dom": "^6.6.3",
58
58
  "@testing-library/react": "^16.3.0",
59
59
  "@testing-library/user-event": "^14.6.1",
60
- "@truedat/test": "8.14.3",
60
+ "@truedat/test": "8.14.4",
61
61
  "identity-obj-proxy": "^3.0.0",
62
62
  "jest": "^29.7.0",
63
63
  "redux-saga-test-plan": "^4.0.6"
@@ -99,5 +99,5 @@
99
99
  "swr": "^2.3.3",
100
100
  "turndown": "^7.2.2"
101
101
  },
102
- "gitHead": "ca5cc79e39ed78a1b5e317d825832e0676b011f6"
102
+ "gitHead": "3f043f28fd445df46a419f7872e23fe73e00e93d"
103
103
  }
@@ -1,7 +1,7 @@
1
1
  import _ from "lodash/fp";
2
2
  import { useState, useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
- import { FormattedMessage } from "react-intl";
4
+ import { FormattedMessage, useIntl } from "react-intl";
5
5
  import {
6
6
  Label,
7
7
  Icon,
@@ -25,6 +25,7 @@ export const FilterDropdown = ({
25
25
  removeFilter,
26
26
  toggleFilterValue,
27
27
  }) => {
28
+ const { formatMessage } = useIntl();
28
29
  const [selected, setSelected] = useState();
29
30
  const [query, setQuery] = useState();
30
31
 
@@ -64,6 +65,10 @@ export const FilterDropdown = ({
64
65
  return (
65
66
  <Dropdown
66
67
  item
68
+ aria-label={formatMessage({
69
+ id: `filters.${filter}`,
70
+ defaultMessage: removePrefix(filter),
71
+ })}
67
72
  floating
68
73
  scrolling
69
74
  icon={false}
@@ -76,6 +81,21 @@ export const FilterDropdown = ({
76
81
  />
77
82
  <Icon
78
83
  name="delete"
84
+ role="button"
85
+ tabIndex={0}
86
+ onFocus={(event) => event.stopPropagation()}
87
+ aria-hidden={false}
88
+ aria-label={formatMessage({
89
+ id: "actions.delete",
90
+ defaultMessage: "Delete",
91
+ })}
92
+ onKeyDown={(event) => {
93
+ if (event.key === "Enter" || event.key === " ") {
94
+ event.preventDefault();
95
+ event.stopPropagation();
96
+ removeFilter({ filter });
97
+ }
98
+ }}
79
99
  onClick={(e) => {
80
100
  e.preventDefault();
81
101
  e.stopPropagation();
@@ -33,6 +33,12 @@ export const FilterItem = ({
33
33
  option: { text, value },
34
34
  }) => (
35
35
  <Dropdown.Item
36
+ tabIndex={0}
37
+ onKeyDown={(event) => {
38
+ if (event.key === "Enter" || event.key === " ") {
39
+ preventDefault(event, () => toggleFilterValue({ filter, value }));
40
+ }
41
+ }}
36
42
  onClick={(e) =>
37
43
  preventDefault(e, () => toggleFilterValue({ filter, value }))
38
44
  }
@@ -1,19 +1,32 @@
1
- import { useEffect } from "react";
1
+ import { useEffect, useRef } from "react";
2
2
  import PropTypes from "prop-types";
3
3
  import { useLocation } from "react-router";
4
4
 
5
- function ScrollToTop({ children }) {
5
+ const NO_PRESERVED_ROUTES = [];
6
+ const withinRoute = (pathname, route) =>
7
+ pathname === route || pathname.startsWith(`${route}/`);
8
+
9
+ function ScrollToTop({ children, preserveWithin = NO_PRESERVED_ROUTES }) {
6
10
  const location = useLocation();
11
+ const previousLocation = useRef(null);
7
12
 
8
13
  useEffect(() => {
9
- window.scrollTo(0, 0);
10
- }, [location]);
14
+ const preserveScroll = preserveWithin.some(
15
+ (route) =>
16
+ previousLocation.current &&
17
+ withinRoute(previousLocation.current.pathname, route) &&
18
+ withinRoute(location.pathname, route),
19
+ );
20
+ if (!preserveScroll) window.scrollTo(0, 0);
21
+ previousLocation.current = location;
22
+ }, [location, preserveWithin]);
11
23
 
12
24
  return children;
13
25
  }
14
26
 
15
27
  ScrollToTop.propTypes = {
16
28
  children: PropTypes.node,
29
+ preserveWithin: PropTypes.arrayOf(PropTypes.string),
17
30
  };
18
31
 
19
32
  export default ScrollToTop;
@@ -1,6 +1,6 @@
1
1
  import _ from "lodash/fp";
2
2
  import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
3
- import { waitFor, within } from "@testing-library/react";
3
+ import { act, waitFor, within } from "@testing-library/react";
4
4
  import userEvent from "@testing-library/user-event";
5
5
 
6
6
  import { FilterDropdown } from "../FilterDropdown";
@@ -181,3 +181,20 @@ describe("<FilterDropdown/>", () => {
181
181
  });
182
182
  });
183
183
  });
184
+
185
+ it("removes the filter with the keyboard without opening it", async () => {
186
+ const removeFilter = jest.fn();
187
+ const openFilter = jest.fn();
188
+ const rendered = render(
189
+ <FilterDropdown
190
+ {...props}
191
+ options={null}
192
+ removeFilter={removeFilter}
193
+ openFilter={openFilter}
194
+ />,
195
+ );
196
+ act(() => rendered.getByRole("button", { name: "Delete" }).focus());
197
+ await user.keyboard("{Enter}");
198
+ expect(removeFilter).toHaveBeenCalledWith({ filter: "foo" });
199
+ expect(openFilter).not.toHaveBeenCalled();
200
+ });
@@ -1,5 +1,6 @@
1
1
  import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
2
  import { waitForLoad } from "@truedat/test/waitForLoad";
3
+ import userEvent from "@testing-library/user-event";
3
4
  import { FilterItem } from "../FilterItem";
4
5
 
5
6
  describe("<FilterItem/>", () => {
@@ -25,3 +26,23 @@ describe("<FilterItem/>", () => {
25
26
  expect(props.toggleFilterValue).toHaveBeenCalledTimes(1);
26
27
  });
27
28
  });
29
+
30
+ it("toggles an option with the keyboard", async () => {
31
+ const toggleFilterValue = jest.fn();
32
+ const rendered = render(
33
+ <FilterItem
34
+ filter="foo"
35
+ active={false}
36
+ option={{ value: "bar", text: "Bar" }}
37
+ toggleFilterValue={toggleFilterValue}
38
+ />,
39
+ );
40
+ const user = userEvent.setup({ delay: null });
41
+ rendered.getByRole("option").focus();
42
+ await user.keyboard("{Enter} ");
43
+ expect(toggleFilterValue).toHaveBeenCalledTimes(2);
44
+ expect(toggleFilterValue).toHaveBeenCalledWith({
45
+ filter: "foo",
46
+ value: "bar",
47
+ });
48
+ });
@@ -0,0 +1,54 @@
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
+ import { Link, MemoryRouter, useNavigate } from "react-router";
3
+ import ScrollToTop from "../ScrollToTop";
4
+
5
+ const Navigation = () => {
6
+ const navigate = useNavigate();
7
+ return (
8
+ <>
9
+ <Link to="/users/1">User</Link>
10
+ <Link to="/users/1/edit_password">Password</Link>
11
+ <Link to="/users">Close</Link>
12
+ <Link to="/groups">Groups</Link>
13
+ <Link to="/users-other">Other</Link>
14
+ <button onClick={() => navigate(-1)}>Back</button>
15
+ </>
16
+ );
17
+ };
18
+
19
+ it("preserves scroll between users and their modals, including browser back", () => {
20
+ const scroll = jest.spyOn(window, "scrollTo").mockImplementation(() => {});
21
+ render(
22
+ <MemoryRouter initialEntries={["/users"]}>
23
+ <ScrollToTop preserveWithin={["/users"]}>
24
+ <Navigation />
25
+ </ScrollToTop>
26
+ </MemoryRouter>,
27
+ );
28
+ expect(scroll).toHaveBeenCalledTimes(1);
29
+ ["User", "Password", "Close", "Back"].forEach((name) =>
30
+ fireEvent.click(screen.getByText(name)),
31
+ );
32
+ expect(scroll).toHaveBeenCalledTimes(1);
33
+ fireEvent.click(screen.getByText("Groups"));
34
+ expect(scroll).toHaveBeenCalledTimes(2);
35
+ fireEvent.click(screen.getByText("Close"));
36
+ expect(scroll).toHaveBeenCalledTimes(3);
37
+ fireEvent.click(screen.getByText("Other"));
38
+ expect(scroll).toHaveBeenCalledTimes(4);
39
+ scroll.mockRestore();
40
+ });
41
+
42
+ it("retains the default scroll reset behavior without preserved routes", () => {
43
+ const scroll = jest.spyOn(window, "scrollTo").mockImplementation(() => {});
44
+ render(
45
+ <MemoryRouter initialEntries={["/users"]}>
46
+ <ScrollToTop>
47
+ <Navigation />
48
+ </ScrollToTop>
49
+ </MemoryRouter>,
50
+ );
51
+ fireEvent.click(screen.getByText("User"));
52
+ expect(scroll).toHaveBeenCalledTimes(2);
53
+ scroll.mockRestore();
54
+ });
@@ -4,6 +4,7 @@ exports[`<FilterDropdown/> matches the latest snapshot 1`] = `
4
4
  <div>
5
5
  <div
6
6
  aria-expanded="true"
7
+ aria-label="foo"
7
8
  class="ui active visible floating item scrolling dropdown"
8
9
  role="listbox"
9
10
  tabindex="0"
@@ -13,8 +14,11 @@ exports[`<FilterDropdown/> matches the latest snapshot 1`] = `
13
14
  >
14
15
  foo
15
16
  <i
16
- aria-hidden="true"
17
+ aria-hidden="false"
18
+ aria-label="Delete"
17
19
  class="delete icon"
20
+ role="button"
21
+ tabindex="0"
18
22
  />
19
23
  </div>
20
24
  <div
@@ -24,6 +28,7 @@ exports[`<FilterDropdown/> matches the latest snapshot 1`] = `
24
28
  aria-checked="false"
25
29
  class="item"
26
30
  role="option"
31
+ tabindex="0"
27
32
  >
28
33
  <i
29
34
  aria-hidden="true"
@@ -35,6 +40,7 @@ exports[`<FilterDropdown/> matches the latest snapshot 1`] = `
35
40
  aria-checked="false"
36
41
  class="item"
37
42
  role="option"
43
+ tabindex="0"
38
44
  >
39
45
  <i
40
46
  aria-hidden="true"
@@ -6,6 +6,7 @@ exports[`<FilterItem/> matches the latest snapshot 1`] = `
6
6
  aria-checked="true"
7
7
  class="active item"
8
8
  role="option"
9
+ tabindex="0"
9
10
  >
10
11
  <i
11
12
  aria-hidden="true"
@@ -38,6 +38,7 @@ exports[`<SelectedFilters/> matches the latest snapshot 1`] = `
38
38
  </div>
39
39
  <div
40
40
  aria-expanded="true"
41
+ aria-label="foo"
41
42
  class="ui active visible floating item scrolling dropdown"
42
43
  role="listbox"
43
44
  tabindex="0"
@@ -47,8 +48,11 @@ exports[`<SelectedFilters/> matches the latest snapshot 1`] = `
47
48
  >
48
49
  foo
49
50
  <i
50
- aria-hidden="true"
51
+ aria-hidden="false"
52
+ aria-label="Delete"
51
53
  class="delete icon"
54
+ role="button"
55
+ tabindex="0"
52
56
  />
53
57
  </div>
54
58
  <div
@@ -58,6 +62,7 @@ exports[`<SelectedFilters/> matches the latest snapshot 1`] = `
58
62
  aria-checked="false"
59
63
  class="item"
60
64
  role="option"
65
+ tabindex="0"
61
66
  >
62
67
  <i
63
68
  aria-hidden="true"
@@ -71,6 +76,7 @@ exports[`<SelectedFilters/> matches the latest snapshot 1`] = `
71
76
  aria-checked="false"
72
77
  class="item"
73
78
  role="option"
79
+ tabindex="0"
74
80
  >
75
81
  <i
76
82
  aria-hidden="true"
@@ -84,6 +90,7 @@ exports[`<SelectedFilters/> matches the latest snapshot 1`] = `
84
90
  </div>
85
91
  <div
86
92
  aria-expanded="false"
93
+ aria-label="bar"
87
94
  class="ui floating item scrolling dropdown"
88
95
  role="listbox"
89
96
  tabindex="0"
@@ -93,8 +100,11 @@ exports[`<SelectedFilters/> matches the latest snapshot 1`] = `
93
100
  >
94
101
  bar
95
102
  <i
96
- aria-hidden="true"
103
+ aria-hidden="false"
104
+ aria-label="Delete"
97
105
  class="delete icon"
106
+ role="button"
107
+ tabindex="0"
98
108
  />
99
109
  </div>
100
110
  <div
@@ -144,6 +154,7 @@ exports[`<SelectedFilters/> taxonomy screens matches the latest snapshot 1`] = `
144
154
  </div>
145
155
  <div
146
156
  aria-expanded="true"
157
+ aria-label="foo"
147
158
  class="ui active visible floating item scrolling dropdown"
148
159
  role="listbox"
149
160
  tabindex="0"
@@ -153,8 +164,11 @@ exports[`<SelectedFilters/> taxonomy screens matches the latest snapshot 1`] = `
153
164
  >
154
165
  foo
155
166
  <i
156
- aria-hidden="true"
167
+ aria-hidden="false"
168
+ aria-label="Delete"
157
169
  class="delete icon"
170
+ role="button"
171
+ tabindex="0"
158
172
  />
159
173
  </div>
160
174
  <div
@@ -164,6 +178,7 @@ exports[`<SelectedFilters/> taxonomy screens matches the latest snapshot 1`] = `
164
178
  aria-checked="false"
165
179
  class="item"
166
180
  role="option"
181
+ tabindex="0"
167
182
  >
168
183
  <i
169
184
  aria-hidden="true"
@@ -177,6 +192,7 @@ exports[`<SelectedFilters/> taxonomy screens matches the latest snapshot 1`] = `
177
192
  aria-checked="false"
178
193
  class="item"
179
194
  role="option"
195
+ tabindex="0"
180
196
  >
181
197
  <i
182
198
  aria-hidden="true"
@@ -190,6 +206,7 @@ exports[`<SelectedFilters/> taxonomy screens matches the latest snapshot 1`] = `
190
206
  </div>
191
207
  <div
192
208
  aria-expanded="false"
209
+ aria-label="bar"
193
210
  class="ui floating item scrolling dropdown"
194
211
  role="listbox"
195
212
  tabindex="0"
@@ -199,8 +216,11 @@ exports[`<SelectedFilters/> taxonomy screens matches the latest snapshot 1`] = `
199
216
  >
200
217
  bar
201
218
  <i
202
- aria-hidden="true"
219
+ aria-hidden="false"
220
+ aria-label="Delete"
203
221
  class="delete icon"
222
+ role="button"
223
+ tabindex="0"
204
224
  />
205
225
  </div>
206
226
  <div
@@ -82,7 +82,7 @@ const LanguageContextProvider = ({ children, defaultMessages }) => {
82
82
 
83
83
  const getMessagesForLang = (requestedLang) => {
84
84
  if (loading || !requestedLang) return null;
85
- return messages[requestedLang] || defaultMessages[requestedLang];
85
+ return { ...defaultMessages[requestedLang], ...messages[requestedLang] };
86
86
  };
87
87
 
88
88
  const context = {
@@ -0,0 +1,44 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { FormattedMessage } from "react-intl";
3
+ import { useLocales } from "../../../hooks/useLocales";
4
+ import { LangProvider, useLanguage } from "../LangProvider";
5
+
6
+ jest.mock("../../../hooks/useLocales", () => ({ useLocales: jest.fn() }));
7
+
8
+ const defaults = {
9
+ en: { existing: "Local", added: "New local message" },
10
+ es: { added: "Mensaje local" },
11
+ };
12
+ const Catalogs = () => {
13
+ const { getMessagesForLang } = useLanguage();
14
+ return (
15
+ <>
16
+ <FormattedMessage id="existing" />
17
+ <FormattedMessage id="added" />
18
+ <output>{JSON.stringify(getMessagesForLang("es"))}</output>
19
+ </>
20
+ );
21
+ };
22
+
23
+ it("fills missing keys locally while preserving remote translations", () => {
24
+ useLocales.mockReturnValue({
25
+ locales: [
26
+ {
27
+ lang: "en",
28
+ is_default: true,
29
+ is_enabled: true,
30
+ messages: [{ message_id: "existing", definition: "Remote" }],
31
+ },
32
+ ],
33
+ loading: false,
34
+ });
35
+ render(
36
+ <LangProvider defaultMessages={defaults}>
37
+ <Catalogs />
38
+ </LangProvider>,
39
+ );
40
+ expect(screen.getByText(/Remote/)).toHaveTextContent("New local message");
41
+ expect(screen.getByRole("status")).toHaveTextContent(
42
+ '{"added":"Mensaje local"}',
43
+ );
44
+ });
@@ -1,4 +1,5 @@
1
1
  export default {
2
+ "actions.close": "Close",
2
3
  actions: "Actions",
3
4
  "actions.cancel": "Cancel",
4
5
  "actions.confirm": "Are you sure?",
@@ -1,4 +1,5 @@
1
1
  export default {
2
+ "actions.close": "Cerrar",
2
3
  actions: "Acciones",
3
4
  "actions.cancel": "Cancelar",
4
5
  "actions.confirm": "¿Estás seguro?",
@@ -0,0 +1,160 @@
1
+ :root {
2
+ --card-groups-popup-background: var(--td-card-accent, #002057);
3
+ --card-groups-popup-text-color: #ffffff;
4
+ --card-groups-popup-muted-text-color: rgba(255, 255, 255, 0.86);
5
+ --card-groups-popup-border-color: rgba(255, 255, 255, 0.24);
6
+ --card-groups-popup-header-background: rgba(255, 255, 255, 0.12);
7
+ --card-groups-popup-item-background: rgba(255, 255, 255, 0.14);
8
+ --card-groups-popup-item-border-color: rgba(255, 255, 255, 0.18);
9
+ --card-groups-popup-marker-background: rgba(255, 255, 255, 0.24);
10
+ --card-groups-popup-marker-color: #ffffff;
11
+ }
12
+
13
+ .ui.card {
14
+ --card-accent-color: var(--td-card-accent, #002057);
15
+ --card-accent-70: var(--td-card-accent-70, rgba(0, 32, 87, 0.7));
16
+ --card-border-color: #edf2f7;
17
+ --card-elevation-shadow: 0 12px 28px -10px rgba(15, 23, 42, 0.28);
18
+ --card-motion-duration: 300ms;
19
+ --card-motion-easing: cubic-bezier(0.22, 1, 0.36, 1);
20
+ border: 1px solid var(--card-border-color);
21
+ border-radius: 1.5rem;
22
+ transition:
23
+ transform var(--card-motion-duration) var(--card-motion-easing),
24
+ box-shadow var(--card-motion-duration) var(--card-motion-easing);
25
+ overflow: hidden;
26
+ }
27
+
28
+ .ui.cards > .card {
29
+ --card-border-color: #edf2f7;
30
+ --card-elevation-shadow: 0 12px 28px -10px rgba(15, 23, 42, 0.28);
31
+ --card-motion-duration: 300ms;
32
+ --card-motion-easing: cubic-bezier(0.22, 1, 0.36, 1);
33
+ border: 1px solid var(--card-border-color);
34
+ margin: 0.875em 0.5em;
35
+ }
36
+
37
+ /*******************************
38
+ Unified Card Base
39
+ *******************************/
40
+
41
+ .ui.card.td-card-base {
42
+ --card-accent-color: var(--td-card-accent, #002057);
43
+ --card-accent-shadow: var(--td-card-accent-shadow, rgba(0, 32, 87, 0.18));
44
+ --card-accent-soft: var(--td-card-accent-soft, rgba(0, 32, 87, 0.12));
45
+ --card-accent-70: var(--td-card-accent-70, rgba(0, 32, 87, 0.7));
46
+ --card-secondary-color: var(--td-card-secondary, #ed5c17);
47
+ --card-motion-duration: 300ms;
48
+ --card-motion-easing: cubic-bezier(0.22, 1, 0.36, 1);
49
+
50
+ position: relative;
51
+ overflow: hidden;
52
+ background: #ffffff;
53
+ border: 1px solid var(--card-border-color);
54
+ border-radius: var(--uc-top-left-radius, 1.5rem)
55
+ var(--uc-top-right-radius, 1.5rem) var(--uc-bottom-right-radius, 1.5rem)
56
+ var(--uc-bottom-left-radius, 1.5rem);
57
+ box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
58
+ transition:
59
+ transform var(--card-motion-duration) var(--card-motion-easing),
60
+ box-shadow var(--card-motion-duration) var(--card-motion-easing),
61
+ border-color var(--card-motion-duration) var(--card-motion-easing);
62
+ will-change: transform;
63
+ }
64
+
65
+ .ui.card.td-card-base:hover,
66
+ .ui.card.td-card-base:focus-within {
67
+ border: 1px solid var(--card-border-color);
68
+ box-shadow: var(--card-elevation-shadow);
69
+ transform: translateY(-4px);
70
+ }
71
+
72
+ .ui.card.td-card-base:active {
73
+ transform: translateY(-1px);
74
+ }
75
+
76
+ .ui.card.td-card-base:focus-within {
77
+ outline: 2px solid var(--card-accent-soft);
78
+ outline-offset: 2px;
79
+ }
80
+
81
+ .ui.card.td-card-base > .hover-indicator {
82
+ display: none;
83
+ }
84
+
85
+ .ui.card.td-card-base.td-card--structure,
86
+ .ui.card.td-card-base.td-card--user-role {
87
+ width: 100%;
88
+ max-width: 300px;
89
+ height: auto;
90
+ min-height: 130px;
91
+ }
92
+
93
+ .ui.card.td-card-base.td-card--user {
94
+ width: 100%;
95
+ max-width: 330px;
96
+ height: auto;
97
+ min-height: 128px;
98
+ }
99
+
100
+ .ui.card.td-card-base.td-card--group {
101
+ width: 100%;
102
+ max-width: 300px;
103
+ height: auto;
104
+ min-height: 112px;
105
+ }
106
+
107
+ .ui.card.td-card-base.td-card--taxonomy {
108
+ width: 100%;
109
+ max-width: 290px;
110
+ height: auto;
111
+ min-height: 310px;
112
+ }
113
+
114
+ .ui.card.td-card-base.td-card--domain {
115
+ width: 100%;
116
+ max-width: 290px;
117
+ height: auto;
118
+ min-height: 240px;
119
+ }
120
+
121
+ @media (min-width: 768px) {
122
+ .ui.card.td-card-base.td-card--structure,
123
+ .ui.card.td-card-base.td-card--user-role {
124
+ width: 300px;
125
+ height: 130px;
126
+ }
127
+
128
+ .ui.card.td-card-base.td-card--user {
129
+ width: 330px;
130
+ height: auto;
131
+ min-height: 128px;
132
+ }
133
+
134
+ .ui.card.td-card-base.td-card--group {
135
+ width: 300px;
136
+ height: auto;
137
+ min-height: 112px;
138
+ }
139
+
140
+ .ui.card.td-card-base.td-card--taxonomy {
141
+ width: 290px;
142
+ height: 310px;
143
+ }
144
+
145
+ .ui.card.td-card-base.td-card--domain {
146
+ width: 290px;
147
+ height: 240px;
148
+ }
149
+ }
150
+
151
+ .ui.card:hover,
152
+ .ui.card:focus-within,
153
+ .ui.cards > .card:hover,
154
+ .ui.cards > .card:focus-within,
155
+ .ui.cards > .card.create.link:hover,
156
+ .ui.cards > .card.create.link:focus-within {
157
+ border: 1px solid var(--card-border-color);
158
+ box-shadow: var(--card-elevation-shadow);
159
+ transform: translateY(-4px);
160
+ }
@@ -30,3 +30,9 @@
30
30
  line-height: 1.2;
31
31
  }
32
32
  }
33
+
34
+ .button:not(.icon):not(.circular) > .icon {
35
+ height: auto;
36
+ line-height: 1;
37
+ flex-shrink: 0;
38
+ }
@@ -0,0 +1,303 @@
1
+ :root {
2
+ --td-danger: #e42135;
3
+ }
4
+
5
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal),
6
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) {
7
+ width: min(560px, calc(100vw - 48px));
8
+ border-radius: 18px;
9
+ overflow: visible;
10
+ box-shadow: 0 24px 70px rgba(15, 23, 42, 0.32);
11
+ }
12
+
13
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .close.icon,
14
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .close.icon {
15
+ top: 22px;
16
+ right: 24px;
17
+ width: 32px;
18
+ height: 32px;
19
+ padding: 8px 0 0 0;
20
+ border-radius: 8px;
21
+ color: #8aa0bd;
22
+ text-shadow: none;
23
+ opacity: 1;
24
+ transition:
25
+ color 0.15s ease,
26
+ background-color 0.15s ease;
27
+ }
28
+
29
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
30
+ > .close.icon:hover,
31
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .close.icon:hover,
32
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
33
+ > .close.icon:focus,
34
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
35
+ > .close.icon:focus {
36
+ background: #f1f5f9;
37
+ color: #334155;
38
+ }
39
+
40
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .header,
41
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .header {
42
+ min-height: 80px;
43
+ padding: 20px 58px 20px 28px;
44
+ border-bottom: 1px solid #e8edf5;
45
+ border-radius: 18px 18px 0 0;
46
+ background: #ffffff;
47
+ color: #1e293b;
48
+ font-size: 22px;
49
+ font-weight: 900;
50
+ line-height: 1.15;
51
+ letter-spacing: 0;
52
+ text-transform: uppercase;
53
+ }
54
+
55
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
56
+ > .header
57
+ .subheader,
58
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
59
+ > .header
60
+ .subheader {
61
+ margin-top: 8px;
62
+ color: #64748b;
63
+ font-size: 12px;
64
+ font-weight: 700;
65
+ line-height: 1.35;
66
+ text-transform: none;
67
+ }
68
+
69
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .content,
70
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .content {
71
+ padding: 30px;
72
+ background: #ffffff;
73
+ color: #50627a;
74
+ font-size: 14px;
75
+ font-weight: 600;
76
+ line-height: 1.5;
77
+ white-space: pre-line;
78
+ }
79
+
80
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .content i,
81
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .content i {
82
+ display: inline-flex;
83
+ align-items: center;
84
+ max-width: 100%;
85
+ min-height: 28px;
86
+ margin: 0 4px;
87
+ padding: 5px 10px;
88
+ border: 1px solid #dbe4ef;
89
+ border-radius: 7px;
90
+ background: #eef3f8;
91
+ color: #002057;
92
+ font-style: normal;
93
+ font-weight: 900;
94
+ line-height: 1.2;
95
+ overflow-wrap: anywhere;
96
+ vertical-align: middle;
97
+ }
98
+
99
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .actions,
100
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .actions {
101
+ min-height: 0;
102
+ padding: 0;
103
+ border-top: 1px solid #e8edf5;
104
+ border-radius: 0 0 18px 18px;
105
+ background: #ffffff;
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: flex-end;
109
+ gap: 8px;
110
+ }
111
+
112
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .actions::after,
113
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .actions::after {
114
+ display: none;
115
+ }
116
+
117
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
118
+ > .actions
119
+ .ui.button,
120
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
121
+ > .actions
122
+ .ui.button {
123
+ min-width: 132px;
124
+ min-height: 48px;
125
+ margin: 10px 0 0;
126
+ border-radius: 4px;
127
+ box-shadow: none;
128
+ font-size: 12px;
129
+ font-weight: 900;
130
+ letter-spacing: 0.02em;
131
+ text-transform: uppercase;
132
+ transition:
133
+ background-color 0.15s ease,
134
+ border-color 0.15s ease,
135
+ color 0.15s ease;
136
+ }
137
+
138
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
139
+ > .actions
140
+ .ui.secondary.button,
141
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
142
+ > .actions
143
+ .ui.secondary.button,
144
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
145
+ > .actions
146
+ .ui.button:not(.primary):not(.positive):not(.negative),
147
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
148
+ > .actions
149
+ .ui.button:not(.primary):not(.positive):not(.negative) {
150
+ border: 1px solid #d8e1ed;
151
+ background: #ffffff;
152
+ color: #64748b;
153
+ }
154
+
155
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
156
+ > .actions
157
+ .ui.secondary.button:hover,
158
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
159
+ > .actions
160
+ .ui.secondary.button:hover,
161
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
162
+ > .actions
163
+ .ui.secondary.button:focus,
164
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
165
+ > .actions
166
+ .ui.secondary.button:focus,
167
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
168
+ > .actions
169
+ .ui.button:not(.primary):not(.positive):not(.negative):hover,
170
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
171
+ > .actions
172
+ .ui.button:not(.primary):not(.positive):not(.negative):hover,
173
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
174
+ > .actions
175
+ .ui.button:not(.primary):not(.positive):not(.negative):focus,
176
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
177
+ > .actions
178
+ .ui.button:not(.primary):not(.positive):not(.negative):focus {
179
+ border-color: #bdc9d8;
180
+ background: #f8fafc;
181
+ color: #475569;
182
+ }
183
+
184
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
185
+ > .actions
186
+ .ui.primary.button,
187
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
188
+ > .actions
189
+ .ui.primary.button,
190
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
191
+ > .actions
192
+ .ui.positive.button,
193
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
194
+ > .actions
195
+ .ui.positive.button {
196
+ background: #002057;
197
+ color: #ffffff;
198
+ }
199
+
200
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
201
+ > .actions
202
+ .ui.primary.button:hover,
203
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
204
+ > .actions
205
+ .ui.primary.button:hover,
206
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
207
+ > .actions
208
+ .ui.primary.button:focus,
209
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
210
+ > .actions
211
+ .ui.primary.button:focus,
212
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
213
+ > .actions
214
+ .ui.positive.button:hover,
215
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
216
+ > .actions
217
+ .ui.positive.button:hover,
218
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
219
+ > .actions
220
+ .ui.positive.button:focus,
221
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
222
+ > .actions
223
+ .ui.positive.button:focus {
224
+ background: #001840;
225
+ }
226
+
227
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
228
+ > .actions
229
+ .ui.negative.button,
230
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
231
+ > .actions
232
+ .ui.negative.button {
233
+ background: #ed5c17;
234
+ color: #ffffff;
235
+ }
236
+
237
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
238
+ > .actions
239
+ .ui.negative.button:hover,
240
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
241
+ > .actions
242
+ .ui.negative.button:hover,
243
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
244
+ > .actions
245
+ .ui.negative.button:focus,
246
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
247
+ > .actions
248
+ .ui.negative.button:focus {
249
+ background: #d94f12;
250
+ }
251
+
252
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
253
+ > .actions
254
+ .ui.disabled.button,
255
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
256
+ > .actions
257
+ .ui.disabled.button,
258
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
259
+ > .actions
260
+ .ui.button:disabled,
261
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
262
+ > .actions
263
+ .ui.button:disabled {
264
+ cursor: not-allowed;
265
+ opacity: 0.55;
266
+ }
267
+
268
+ @media only screen and (max-width: 767px) {
269
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal),
270
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) {
271
+ width: calc(100vw - 24px);
272
+ }
273
+
274
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .header,
275
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .header {
276
+ min-height: 72px;
277
+ padding: 18px 54px 18px 20px !important;
278
+ font-size: 18px;
279
+ }
280
+
281
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .content,
282
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .content {
283
+ padding: 22px 20px !important;
284
+ }
285
+
286
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal) > .actions,
287
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal) > .actions {
288
+ min-height: auto;
289
+ padding: 0 !important;
290
+ flex-direction: column-reverse;
291
+ align-items: stretch;
292
+ }
293
+
294
+ .ui.small.modal:not(.user-modal):not(.execution-popup-modal)
295
+ > .actions
296
+ .ui.button,
297
+ .ui.mini.modal:not(.user-modal):not(.execution-popup-modal)
298
+ > .actions
299
+ .ui.button {
300
+ width: 100%;
301
+ margin: 0 !important;
302
+ }
303
+ }