aloha-vue 1.0.233 → 1.0.235

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.
@@ -6,8 +6,42 @@ export default {
6
6
  ADropdown,
7
7
  },
8
8
  setup() {
9
- return {
9
+ const dropdownActions = [
10
+ {
11
+ text: "Actions 0",
12
+ type: "button",
13
+ callback: () => {},
14
+ isHidden: true,
15
+ },
16
+ {
17
+ text: "Actions 1",
18
+ type: "button",
19
+ callback: () => {},
20
+ disabled: true,
21
+ },
22
+ {
23
+ type: "divider",
24
+ },
25
+ {
26
+ type: "divider",
27
+ },
28
+ {
29
+ text: "Actions 2",
30
+ type: "button",
31
+ callback: () => {},
32
+ },
33
+ {
34
+ type: "link",
35
+ text: "Link 1",
36
+ href: "#",
37
+ },
38
+ {
39
+ type: "divider",
40
+ },
41
+ ];
10
42
 
43
+ return {
44
+ dropdownActions,
11
45
  };
12
46
  },
13
47
  };
@@ -27,4 +27,22 @@ div
27
27
  href="#"
28
28
  ) Item 3
29
29
 
30
- button.a_btn.a_btn_primary.a_ml_2 Test
30
+ a-dropdown.ml_3(
31
+ button-class="a_btn a_btn_primary"
32
+ :is-render-default="true"
33
+ :actions="dropdownActions"
34
+ )
35
+ template(
36
+ v-slot:button
37
+ )
38
+ span Weitere Aktionen
39
+
40
+ a-dropdown.ml_3(
41
+ button-class="a_btn a_btn_primary"
42
+ :is-render-default="true"
43
+ )
44
+ template(
45
+ v-slot:button
46
+ )
47
+ span Weitere Aktionen2
48
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aloha-vue",
3
3
  "description": "Project aloha",
4
- "version": "1.0.233",
4
+ "version": "1.0.235",
5
5
  "author": "Ilia Brykin",
6
6
  "scripts": {
7
7
  "build-icons": "node scriptsNode/iconsSvgToJs.js bootstrap3 && node scriptsNode/iconsSvgToJs.js bootstrap-1-9-1"
@@ -18,12 +18,23 @@ import {
18
18
 
19
19
  export default {
20
20
  name: "AButton",
21
+ inheritAttrs: false,
21
22
  props: {
22
23
  id: {
23
24
  type: String,
24
25
  required: false,
25
26
  default: () => uniqueId("a_btn_"),
26
27
  },
28
+ class: {
29
+ type: [String, Object],
30
+ required: false,
31
+ default: undefined,
32
+ },
33
+ attributes: {
34
+ type: Object,
35
+ required: false,
36
+ default: () => ({}),
37
+ },
27
38
  type: {
28
39
  type: String,
29
40
  required: false,
@@ -171,9 +182,11 @@ export default {
171
182
  },
172
183
  render() {
173
184
  return h("button", {
185
+ ...this.attributes,
174
186
  id: this.id,
175
187
  class: [
176
188
  "aloha_btn",
189
+ this.class,
177
190
  {
178
191
  disabled: this.ariaDisabled,
179
192
  },
@@ -1,23 +1,24 @@
1
1
  import {
2
- h,
2
+ h, onBeforeUnmount,
3
3
  Teleport,
4
4
  } from "vue";
5
5
 
6
+ import ADropdownAction from "./ADropdownAction/ADropdownAction";
6
7
  import AIcon from "../AIcon/AIcon";
7
8
  import ATranslation from "../ATranslation/ATranslation";
8
9
 
9
- import AKeysCode from "../const/AKeysCode";
10
+ import ActionsAPI from "./compositionAPI/ActionsAPI";
11
+ import AttributesAPI from "./compositionAPI/AttributesAPI";
12
+ import FocusAPI from "./compositionAPI/FocusAPI";
13
+ import PopoverAPI from "./compositionAPI/PopoverAPI";
14
+ import RefsAPI from "./compositionAPI/RefsAPI";
15
+ import ToggleAPI from "./compositionAPI/ToggleAPI";
16
+
10
17
  import placements from "../const/placements";
11
18
  import {
12
- createPopper,
13
- } from "@popperjs/core";
14
- import {
15
- cloneDeep,
16
- forEach,
17
19
  uniqueId,
18
20
  } from "lodash-es";
19
21
 
20
- const ELEMENTS_FOR_ARROWS = `button:not([disabled]), input:not([disabled]), a`;
21
22
 
22
23
  export default {
23
24
  name: "ADropdown",
@@ -25,32 +26,41 @@ export default {
25
26
  ATranslation,
26
27
  },
27
28
  props: {
28
- id: {
29
- type: String,
29
+ actions: {
30
+ type: Array,
30
31
  required: false,
31
- default: () => uniqueId("a_dropdown_btn_"),
32
+ default: () => [],
32
33
  },
33
- tag: {
34
- type: String,
34
+ isHideWithoutActionAndSlot: {
35
+ type: Boolean,
35
36
  required: false,
36
- default: "div",
37
+ default: true,
37
38
  },
38
- buttonTag: {
39
- type: String,
39
+ buttonAttributes: {
40
+ type: Object,
40
41
  required: false,
41
- default: "button",
42
+ default: () => ({}),
42
43
  },
43
44
  buttonClass: {
44
- type: String,
45
+ type: [String, Object],
45
46
  required: false,
46
47
  default: "a_btn a_btn_secondary dropdown_button",
47
48
  },
48
- buttonStyle: {
49
+ buttonTag: {
50
+ type: String,
51
+ required: false,
52
+ default: "button",
53
+ },
54
+ classForTooltipInner: {
49
55
  type: [String, Object],
50
56
  required: false,
51
- default: undefined,
57
+ default: "a_position_relative p-0 font_family_inherit",
52
58
  },
53
- buttonAttributes: {
59
+ disabled: {
60
+ type: Boolean,
61
+ required: false,
62
+ },
63
+ dropdownAttributes: {
54
64
  type: Object,
55
65
  required: false,
56
66
  default: () => ({}),
@@ -65,34 +75,20 @@ export default {
65
75
  required: false,
66
76
  default: "ul",
67
77
  },
68
- dropdownAttributes: {
69
- type: Object,
70
- required: false,
71
- default: () => ({}),
72
- },
73
- inBody: {
74
- type: Boolean,
78
+ elementsForArrows: {
79
+ type: String,
75
80
  required: false,
81
+ default: "button:not([disabled]), input:not([disabled]), a",
76
82
  },
77
- disabled: {
78
- type: Boolean,
83
+ id: {
84
+ type: String,
79
85
  required: false,
86
+ default: () => uniqueId("a_dropdown_btn_"),
80
87
  },
81
- isListWidthSameWithButton: {
88
+ inBody: {
82
89
  type: Boolean,
83
90
  required: false,
84
91
  },
85
- classForTooltipInner: {
86
- type: [String, Object],
87
- required: false,
88
- default: "a_position_relative p-0 font_family_inherit",
89
- },
90
- placement: {
91
- type: String,
92
- required: false,
93
- default: "bottom-start",
94
- validator: placement => placements.indexOf(placement) !== -1,
95
- },
96
92
  isCaret: {
97
93
  type: Boolean,
98
94
  required: false,
@@ -103,263 +99,105 @@ export default {
103
99
  required: false,
104
100
  default: true,
105
101
  },
106
- menuWidth: {
107
- type: Number,
102
+ isListWidthSameWithButton: {
103
+ type: Boolean,
108
104
  required: false,
109
- default: undefined,
110
105
  },
111
106
  isRenderDefault: {
112
107
  type: Boolean,
113
108
  required: false,
114
109
  default: false,
115
110
  },
116
- },
117
- data() {
118
- return {
119
- statusExpanded: false,
120
- statusEventPressArrows: false,
121
- buttonWidth: undefined,
122
- popper: undefined,
123
- };
124
- },
125
- computed: {
126
- ariaExpanded() {
127
- return `${ this.statusExpanded }`;
128
- },
129
-
130
- idForPopover() {
131
- return `${ this.htmlId }_popover`;
132
- },
133
-
134
- idLocal() {
135
- return this.buttonAttributes.id || this.id;
136
- },
137
-
138
- buttonAttributesLocal() {
139
- const BUTTON_ATTRIBUTES = cloneDeep(this.buttonAttributes);
140
- BUTTON_ATTRIBUTES.id = this.idLocal;
141
- BUTTON_ATTRIBUTES.ref = "dropdown_button";
142
- BUTTON_ATTRIBUTES["aria-haspopup"] = "true";
143
- BUTTON_ATTRIBUTES["aria-expanded"] = this.ariaExpanded;
144
- if (this.buttonClass) {
145
- BUTTON_ATTRIBUTES.class = this.buttonClass;
146
- }
147
- if (this.buttonStyle) {
148
- BUTTON_ATTRIBUTES.style = this.buttonStyle;
149
- }
150
- BUTTON_ATTRIBUTES.onClick = this.onToggle;
151
- BUTTON_ATTRIBUTES.onKeydown = this.onKeydown;
152
-
153
- if (this.buttonTag === "button") {
154
- BUTTON_ATTRIBUTES.type = BUTTON_ATTRIBUTES.type || "button";
155
- }
156
- if (this.disabled) {
157
- if (this.buttonTag === "button") {
158
- BUTTON_ATTRIBUTES.disabled = true;
159
- } else if (this.buttonTag === "a") {
160
- BUTTON_ATTRIBUTES["aria-disabled"] = "true";
161
- }
162
- }
163
- return BUTTON_ATTRIBUTES;
164
- },
165
-
166
- stylesForTooltipInner() {
167
- if (this.buttonWidth) {
168
- return `min-width: ${ this.buttonWidth }px; max-width: ${ this.buttonWidth }px;`;
169
- }
170
- return "";
111
+ menuWidth: {
112
+ type: Number,
113
+ required: false,
114
+ default: undefined,
171
115
  },
172
-
173
- dropdownAttributesLocal() {
174
- const DROPDOWN_ATTRIBUTES = cloneDeep(this.dropdownAttributes);
175
- DROPDOWN_ATTRIBUTES.ref = "dropdown";
176
- DROPDOWN_ATTRIBUTES["aria-labelledby"] = this.idLocal;
177
- DROPDOWN_ATTRIBUTES.class = ["a_dropdown__menu", this.dropdownClass, {
178
- a_dropdown__menu_show: this.statusExpanded,
179
- }];
180
- if (this.menuWidth) {
181
- DROPDOWN_ATTRIBUTES.style = `width: ${ this.menuWidth }px`;
182
- }
183
- return DROPDOWN_ATTRIBUTES;
116
+ placement: {
117
+ type: String,
118
+ required: false,
119
+ default: "bottom-start",
120
+ validator: placement => placements.indexOf(placement) !== -1,
184
121
  },
185
-
186
- isMenuRendered() {
187
- return this.isRenderDefault || this.statusExpanded;
122
+ tag: {
123
+ type: String,
124
+ required: false,
125
+ default: "div",
188
126
  },
189
127
  },
190
- beforeUnmount() {
191
- this.destroyEventCloseClick();
192
- this.destroyEventPressArrows();
193
- this.destroyPopover();
194
- },
195
- methods: {
196
- onKeydown($event) {
197
- if ($event.keyCode === AKeysCode.enter ||
198
- $event.keyCode === AKeysCode.space ||
199
- $event.keyCode === AKeysCode.arrowUp ||
200
- $event.keyCode === AKeysCode.arrowDown) {
201
- this.onToggle();
202
- $event.stopPropagation();
203
- $event.preventDefault();
204
- }
205
- },
128
+ setup(props) {
129
+ const {
130
+ dropdownButtonRef,
131
+ dropdownRef,
132
+ } = RefsAPI();
133
+
134
+ const {
135
+ setFocusToFirstElement,
136
+ } = FocusAPI(props, {
137
+ dropdownRef,
138
+ });
139
+
140
+ const {
141
+ destroyPopover,
142
+ openPopoverWithPopperjs,
143
+ } = PopoverAPI(props, {
144
+ dropdownButtonRef,
145
+ dropdownRef,
146
+ });
147
+
148
+ const {
149
+ buttonWidth,
150
+ destroyEventCloseClick,
151
+ destroyEventPressArrows,
152
+ onKeydown,
153
+ onToggle,
154
+ statusExpanded,
155
+ } = ToggleAPI(props, {
156
+ dropdownButtonRef,
157
+ dropdownRef,
158
+ openPopoverWithPopperjs,
159
+ destroyPopover,
160
+ setFocusToFirstElement,
161
+ });
162
+
163
+ const {
164
+ buttonAttributesLocal,
165
+ dropdownAttributesLocal,
166
+ isMenuRendered,
167
+ } = AttributesAPI(props, {
168
+ statusExpanded,
169
+ onToggle,
170
+ onKeydown,
171
+ });
172
+
173
+ const {
174
+ actionsFiltered,
175
+ hasActions,
176
+ } = ActionsAPI(props);
177
+
178
+ onBeforeUnmount(() => {
179
+ destroyEventCloseClick();
180
+ destroyEventPressArrows();
181
+ destroyPopover();
182
+ });
206
183
 
207
- onToggle() {
208
- if (this.disabled) {
209
- return;
210
- }
211
- if (this.statusExpanded) {
212
- this.onClose();
213
- } else {
214
- this.onOpen();
215
- }
216
- },
217
-
218
- onOpen() {
219
- this.setButtonWidth();
220
- this.initEventPressArrows();
221
- setTimeout(() => {
222
- this.openPopoverWithPopperjs();
223
- this.setEventCloseClick();
224
- setTimeout(() => {
225
- this.setFocusToFirstElement();
226
- });
227
- });
228
- this.statusExpanded = true;
229
- },
230
-
231
- setButtonWidth() {
232
- if (this.isListWidthSameWithButton) {
233
- this.buttonWidth = this.$refs.dropdown_button.clientWidth;
234
- }
235
- },
236
-
237
- setEventCloseClick() {
238
- document.addEventListener("click", this.onClickEvent);
239
- },
240
-
241
- destroyEventCloseClick() {
242
- document.removeEventListener("click", this.onClickEvent);
243
- },
244
-
245
- onClickEvent($event) {
246
- if (this.$refs.dropdown.contains($event.target)) {
247
- if (this.isCloseByClickInside) {
248
- this.onClose();
249
- this.setFocusToButton();
250
- }
251
- } else {
252
- this.onClose();
253
- }
254
- },
255
-
256
- onClose() {
257
- this.destroyEventCloseClick();
258
- this.destroyEventPressArrows();
259
- this.destroyPopover();
260
- this.statusExpanded = false;
261
- },
262
-
263
- initEventPressArrows() {
264
- if (this.statusEventPressArrows) { // Event ist schon installiert
265
- return;
266
- }
267
- this.statusEventPressArrows = true;
268
- document.body.addEventListener("keydown", this.pressButton);
269
- },
270
-
271
- destroyEventPressArrows() {
272
- if (!this.statusEventPressArrows) { // Event ist schon zestört
273
- return;
274
- }
275
- this.statusEventPressArrows = false;
276
- document.body.removeEventListener("keydown", this.pressButton);
277
- },
278
-
279
- setFocusToButton() {
280
- this.$refs.dropdown_button.focus();
281
- },
282
-
283
- pressButton($event) {
284
- const EVENT = $event || window.$event;
285
- if (EVENT.keyCode === AKeysCode.arrowDown ||
286
- EVENT.keyCode === AKeysCode.arrowUp) { // arrow down or up
287
- const DOWN = EVENT.keyCode === AKeysCode.arrowDown;
288
- this.pressArrows({ down: DOWN });
289
- $event.preventDefault();
290
- $event.stopPropagation();
291
- } else if (EVENT.keyCode === AKeysCode.escape) {
292
- this.onClose();
293
- this.setFocusToButton();
294
- $event.preventDefault();
295
- $event.stopPropagation();
296
- } else if (EVENT.keyCode === AKeysCode.tab) {
297
- this.onClose();
298
- this.setFocusToButton();
299
- }
300
- },
301
-
302
- pressArrows({ down }) {
303
- const ELEMENTS = this.$refs.dropdown.querySelectorAll(ELEMENTS_FOR_ARROWS);
304
- if (ELEMENTS.length === 0) {
305
- return;
306
- }
307
- forEach(ELEMENTS, (element, index) => {
308
- if (element === document.activeElement) {
309
- if (down) {
310
- if (index < ELEMENTS.length - 1) {
311
- ELEMENTS[index + 1].focus();
312
- }
313
- } else {
314
- if (index > 0) {
315
- ELEMENTS[index - 1].focus();
316
- }
317
- }
318
- return false;
319
- }
320
- });
321
- },
322
-
323
- setFocusToFirstElement() {
324
- if (!this.$refs.dropdown) {
325
- return;
326
- }
327
- const ELEMENTS = this.$refs.dropdown.querySelectorAll(ELEMENTS_FOR_ARROWS);
328
- if (ELEMENTS.length === 0) {
329
- return;
330
- }
331
- ELEMENTS[0].focus();
332
- },
333
-
334
- openPopoverWithPopperjs() {
335
- if (!this.popper) {
336
- this.popper = createPopper(
337
- this.$refs.dropdown_button,
338
- this.$refs.dropdown,
339
- {
340
- placement: this.placement,
341
- removeOnDestroy: true,
342
- modifiers: [
343
- {
344
- name: "offset",
345
- options: {
346
- offset: [0, 0],
347
- },
348
- },
349
- ],
350
- },
351
- );
352
- }
353
- },
354
-
355
- destroyPopover() {
356
- if (this.popper) {
357
- this.popper.destroy();
358
- this.popper = undefined;
359
- }
360
- },
184
+ return {
185
+ actionsFiltered,
186
+ buttonAttributesLocal,
187
+ buttonWidth,
188
+ dropdownAttributesLocal,
189
+ dropdownButtonRef,
190
+ dropdownRef,
191
+ hasActions,
192
+ isMenuRendered,
193
+ };
361
194
  },
362
195
  render() {
196
+ if (this.isHideWithoutActionAndSlot &&
197
+ !this.hasActions &&
198
+ !this.$slots.dropdown) {
199
+ return "";
200
+ }
363
201
  return h(
364
202
  this.tag,
365
203
  {
@@ -382,6 +220,12 @@ export default {
382
220
  this.dropdownAttributesLocal,
383
221
  [
384
222
  this.$slots.dropdown && this.$slots.dropdown(),
223
+ this.hasActions && this.actionsFiltered.map((action, actionIndex) => {
224
+ return h(ADropdownAction, {
225
+ key: actionIndex,
226
+ action,
227
+ }, this.$slots);
228
+ }),
385
229
  ],
386
230
  ),
387
231
  ]),
@@ -0,0 +1,55 @@
1
+ import {
2
+ h,
3
+ } from "vue";
4
+
5
+ import AButton from "../../AButton/AButton";
6
+ import ALink from "../../ALink/ALink";
7
+
8
+ export default {
9
+ name: "ADropdownAction",
10
+ props: {
11
+ action: {
12
+ type: Object,
13
+ required: true,
14
+ },
15
+ },
16
+ render() {
17
+ if (this.action.type === "button") {
18
+ return h("li", {
19
+ class: this.action.liClass,
20
+ }, [
21
+ h(AButton, {
22
+ class: "a_dropdown__item",
23
+ ...this.action,
24
+ onClick: this.action.callback,
25
+ }),
26
+ ]);
27
+ }
28
+ if (this.action.type === "divider") {
29
+ return h("li", {
30
+ class: [
31
+ "a_dropdown__divider",
32
+ this.action.liClass,
33
+ ],
34
+ });
35
+ }
36
+ if (this.action.type === "link") {
37
+ return h("li", {
38
+ class: this.action.liClass,
39
+ }, [
40
+ h(ALink, {
41
+ class: "a_dropdown__item",
42
+ ...this.action,
43
+ })
44
+ ]);
45
+ }
46
+ if (this.action.type === "template" &&
47
+ this.action.slotName &&
48
+ this.$slots[this.action.slotName]) {
49
+ return this.$slots[this.action.slotName]({
50
+ action: this.action,
51
+ });
52
+ }
53
+ return "";
54
+ },
55
+ };
@@ -0,0 +1,44 @@
1
+ import {
2
+ computed,
3
+ toRef,
4
+ } from "vue";
5
+
6
+ import {
7
+ filter,
8
+ forEach,
9
+ last,
10
+ } from "lodash-es";
11
+
12
+ export default function ActionsAPI(props) {
13
+ const actions = toRef(props, "actions");
14
+
15
+ const actionsFiltered = computed(() => {
16
+ const ACTIONS_FILTERED = filter(actions.value, action => {
17
+ return !action.isHidden;
18
+ });
19
+
20
+ const ACTIONS_DIVIDER_FILTERED = [];
21
+ forEach(ACTIONS_FILTERED, action => {
22
+ if (action.type !== "divider" ||
23
+ (ACTIONS_DIVIDER_FILTERED.length > 0 &&
24
+ last(ACTIONS_DIVIDER_FILTERED).type !== "divider")) {
25
+ ACTIONS_DIVIDER_FILTERED.push(action);
26
+ }
27
+ });
28
+ const LAST_ACTION = last(ACTIONS_DIVIDER_FILTERED);
29
+ if (LAST_ACTION && LAST_ACTION.type === "divider") {
30
+ ACTIONS_DIVIDER_FILTERED.pop();
31
+ }
32
+
33
+ return ACTIONS_DIVIDER_FILTERED;
34
+ });
35
+
36
+ const hasActions = computed(() => {
37
+ return actionsFiltered.value.length > 0;
38
+ });
39
+
40
+ return {
41
+ actionsFiltered,
42
+ hasActions,
43
+ };
44
+ }
@@ -0,0 +1,77 @@
1
+ import {
2
+ computed,
3
+ ref,
4
+ toRef,
5
+ } from "vue";
6
+
7
+ import {
8
+ cloneDeep,
9
+ } from "lodash-es";
10
+
11
+ export default function AttributesAPI(props, {
12
+ statusExpanded = ref(false),
13
+ onToggle = () => {},
14
+ onKeydown = () => {},
15
+ }) {
16
+ const buttonAttributes = toRef(props, "buttonAttributes");
17
+ const buttonClass = toRef(props, "buttonClass");
18
+ const buttonTag = toRef(props, "buttonTag");
19
+ const disabled = toRef(props, "disabled");
20
+ const dropdownAttributes = toRef(props, "dropdownAttributes");
21
+ const dropdownClass = toRef(props, "dropdownClass");
22
+ const id = toRef(props, "id");
23
+ const isRenderDefault = toRef(props, "isRenderDefault");
24
+ const menuWidth = toRef(props, "menuWidth");
25
+
26
+ const idLocal = computed(() => {
27
+ return buttonAttributes.value.id || id.value;
28
+ });
29
+
30
+ const buttonAttributesLocal = computed(() => {
31
+ const BUTTON_ATTRIBUTES = cloneDeep(buttonAttributes.value);
32
+ BUTTON_ATTRIBUTES.id = idLocal.value;
33
+ BUTTON_ATTRIBUTES.ref = "dropdownButtonRef";
34
+ BUTTON_ATTRIBUTES["aria-haspopup"] = "true";
35
+ BUTTON_ATTRIBUTES["aria-expanded"] = statusExpanded.value;
36
+ if (buttonClass.value) {
37
+ BUTTON_ATTRIBUTES.class = buttonClass.value;
38
+ }
39
+ BUTTON_ATTRIBUTES.onClick = onToggle;
40
+ BUTTON_ATTRIBUTES.onKeydown = onKeydown;
41
+
42
+ if (buttonTag.value === "button") {
43
+ BUTTON_ATTRIBUTES.type = BUTTON_ATTRIBUTES.type || "button";
44
+ }
45
+ if (disabled.value) {
46
+ if (buttonTag.value === "button") {
47
+ BUTTON_ATTRIBUTES.disabled = true;
48
+ } else if (buttonTag.value === "a") {
49
+ BUTTON_ATTRIBUTES["aria-disabled"] = "true";
50
+ }
51
+ }
52
+ return BUTTON_ATTRIBUTES;
53
+ });
54
+
55
+ const dropdownAttributesLocal = computed(() => {
56
+ const DROPDOWN_ATTRIBUTES = cloneDeep(dropdownAttributes.value);
57
+ DROPDOWN_ATTRIBUTES.ref = "dropdownRef";
58
+ DROPDOWN_ATTRIBUTES["aria-labelledby"] = idLocal.value;
59
+ DROPDOWN_ATTRIBUTES.class = ["a_dropdown__menu", dropdownClass.value, {
60
+ a_dropdown__menu_show: statusExpanded.value,
61
+ }];
62
+ if (menuWidth.value) {
63
+ DROPDOWN_ATTRIBUTES.style = `width: ${ menuWidth.value }px`;
64
+ }
65
+ return DROPDOWN_ATTRIBUTES;
66
+ });
67
+
68
+ const isMenuRendered = computed(() => {
69
+ return isRenderDefault.value || statusExpanded.value;
70
+ });
71
+
72
+ return {
73
+ buttonAttributesLocal,
74
+ dropdownAttributesLocal,
75
+ isMenuRendered,
76
+ };
77
+ }
@@ -0,0 +1,25 @@
1
+ import {
2
+ ref,
3
+ toRef,
4
+ } from "vue";
5
+
6
+ export default function FocusAPI(props, {
7
+ dropdownRef = ref(undefined),
8
+ }) {
9
+ const elementsForArrows = toRef(props, "elementsForArrows");
10
+
11
+ const setFocusToFirstElement = () => {
12
+ if (!dropdownRef.value) {
13
+ return;
14
+ }
15
+ const ELEMENTS = dropdownRef.value.querySelectorAll(elementsForArrows.value);
16
+ if (ELEMENTS.length === 0) {
17
+ return;
18
+ }
19
+ ELEMENTS[0].focus();
20
+ };
21
+
22
+ return {
23
+ setFocusToFirstElement,
24
+ };
25
+ }
@@ -0,0 +1,49 @@
1
+ import {
2
+ ref,
3
+ toRef,
4
+ } from "vue";
5
+
6
+ import {
7
+ createPopper,
8
+ } from "@popperjs/core";
9
+
10
+ export default function PopoverAPI(props, {
11
+ dropdownButtonRef = ref(undefined),
12
+ dropdownRef = ref(undefined),
13
+ }) {
14
+ const placement = toRef(props, "placement");
15
+
16
+ const popper = ref(undefined);
17
+ const openPopoverWithPopperjs = () => {
18
+ if (!popper.value) {
19
+ popper.value = createPopper(
20
+ dropdownButtonRef.value,
21
+ dropdownRef.value,
22
+ {
23
+ placement: placement.value,
24
+ removeOnDestroy: true,
25
+ modifiers: [
26
+ {
27
+ name: "offset",
28
+ options: {
29
+ offset: [0, 0],
30
+ },
31
+ },
32
+ ],
33
+ },
34
+ );
35
+ }
36
+ };
37
+
38
+ const destroyPopover = () => {
39
+ if (popper.value) {
40
+ popper.value.destroy();
41
+ popper.value = undefined;
42
+ }
43
+ };
44
+
45
+ return {
46
+ destroyPopover,
47
+ openPopoverWithPopperjs,
48
+ };
49
+ }
@@ -0,0 +1,13 @@
1
+ import {
2
+ ref,
3
+ } from "vue";
4
+
5
+ export default function RefsAPI() {
6
+ const dropdownButtonRef = ref(undefined);
7
+ const dropdownRef = ref(undefined);
8
+
9
+ return {
10
+ dropdownButtonRef,
11
+ dropdownRef,
12
+ };
13
+ }
@@ -0,0 +1,163 @@
1
+ import {
2
+ ref,
3
+ toRef,
4
+ } from "vue";
5
+
6
+ import AKeysCode from "../../const/AKeysCode";
7
+ import {
8
+ forEach,
9
+ } from "lodash-es";
10
+
11
+ export default function ToggleAPI(props, {
12
+ dropdownButtonRef = ref(undefined),
13
+ dropdownRef = ref(undefined),
14
+ openPopoverWithPopperjs = () => {},
15
+ destroyPopover = () => {},
16
+ setFocusToFirstElement = () => {},
17
+ }) {
18
+ const disabled = toRef(props, "disabled");
19
+ const isCloseByClickInside = toRef(props, "isCloseByClickInside");
20
+ const isListWidthSameWithButton = toRef(props, "isListWidthSameWithButton");
21
+ const elementsForArrows = toRef(props, "elementsForArrows");
22
+
23
+ const statusExpanded = ref(false);
24
+ const buttonWidth = ref(undefined);
25
+ const statusEventPressArrows = ref(false);
26
+
27
+ const pressArrows = ({ down }) => {
28
+ const ELEMENTS = dropdownRef.value.querySelectorAll(elementsForArrows.value);
29
+ if (ELEMENTS.length === 0) {
30
+ return;
31
+ }
32
+ forEach(ELEMENTS, (element, index) => {
33
+ if (element === document.activeElement) {
34
+ if (down) {
35
+ if (index < ELEMENTS.length - 1) {
36
+ ELEMENTS[index + 1].focus();
37
+ }
38
+ } else {
39
+ if (index > 0) {
40
+ ELEMENTS[index - 1].focus();
41
+ }
42
+ }
43
+ return false;
44
+ }
45
+ });
46
+ };
47
+
48
+ const setFocusToButton = () => {
49
+ dropdownRef.value.focus();
50
+ };
51
+
52
+ const pressButton = $event => {
53
+ const EVENT = $event || window.$event;
54
+ if (EVENT.keyCode === AKeysCode.arrowDown ||
55
+ EVENT.keyCode === AKeysCode.arrowUp) { // arrow down or up
56
+ const DOWN = EVENT.keyCode === AKeysCode.arrowDown;
57
+ pressArrows({ down: DOWN });
58
+ $event.preventDefault();
59
+ $event.stopPropagation();
60
+ } else if (EVENT.keyCode === AKeysCode.escape) {
61
+ onClose();
62
+ setFocusToButton();
63
+ $event.preventDefault();
64
+ $event.stopPropagation();
65
+ } else if (EVENT.keyCode === AKeysCode.tab) {
66
+ onClose();
67
+ setFocusToButton();
68
+ }
69
+ };
70
+
71
+ const setEventCloseClick = () => {
72
+ document.addEventListener("click", onClickEvent);
73
+ };
74
+
75
+ const destroyEventCloseClick = () => {
76
+ document.removeEventListener("click", onClickEvent);
77
+ };
78
+
79
+ const initEventPressArrows = () => {
80
+ if (statusEventPressArrows.value) { // Event ist schon installiert
81
+ return;
82
+ }
83
+ statusEventPressArrows.value = true;
84
+ document.body.addEventListener("keydown", pressButton);
85
+ };
86
+
87
+ const destroyEventPressArrows = () => {
88
+ if (!statusEventPressArrows.value) { // Event ist schon zestört
89
+ return;
90
+ }
91
+ statusEventPressArrows.value = false;
92
+ document.body.removeEventListener("keydown", pressButton);
93
+ };
94
+
95
+ const setButtonWidth = () => {
96
+ if (isListWidthSameWithButton.value &&
97
+ dropdownButtonRef.value) {
98
+ buttonWidth.value = dropdownButtonRef.value.clientWidth;
99
+ }
100
+ };
101
+
102
+ const onOpen = () => {
103
+ setButtonWidth();
104
+ initEventPressArrows();
105
+ setTimeout(() => {
106
+ openPopoverWithPopperjs();
107
+ setEventCloseClick();
108
+ setTimeout(() => {
109
+ setFocusToFirstElement();
110
+ });
111
+ });
112
+ statusExpanded.value = true;
113
+ };
114
+
115
+ const onToggle = () => {
116
+ if (disabled.value) {
117
+ return;
118
+ }
119
+ if (statusExpanded.value) {
120
+ onClose();
121
+ } else {
122
+ onOpen();
123
+ }
124
+ };
125
+
126
+ const onKeydown = $event => {
127
+ if ($event.keyCode === AKeysCode.enter ||
128
+ $event.keyCode === AKeysCode.space ||
129
+ $event.keyCode === AKeysCode.arrowUp ||
130
+ $event.keyCode === AKeysCode.arrowDown) {
131
+ onToggle();
132
+ $event.stopPropagation();
133
+ $event.preventDefault();
134
+ }
135
+ };
136
+
137
+ function onClose() {
138
+ destroyEventCloseClick();
139
+ destroyEventPressArrows();
140
+ destroyPopover();
141
+ statusExpanded.value = false;
142
+ }
143
+
144
+ function onClickEvent($event) {
145
+ if (dropdownRef.value.contains($event.target)) {
146
+ if (isCloseByClickInside.value) {
147
+ onClose();
148
+ setFocusToButton();
149
+ }
150
+ } else {
151
+ onClose();
152
+ }
153
+ }
154
+
155
+ return {
156
+ buttonWidth,
157
+ destroyEventCloseClick,
158
+ destroyEventPressArrows,
159
+ onKeydown,
160
+ onToggle,
161
+ statusExpanded,
162
+ };
163
+ }
@@ -0,0 +1,261 @@
1
+ import {
2
+ h,
3
+ resolveComponent,
4
+ } from "vue";
5
+
6
+ import AIcon from "../AIcon/AIcon";
7
+ import ASpinner from "../ASpinner/ASpinner";
8
+ import ATranslation from "../ATranslation/ATranslation";
9
+
10
+ import IconAPI from "../AButton/comositionAPI/IconAPI";
11
+ import LoadingAPI from "../AButton/comositionAPI/LoadingAPI";
12
+ import TextAPI from "../AButton/comositionAPI/TextAPI";
13
+ import TitleAPI from "../AButton/comositionAPI/TitleAPI";
14
+
15
+ import {
16
+ uniqueId,
17
+ } from "lodash-es";
18
+
19
+ export default {
20
+ name: "ALink",
21
+ inheritAttrs: false,
22
+ props: {
23
+ attributes: {
24
+ type: Object,
25
+ required: false,
26
+ default: () => ({}),
27
+ },
28
+ class: {
29
+ type: [String, Object],
30
+ required: false,
31
+ default: undefined,
32
+ },
33
+ disabled: {
34
+ type: Boolean,
35
+ required: false,
36
+ default: false,
37
+ },
38
+ extraTranslate: {
39
+ type: Object,
40
+ required: false,
41
+ default: undefined,
42
+ },
43
+ href: {
44
+ type: String,
45
+ required: false,
46
+ default: undefined,
47
+ },
48
+ icon: {
49
+ type: String,
50
+ required: false,
51
+ default: undefined,
52
+ },
53
+ iconAlign: {
54
+ type: String,
55
+ required: false,
56
+ default: "left",
57
+ validator: value => ["right", "left"].indexOf(value) !== -1,
58
+ },
59
+ iconAttributes: {
60
+ type: Object,
61
+ required: false,
62
+ default: () => ({}),
63
+ },
64
+ iconClass: {
65
+ type: String,
66
+ required: false,
67
+ default: undefined,
68
+ },
69
+ iconTag: {
70
+ type: String,
71
+ required: false,
72
+ default: undefined,
73
+ },
74
+ id: {
75
+ type: String,
76
+ required: false,
77
+ default: () => uniqueId("a_link_"),
78
+ },
79
+ isTitleHtml: {
80
+ type: Boolean,
81
+ required: false,
82
+ },
83
+ loading: {
84
+ type: Boolean,
85
+ required: false,
86
+ default: false,
87
+ },
88
+ loadingAlign: {
89
+ type: String,
90
+ required: false,
91
+ default: "right",
92
+ validator: value => ["right", "left"].indexOf(value) !== -1,
93
+ },
94
+ loadingClass: {
95
+ type: [String, Object],
96
+ required: false,
97
+ default: "a_spinner_small",
98
+ },
99
+ target: {
100
+ type: String,
101
+ required: false,
102
+ default: undefined,
103
+ },
104
+ text: {
105
+ type: [String, Number],
106
+ required: false,
107
+ default: undefined,
108
+ },
109
+ textAriaHidden: {
110
+ type: Boolean,
111
+ required: false,
112
+ default: false,
113
+ },
114
+ textClass: {
115
+ type: String,
116
+ required: false,
117
+ default: undefined,
118
+ },
119
+ textScreenReader: {
120
+ type: String,
121
+ required: false,
122
+ default: undefined,
123
+ },
124
+ title: {
125
+ type: String,
126
+ required: false,
127
+ default: undefined,
128
+ },
129
+ titlePlacement: {
130
+ type: String,
131
+ required: false,
132
+ default: "top",
133
+ validator: value => ["top", "left", "bottom", "right"].indexOf(value) !== -1,
134
+ },
135
+ to: {
136
+ type: [Object, String],
137
+ required: false,
138
+ default: undefined,
139
+ },
140
+ },
141
+ setup(props) {
142
+ const {
143
+ isTitleVisible,
144
+ } = TitleAPI(props);
145
+
146
+ const {
147
+ isLoadingLeft,
148
+ isLoadingRight,
149
+ } = LoadingAPI(props);
150
+
151
+ const {
152
+ isIconLeft,
153
+ isIconRight,
154
+ } = IconAPI(props);
155
+
156
+ const {
157
+ isTextScreenReaderVisible,
158
+ isTextVisible,
159
+ } = TextAPI(props);
160
+
161
+ return {
162
+ isIconLeft,
163
+ isIconRight,
164
+ isLoadingLeft,
165
+ isLoadingRight,
166
+ isTextScreenReaderVisible,
167
+ isTextVisible,
168
+ isTitleVisible,
169
+ };
170
+ },
171
+ render() {
172
+ const CHILDREN = [
173
+ this.isTitleVisible && h("span", {
174
+ ariaHidden: true,
175
+ class: "a_position_absolute_all",
176
+ title: this.title,
177
+ }),
178
+ this.isLoadingLeft && h(ASpinner, {
179
+ class: [
180
+ "aloha_link__spinner_left",
181
+ this.loadingClass,
182
+ ],
183
+ }),
184
+ this.isIconLeft && h(AIcon, {
185
+ icon: this.icon,
186
+ iconTag: this.iconTag,
187
+ class: [
188
+ this.iconClass,
189
+ {
190
+ aloha_link__icon_left: this.isTextVisible || this.$slots.default,
191
+ },
192
+ ],
193
+ ...this.iconAttributes,
194
+ }),
195
+ this.$slots.default && this.$slots.default(),
196
+ this.isTextVisible && h(ATranslation, {
197
+ tag: "span",
198
+ class: this.textClass,
199
+ html: this.text,
200
+ extra: this.extraTranslate,
201
+ ariaHidden: this.textAriaHidden,
202
+ }),
203
+ this.isTextScreenReaderVisible && h(ATranslation, {
204
+ class: "a_sr_only",
205
+ tag: "span",
206
+ html: this.textScreenReader,
207
+ extra: this.extraTranslate,
208
+ }),
209
+ this.isIconRight && h(AIcon, {
210
+ icon: this.icon,
211
+ iconTag: this.iconTag,
212
+ class: [
213
+ "aloha_link__icon_right",
214
+ this.iconClass,
215
+ {
216
+ aloha_link__icon_right: this.isTextVisible || this.$slots.default,
217
+ },
218
+ ],
219
+ ...this.iconAttributes,
220
+ }),
221
+ this.isLoadingRight && h(ASpinner, {
222
+ class: [
223
+ "aloha_link__spinner_right",
224
+ this.loadingClass,
225
+ ],
226
+ }),
227
+ ];
228
+
229
+ if (this.href) {
230
+ return h("a", {
231
+ ...this.attributes,
232
+ href: this.href,
233
+ target: this.target,
234
+ id: this.id,
235
+ class: [
236
+ "aloha_link",
237
+ this.class,
238
+ {
239
+ inactive: this.disabled,
240
+ },
241
+ ],
242
+ ariaDisabled: this.disabled,
243
+ }, CHILDREN);
244
+ }
245
+ if (this.to) {
246
+ return h(resolveComponent("RouterLink"), {
247
+ ...this.attributes,
248
+ to: this.to,
249
+ id: this.id,
250
+ class: [
251
+ "aloha_link",
252
+ this.class,
253
+ {
254
+ inactive: this.disabled,
255
+ },
256
+ ],
257
+ ariaDisabled: this.disabled,
258
+ }, CHILDREN);
259
+ }
260
+ },
261
+ };
@@ -65,8 +65,8 @@ export default function ActionsAPI(props, { emit }) {
65
65
  ACTIONS_DIVIDER_FILTERED.push(action);
66
66
  }
67
67
  });
68
-
69
- if (last(ACTIONS_DIVIDER_FILTERED).isDivider) {
68
+ const LAST_ACTION = last(ACTIONS_DIVIDER_FILTERED);
69
+ if (LAST_ACTION && LAST_ACTION.isDivider) {
70
70
  ACTIONS_DIVIDER_FILTERED.pop();
71
71
  }
72
72
 
@@ -43,7 +43,8 @@ export default function RowActionsAPI(props) {
43
43
  }
44
44
  });
45
45
 
46
- if (last(ROW_ACTIONS_DIVIDER_FILTERED).isDivider) {
46
+ const LAST_ACTION = last(ROW_ACTIONS_DIVIDER_FILTERED);
47
+ if (LAST_ACTION && LAST_ACTION.isDivider) {
47
48
  ROW_ACTIONS_DIVIDER_FILTERED.pop();
48
49
  }
49
50
 
@@ -0,0 +1,11 @@
1
+ .aloha_link {
2
+ position: relative;
3
+ }
4
+ .aloha_link__spinner_left,
5
+ .aloha_link__icon_left {
6
+ margin-right: .5rem;
7
+ }
8
+ .aloha_link__spinner_right,
9
+ .aloha_link__icon_right {
10
+ margin-left: .5rem;
11
+ }
@@ -17,4 +17,5 @@
17
17
  @import "components/ACloak";
18
18
  @import "components/ALoading";
19
19
  @import "components/AButton";
20
+ @import "components/ALink";
20
21
  @import "components/AWizard";