@design-system-rte/angular 0.2.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +8 -5
  3. package/src/lib/components/button/button.component.scss +11 -13
  4. package/src/lib/components/button/button.component.ts +2 -1
  5. package/src/lib/components/checkbox/checkbox.component.scss +18 -12
  6. package/src/lib/components/checkbox-group/checkbox-group.component.scss +3 -3
  7. package/src/lib/components/icon/icon-map.ts +4 -0
  8. package/src/lib/components/icon/icon.stories.ts +1 -1
  9. package/src/lib/components/icon-button/icon-button.component.html +16 -0
  10. package/src/lib/components/icon-button/icon-button.component.scss +165 -0
  11. package/src/lib/components/icon-button/icon-button.component.ts +40 -0
  12. package/src/lib/components/icon-button/icon-button.stories.ts +200 -0
  13. package/src/lib/components/icon-button-toggle/icon-button-toggle.component.html +12 -0
  14. package/src/lib/components/icon-button-toggle/icon-button-toggle.component.ts +36 -0
  15. package/src/lib/components/icon-button-toggle/icon-button-toggle.stories.ts +197 -0
  16. package/src/lib/components/link/link.component.scss +37 -37
  17. package/src/lib/components/radio-button/radio-button.component.scss +2 -2
  18. package/src/lib/components/radio-button-group/radio-button-group.component.scss +3 -3
  19. package/src/lib/components/split-button/split-button.component.html +56 -0
  20. package/src/lib/components/split-button/split-button.component.scss +288 -0
  21. package/src/lib/components/split-button/split-button.component.stories.ts +227 -0
  22. package/src/lib/components/split-button/split-button.component.ts +55 -0
  23. package/src/lib/components/tooltip/tooltip.component.scss +2 -4
  24. package/tsconfig.lib.json +0 -2
@@ -0,0 +1,288 @@
1
+ @use '@design-system-rte/core/design-tokens/main.scss' as *;
2
+
3
+ .split-button-container {
4
+ display: inline-flex;
5
+ padding: $positive-spacing_0;
6
+ align-items: center;
7
+ gap: $positive-spacing_0;
8
+ align-self: stretch;
9
+
10
+ &.size-s {
11
+ height: 24px;
12
+
13
+ &.compact-spacing {
14
+ height: 20px;
15
+ }
16
+ }
17
+
18
+ &.size-m {
19
+ height: 32px;
20
+
21
+ &.compact-spacing {
22
+ height: 24px;
23
+ }
24
+ }
25
+
26
+ &.size-l {
27
+ height: 40px;
28
+
29
+ &.compact-spacing {
30
+ height: 28px;
31
+ }
32
+ }
33
+
34
+ &.secondary {
35
+ .split-button-left {
36
+ border-top: 1px solid var(--border-brand-default);
37
+ border-bottom: 1px solid var(--border-brand-default);
38
+ border-left: 1px solid var(--border-brand-default);
39
+ background-color: var(--background-default);
40
+ color: var(--content-brand-default);
41
+
42
+ &:hover {
43
+ cursor: pointer;
44
+ background-color: var(--background-brand-inverse-hover);
45
+ }
46
+
47
+ &:active {
48
+ background-color: var(--background-brand-inverse-pressed);
49
+ }
50
+
51
+ &:disabled {
52
+ background-color: var(--background-disabled);
53
+ color: var(--content-disabled);
54
+ border-top: 1px solid var(--border-disabled);
55
+ border-bottom: 1px solid var(--border-disabled);
56
+ border-left: 1px solid var(--border-disabled);
57
+ cursor: not-allowed;
58
+ }
59
+ }
60
+
61
+ .split-button-divider {
62
+ background-color: var(--border-brand-default);
63
+ }
64
+
65
+ .split-button-right-container {
66
+ .split-button-right {
67
+ border-top: 1px solid var(--border-brand-default);
68
+ border-bottom: 1px solid var(--border-brand-default);
69
+ border-right: 1px solid var(--border-brand-default);
70
+ background-color: var(--background-default);
71
+ color: var(--content-brand-default);
72
+
73
+ &:hover {
74
+ cursor: pointer;
75
+ background-color: var(--background-brand-inverse-hover);
76
+ }
77
+
78
+ &:active {
79
+ background-color: var(--background-brand-inverse-pressed);
80
+ }
81
+
82
+ &:disabled {
83
+ background-color: var(--background-disabled);
84
+ color: var(--content-disabled);
85
+ border-top: 1px solid var(--border-disabled);
86
+ border-bottom: 1px solid var(--border-disabled);
87
+ border-right: 1px solid var(--border-disabled);
88
+ cursor: not-allowed;
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ .split-button-left {
95
+ display: flex;
96
+ border: none;
97
+ padding: $positive-spacing_0 $positive-spacing_100;
98
+ align-items: center;
99
+ gap: $positive-spacing_0;
100
+ align-self: stretch;
101
+ border-radius: $radius-s $radius-none $radius-none $radius-s;
102
+ border-color: var(--border-brand-default);
103
+ background-color: var(--background-brand-default);
104
+ color: var(--content-primary-inverse);
105
+
106
+ .split-button-label {
107
+ margin: 0;
108
+
109
+ &.size-s {
110
+ @include typography-split-button-s;
111
+ }
112
+
113
+ &.size-m {
114
+ @include typography-split-button-m;
115
+ }
116
+
117
+ &.size-l {
118
+ @include typography-split-button-l;
119
+ }
120
+ }
121
+
122
+ &:hover {
123
+ cursor: pointer;
124
+ background-color: var(--background-brand-hover);
125
+ }
126
+
127
+ &:active {
128
+ background-color: var(--background-brand-pressed);
129
+ }
130
+
131
+ &:disabled {
132
+ background-color: var(--background-disabled);
133
+ color: var(--content-disabled);
134
+ border-top: 1px solid var(--border-disabled);
135
+ border-bottom: 1px solid var(--border-disabled);
136
+ border-left: 1px solid var(--border-disabled);
137
+ cursor: not-allowed;
138
+ }
139
+
140
+ &:focus {
141
+ outline: none;
142
+ position: relative;
143
+ z-index: 1;
144
+
145
+ &::after {
146
+ content: '';
147
+ position: absolute;
148
+ top: -8px;
149
+ left: -8px;
150
+ bottom: -8px;
151
+ right: -1px;
152
+ border: 1px solid var(--border-brand-focused);
153
+ pointer-events: none;
154
+ border-radius: $radius-s;
155
+ z-index: 2;
156
+ }
157
+ }
158
+ }
159
+
160
+ .split-button-divider {
161
+ width: 1px;
162
+ height: 100%;
163
+ background-color: var(--content-primary-inverse);
164
+
165
+ &.disabled {
166
+ background-color: var(--border-disabled);
167
+ }
168
+ }
169
+
170
+ .split-button-right-container {
171
+ display: flex;
172
+ position: relative;
173
+ padding: $positive-spacing_0;
174
+ align-items: center;
175
+ justify-content: center;
176
+ gap: $positive-spacing_0;
177
+ align-self: stretch;
178
+
179
+ &.size-s {
180
+ width: 24px;
181
+ }
182
+
183
+ &.size-m {
184
+ width: 34px;
185
+ }
186
+
187
+ &.size-l {
188
+ width: 44px;
189
+ }
190
+
191
+ .split-button-right {
192
+ display: flex;
193
+ border: none;
194
+ padding: $positive-spacing_075 $positive-spacing_200;
195
+ align-items: center;
196
+ justify-content: center;
197
+ align-self: stretch;
198
+
199
+ border-radius: $radius-none $radius-s $radius-s $radius-none;
200
+ border-color: var(--border-brand-default);
201
+ background-color: var(--background-brand-default);
202
+
203
+ color: var(--content-primary-inverse);
204
+
205
+ &:hover {
206
+ cursor: pointer;
207
+ background-color: var(--background-brand-hover);
208
+ }
209
+
210
+ &:active {
211
+ background-color: var(--background-brand-pressed);
212
+ }
213
+
214
+ &:disabled {
215
+ background-color: var(--background-disabled);
216
+ color: var(--content-disabled);
217
+ border-top: 1px solid var(--border-disabled);
218
+ border-bottom: 1px solid var(--border-disabled);
219
+ border-right: 1px solid var(--border-disabled);
220
+ cursor: not-allowed;
221
+ }
222
+
223
+ &:focus {
224
+ outline: none;
225
+ position: relative;
226
+ z-index: 1;
227
+
228
+ &::after {
229
+ content: '';
230
+ position: absolute;
231
+ top: -8px;
232
+ left: -1px;
233
+ bottom: -8px;
234
+ right: -8px;
235
+ border: 1px solid var(--border-brand-focused);
236
+ pointer-events: none;
237
+ border-radius: $radius-s;
238
+ z-index: 2;
239
+ }
240
+ }
241
+
242
+ .split-button-right-icon-container {
243
+ pointer-events: none;
244
+ }
245
+ }
246
+
247
+ .split-button-dropdown {
248
+ position: absolute;
249
+
250
+ &.position-bottom-start {
251
+ top: 100%;
252
+ left: 0;
253
+ }
254
+
255
+ &.position-bottom-end {
256
+ top: 100%;
257
+ right: 0;
258
+ }
259
+
260
+ &.position-top-start {
261
+ bottom: 100%;
262
+ left: 0;
263
+ }
264
+
265
+ &.position-top-end {
266
+ bottom: 100%;
267
+ right: 0;
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ .animation-slide-from-top {
274
+ animation: slide-from-top 0.2s ease-in-out;
275
+ }
276
+
277
+ @keyframes slide-from-top {
278
+ from {
279
+ transform: translateY(-10px);
280
+ pointer-events: none;
281
+ opacity: 0;
282
+ }
283
+
284
+ to {
285
+ transform: translateY(0);
286
+ opacity: 1;
287
+ }
288
+ }
@@ -0,0 +1,227 @@
1
+ import { DOWN_KEY } from "@design-system-rte/core/constants/keyboard.constants";
2
+ import { Meta, StoryObj } from "@storybook/angular";
3
+ import { expect, userEvent, waitFor, within } from "@storybook/test";
4
+
5
+ import { SplitButtonComponent } from "./split-button.component";
6
+
7
+ const mockChildren = `
8
+ <div style="display: flex; flex-direction: column; gap: 8px; min-width: 120px">
9
+ <button
10
+ style="
11
+ padding: 8px 16px;
12
+ border: none;
13
+ border-radius: 4px;
14
+ background: #2563eb;
15
+ color: #fff;
16
+ font-weight: 500;
17
+ font-size: 15px;
18
+ cursor: pointer;
19
+ "
20
+ >
21
+ Action 1
22
+ </button>
23
+ <button
24
+ style="
25
+ padding: 8px 16px;
26
+ border: none;
27
+ border-radius: 4px;
28
+ background: #64748b;
29
+ color: #fff;
30
+ font-weight: 500;
31
+ font-size: 15px;
32
+ cursor: pointer;
33
+ "
34
+ >
35
+ Action 2
36
+ </button>
37
+ </div>
38
+ `;
39
+
40
+ function generateSplitButtonElement(props: Record<string, string> = {}): string {
41
+ const defaultProps = {
42
+ appearance: "appearance",
43
+ label: "label",
44
+ position: "position",
45
+ disabled: "disabled",
46
+ ariaLabelRight: "ariaLabelRight",
47
+ ...props,
48
+ };
49
+
50
+ const attributes = Object.entries(defaultProps)
51
+ .map(([key, value]) => `[${key}]="${value}"`)
52
+ .join("\n");
53
+
54
+ return `
55
+ <rte-split-button
56
+ ${attributes}
57
+ >
58
+ ${mockChildren}
59
+ </rte-split-button>`;
60
+ }
61
+
62
+ const meta: Meta<SplitButtonComponent> = {
63
+ title: "SplitButton",
64
+ component: SplitButtonComponent,
65
+ tags: ["autodocs"],
66
+ argTypes: {
67
+ appearance: {
68
+ control: "select",
69
+ options: ["primary", "secondary"],
70
+ },
71
+ size: {
72
+ control: "select",
73
+ options: ["s", "m", "l"],
74
+ },
75
+ compactSpacing: {
76
+ control: "boolean",
77
+ },
78
+ selected: {
79
+ control: "boolean",
80
+ },
81
+ position: {
82
+ control: "select",
83
+ options: ["bottom-start", "bottom-end", "top-start", "top-end"],
84
+ },
85
+ disabled: {
86
+ control: "boolean",
87
+ },
88
+ },
89
+ };
90
+ export default meta;
91
+ type Story = StoryObj<SplitButtonComponent>;
92
+
93
+ export const Default: Story = {
94
+ args: {
95
+ appearance: "primary",
96
+ label: "Button Label",
97
+ compactSpacing: false,
98
+ position: "bottom-start",
99
+ disabled: false,
100
+ ariaLabelRight: "Open menu",
101
+ },
102
+ render: (args) => ({
103
+ props: args,
104
+ template: `
105
+ ${generateSplitButtonElement()}
106
+ `,
107
+ }),
108
+ play: async ({ canvasElement }) => {
109
+ const canvas = within(canvasElement);
110
+ const button = canvas.getByTestId("Main action button");
111
+ await userEvent.click(button);
112
+ },
113
+ };
114
+
115
+ export const Appearance: Story = {
116
+ render: (args) => ({
117
+ props: args,
118
+ template: `
119
+ <div style="display: flex; gap: 16px">
120
+ ${generateSplitButtonElement({ appearance: "'primary'" })}
121
+ ${generateSplitButtonElement({ appearance: "'secondary'" })}
122
+ </div>
123
+ `,
124
+ }),
125
+ args: {
126
+ ...Default.args,
127
+ },
128
+ };
129
+
130
+ export const Size: Story = {
131
+ render: (args) => ({
132
+ props: args,
133
+ template: `
134
+ <div style="display: flex; gap: 16px">
135
+ ${generateSplitButtonElement({ size: "'s'" })}
136
+ ${generateSplitButtonElement({ size: "'m'" })}
137
+ ${generateSplitButtonElement({ size: "'l'" })}
138
+ </div>
139
+ `,
140
+ }),
141
+ args: {
142
+ ...Default.args,
143
+ },
144
+ };
145
+
146
+ export const CompactSpacing: Story = {
147
+ render: (args) => ({
148
+ props: args,
149
+ template: `
150
+ <div style="display: flex; gap: 16px">
151
+ ${generateSplitButtonElement({
152
+ size: "'s'",
153
+ compactSpacing: "true",
154
+ icon: "icon",
155
+ })}
156
+ ${generateSplitButtonElement({
157
+ size: "'m'",
158
+ compactSpacing: "true",
159
+ icon: "icon",
160
+ })}
161
+ ${generateSplitButtonElement({
162
+ size: "'l'",
163
+ compactSpacing: "true",
164
+ icon: "icon",
165
+ })}
166
+ </div>
167
+ `,
168
+ }),
169
+ args: {
170
+ ...Default.args,
171
+ },
172
+ };
173
+
174
+ export const Position: Story = {
175
+ render: (args) => ({
176
+ props: args,
177
+ template: `
178
+ <div style="display: flex; justify-content: center; align-items: center; min-height: 600px">
179
+ <div style="display: grid; grid-template-columns: 2fr 2fr; gap: 24px">
180
+ ${generateSplitButtonElement({
181
+ position: "'top-end'",
182
+ compactSpacing: "compactSpacing",
183
+ selected: "selected",
184
+ icon: "icon",
185
+ })}
186
+ ${generateSplitButtonElement({
187
+ position: "'top-start'",
188
+ compactSpacing: "compactSpacing",
189
+ selected: "selected",
190
+ icon: "icon",
191
+ })}
192
+ ${generateSplitButtonElement({
193
+ position: "'bottom-end'",
194
+ compactSpacing: "compactSpacing",
195
+ selected: "selected",
196
+ icon: "icon",
197
+ })}
198
+ ${generateSplitButtonElement({
199
+ position: "'bottom-start'",
200
+ compactSpacing: "compactSpacing",
201
+ selected: "selected",
202
+ icon: "icon",
203
+ })}
204
+ </div>
205
+ </div>
206
+ `,
207
+ }),
208
+ args: {
209
+ ...Default.args,
210
+ },
211
+ };
212
+
213
+ export const KeyboardInteraction: Story = {
214
+ play: async ({ canvasElement }) => {
215
+ const canvas = within(canvasElement);
216
+ const button = canvas.getByTestId("Menu button");
217
+ const menuContainer = canvas.getByTestId("Menu container");
218
+ await userEvent.tab();
219
+ await userEvent.tab();
220
+ expect(document.activeElement).toBe(button);
221
+ await userEvent.keyboard(DOWN_KEY);
222
+ await waitFor(() => expect(menuContainer).toBeVisible());
223
+ },
224
+ args: {
225
+ ...Default.args,
226
+ },
227
+ };
@@ -0,0 +1,55 @@
1
+ import { CommonModule } from "@angular/common";
2
+ import { ChangeDetectionStrategy, Component, computed, input, signal } from "@angular/core";
3
+ import {
4
+ splitButtonLeftIconSize,
5
+ splitButtonRightIconSize,
6
+ } from "@design-system-rte/core/components/split-button/split-button.constants";
7
+ import {
8
+ SplitButtonAppearance,
9
+ SplitButtonPosition,
10
+ SplitButtonSize,
11
+ } from "@design-system-rte/core/components/split-button/split-button.interface";
12
+
13
+ import { IconComponent } from "../icon/icon.component";
14
+ import { RegularIconIdKey, TogglableIconIdKey } from "../icon/icon.service";
15
+
16
+ @Component({
17
+ selector: "rte-split-button",
18
+ imports: [CommonModule, IconComponent],
19
+ standalone: true,
20
+ templateUrl: "./split-button.component.html",
21
+ styleUrl: "./split-button.component.scss",
22
+ changeDetection: ChangeDetectionStrategy.OnPush,
23
+ })
24
+ export class SplitButtonComponent {
25
+ readonly appearance = input<SplitButtonAppearance>("primary");
26
+ readonly size = input<SplitButtonSize>("m");
27
+ readonly label = input.required<string>();
28
+ readonly compactSpacing = input(false);
29
+ readonly selected = input(false);
30
+ readonly position = input<SplitButtonPosition>("bottom-start");
31
+ readonly icon = input<RegularIconIdKey | TogglableIconIdKey>();
32
+ readonly disabled = input(false);
33
+ readonly ariaLabelRight = input<string>();
34
+
35
+ readonly splitButtonLeftIconSize = computed(() => splitButtonLeftIconSize[this.size()]);
36
+ readonly splitButtonRightIconSize = computed(() => splitButtonRightIconSize[this.size()]);
37
+
38
+ readonly isOpen = signal(false);
39
+
40
+ handleKeyDownOnRightButton(event: KeyboardEvent): void {
41
+ this.handleKeyDown(event, "ArrowDown", () => this.isOpen.set(true));
42
+ this.handleKeyDown(event, "Escape", () => this.isOpen.set(false));
43
+ }
44
+
45
+ handleKeyDownOnMenu(event: KeyboardEvent): void {
46
+ this.handleKeyDown(event, "Escape", () => this.isOpen.set(false));
47
+ }
48
+
49
+ private handleKeyDown(event: KeyboardEvent, key: string, callback: () => void): void {
50
+ if (event.key === key) {
51
+ event.preventDefault();
52
+ callback();
53
+ }
54
+ }
55
+ }
@@ -1,4 +1,4 @@
1
- @use '@design-system-rte/core/tokens/main.scss' as *;
1
+ @use '@design-system-rte/core/design-tokens/main.scss' as *;
2
2
 
3
3
  $max-width: 160px;
4
4
  $max-height: 40px;
@@ -113,6 +113,4 @@ $max-height: 40px;
113
113
  border-style: solid;
114
114
  }
115
115
  }
116
- }
117
-
118
-
116
+ }
package/tsconfig.lib.json CHANGED
@@ -4,8 +4,6 @@
4
4
  "compilerOptions": {
5
5
  "outDir": "../../out-tsc/lib",
6
6
  "declaration": true,
7
- "declarationMap": true,
8
- "inlineSources": true,
9
7
  "types": []
10
8
  },
11
9
  "angularCompilerOptions": {