@hashicorp/design-system-components 0.8.0 → 0.9.0
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/CHANGELOG.md +11 -0
- package/addon/components/hds/dropdown/index.hbs +27 -0
- package/addon/components/hds/dropdown/list-item.hbs +84 -0
- package/addon/components/hds/dropdown/list-item.js +120 -0
- package/addon/components/hds/dropdown/toggle-button.hbs +23 -0
- package/addon/components/hds/dropdown/toggle-button.js +59 -0
- package/addon/components/hds/dropdown/toggle-icon.hbs +22 -0
- package/addon/components/hds/dropdown/toggle-icon.js +59 -0
- package/addon/components/hds/link/cta.js +13 -13
- package/addon/components/hds/link-to/cta.js +13 -13
- package/app/components/hds/dropdown/index.js +1 -0
- package/app/components/hds/dropdown/list-item.js +1 -0
- package/app/components/hds/dropdown/toggle-button.js +1 -0
- package/app/components/hds/dropdown/toggle-icon.js +1 -0
- package/app/styles/@hashicorp/design-system-components.scss +1 -0
- package/app/styles/components/dropdown.scss +392 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @hashicorp/design-system-components
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#66](https://github.com/hashicorp/design-system/pull/66) [`29e2ce55`](https://github.com/hashicorp/design-system/commit/29e2ce55bab74cc02dd511794dfadd2b3ac40a14) Thanks [@MelSumner](https://github.com/MelSumner)! - Adds dropdown component to the design system
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies []:
|
|
12
|
+
- @hashicorp/ember-flight-icons@2.0.4
|
|
13
|
+
|
|
3
14
|
## 0.8.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<Hds::Disclosure class="hds-dropdown">
|
|
2
|
+
<:toggle as |t|>
|
|
3
|
+
{{yield
|
|
4
|
+
(hash
|
|
5
|
+
ToggleButton=(component "hds/dropdown/toggle-button" isOpen=t.isActive onClick=t.onClickToggle)
|
|
6
|
+
ToggleIcon=(component "hds/dropdown/toggle-icon" isOpen=t.isActive onClick=t.onClickToggle)
|
|
7
|
+
)
|
|
8
|
+
}}
|
|
9
|
+
</:toggle>
|
|
10
|
+
<:content>
|
|
11
|
+
<ul
|
|
12
|
+
class="hds-dropdown-list
|
|
13
|
+
{{if (eq @listPosition 'left') 'hds-dropdown-list--position-left' 'hds-dropdown-list--position-right'}}"
|
|
14
|
+
>
|
|
15
|
+
{{yield
|
|
16
|
+
(hash
|
|
17
|
+
CopyItem=(component "hds/dropdown/list-item" item="copy-item")
|
|
18
|
+
Description=(component "hds/dropdown/list-item" item="description")
|
|
19
|
+
Generic=(component "hds/dropdown/list-item" item="generic")
|
|
20
|
+
Interactive=(component "hds/dropdown/list-item")
|
|
21
|
+
Separator=(component "hds/dropdown/list-item" item="separator")
|
|
22
|
+
Title=(component "hds/dropdown/list-item" item="title")
|
|
23
|
+
)
|
|
24
|
+
}}
|
|
25
|
+
</ul>
|
|
26
|
+
</:content>
|
|
27
|
+
</Hds::Disclosure>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{{#if (eq this.item "title")}}
|
|
2
|
+
<li class={{this.classNames}}>
|
|
3
|
+
{{this.text}}
|
|
4
|
+
</li>
|
|
5
|
+
|
|
6
|
+
{{else if (eq this.item "generic")}}
|
|
7
|
+
<li class={{this.classNames}}>
|
|
8
|
+
{{yield}}
|
|
9
|
+
</li>
|
|
10
|
+
|
|
11
|
+
{{else if (eq this.item "copy-item")}}
|
|
12
|
+
<li class={{this.classNames}}>
|
|
13
|
+
{{#if @copyItemTitle}}
|
|
14
|
+
<div class="hds-dropdown-list-item__copy-item-title">{{@copyItemTitle}}</div>
|
|
15
|
+
{{/if}}
|
|
16
|
+
<button
|
|
17
|
+
type="button"
|
|
18
|
+
class="{{if @state (concat 'is-' @state)}} {{if this.isSuccess 'is-success'}}"
|
|
19
|
+
{{on "click" this.copyCode}}
|
|
20
|
+
>
|
|
21
|
+
<div class="hds-dropdown-list-item__copy-item-text">
|
|
22
|
+
{{this.text}}
|
|
23
|
+
</div>
|
|
24
|
+
<FlightIcon
|
|
25
|
+
@name="{{if this.isSuccess 'clipboard-checked' 'clipboard-copy'}}"
|
|
26
|
+
class="hds-dropdown-list-item__copy-item-icon"
|
|
27
|
+
/>
|
|
28
|
+
</button>
|
|
29
|
+
</li>
|
|
30
|
+
|
|
31
|
+
{{else if (eq this.item "description")}}
|
|
32
|
+
<li class={{this.classNames}}>
|
|
33
|
+
{{this.text}}
|
|
34
|
+
</li>
|
|
35
|
+
|
|
36
|
+
{{else if (eq this.item "separator")}}
|
|
37
|
+
<li class={{this.classNames}} role="separator"></li>
|
|
38
|
+
|
|
39
|
+
{{else if (eq this.item "interactive")}}
|
|
40
|
+
<li class={{this.classNames}}>
|
|
41
|
+
{{#if @route}}
|
|
42
|
+
<LinkTo
|
|
43
|
+
class="{{if @state (concat 'is-' @state)}}"
|
|
44
|
+
@current-when={{@current-when}}
|
|
45
|
+
@models={{hds-link-to-models @model @models}}
|
|
46
|
+
@query={{hds-link-to-query @query}}
|
|
47
|
+
@replace={{@replace}}
|
|
48
|
+
@route={{@route}}
|
|
49
|
+
...attributes
|
|
50
|
+
>
|
|
51
|
+
{{#if @icon}}
|
|
52
|
+
<div class="hds-dropdown-list-item__interactive-icon">
|
|
53
|
+
<FlightIcon @name={{this.icon}} @isInlineBlock={{false}} />
|
|
54
|
+
</div>
|
|
55
|
+
{{/if}}
|
|
56
|
+
<div class="hds-dropdown-list-item__interactive-text">
|
|
57
|
+
{{this.text}}
|
|
58
|
+
</div>
|
|
59
|
+
</LinkTo>
|
|
60
|
+
{{else if @href}}
|
|
61
|
+
<a target="_blank" rel="noopener noreferrer" href={{@href}} class="{{if @state (concat 'is-' @state)}}">
|
|
62
|
+
{{#if this.icon}}
|
|
63
|
+
<div class="hds-dropdown-list-item__interactive-icon">
|
|
64
|
+
<FlightIcon @name={{this.icon}} @isInlineBlock={{false}} />
|
|
65
|
+
</div>
|
|
66
|
+
{{/if}}
|
|
67
|
+
<div class="hds-dropdown-list-item__interactive-text">
|
|
68
|
+
{{this.text}}
|
|
69
|
+
</div>
|
|
70
|
+
</a>
|
|
71
|
+
{{else}}
|
|
72
|
+
<button class="{{if @state (concat 'is-' @state)}}" type="button" ...attributes>
|
|
73
|
+
{{#if this.icon}}
|
|
74
|
+
<div class="hds-dropdown-list-item__interactive-icon">
|
|
75
|
+
<FlightIcon @name={{this.icon}} @isInlineBlock={{false}} />
|
|
76
|
+
</div>
|
|
77
|
+
{{/if}}
|
|
78
|
+
<div class="hds-dropdown-list-item__interactive-text">
|
|
79
|
+
{{this.text}}
|
|
80
|
+
</div>
|
|
81
|
+
</button>
|
|
82
|
+
{{/if}}
|
|
83
|
+
</li>
|
|
84
|
+
{{/if}}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { assert } from '@ember/debug';
|
|
3
|
+
import { tracked } from '@glimmer/tracking';
|
|
4
|
+
import { action } from '@ember/object';
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_COLOR = 'action';
|
|
7
|
+
export const DEFAULT_ITEM = 'interactive';
|
|
8
|
+
export const COLORS = ['action', 'critical'];
|
|
9
|
+
export const ITEMS = [
|
|
10
|
+
'copy-item',
|
|
11
|
+
'description',
|
|
12
|
+
'generic',
|
|
13
|
+
'interactive',
|
|
14
|
+
'separator',
|
|
15
|
+
'title',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export default class HdsDropdownListItemComponent extends Component {
|
|
19
|
+
@tracked isSuccess = this.args.isSuccess ?? false;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param item
|
|
23
|
+
* @type {string}
|
|
24
|
+
* @default interactive
|
|
25
|
+
* @description Determines the type of item to show
|
|
26
|
+
*/
|
|
27
|
+
get item() {
|
|
28
|
+
let { item = DEFAULT_ITEM } = this.args;
|
|
29
|
+
|
|
30
|
+
assert(
|
|
31
|
+
`@item for "Hds::Dropdown::ListItem" must be one of the following: ${ITEMS.join(
|
|
32
|
+
', '
|
|
33
|
+
)}; received: ${item}`,
|
|
34
|
+
ITEMS.includes(item)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return item;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param text
|
|
42
|
+
* @type {string}
|
|
43
|
+
* @description The text of the item. If no text value is defined an error will be thrown unless it is the generic or separator item type.
|
|
44
|
+
*/
|
|
45
|
+
get text() {
|
|
46
|
+
let { text } = this.args;
|
|
47
|
+
|
|
48
|
+
assert(
|
|
49
|
+
'@text for "Hds::Dropdown::ListItem" must have a valid value',
|
|
50
|
+
text !== undefined
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return text;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param color
|
|
58
|
+
* @type {string}
|
|
59
|
+
* @default primary
|
|
60
|
+
* @description Determines the color of the item (when item is set to interactive)
|
|
61
|
+
*/
|
|
62
|
+
get color() {
|
|
63
|
+
let { color = DEFAULT_COLOR } = this.args;
|
|
64
|
+
|
|
65
|
+
assert(
|
|
66
|
+
`@color for "Hds::Dropdown::ListItem" must be one of the following: ${COLORS.join(
|
|
67
|
+
', '
|
|
68
|
+
)}; received: ${color}`,
|
|
69
|
+
COLORS.includes(color)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return color;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param icon
|
|
77
|
+
* @type {string}
|
|
78
|
+
* @default null
|
|
79
|
+
* @description The name of the icon to be used.
|
|
80
|
+
*/
|
|
81
|
+
get icon() {
|
|
82
|
+
assert(
|
|
83
|
+
`when the "Hds::ListItem" @color is "critical" an @icon is required`,
|
|
84
|
+
!(this.color === 'critical' && !this.args.icon)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return this.args.icon ?? null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the class names to apply to the component.
|
|
92
|
+
* @method classNames
|
|
93
|
+
* @return {string} The "class" attribute to apply to the component.
|
|
94
|
+
*/
|
|
95
|
+
get classNames() {
|
|
96
|
+
let classes = ['hds-dropdown-list-item'];
|
|
97
|
+
|
|
98
|
+
// add a class based on the @item argument
|
|
99
|
+
if (this.item) {
|
|
100
|
+
classes.push(`hds-dropdown-list-item--${this.item}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// add a class based on the @color argument
|
|
104
|
+
if (this.item === 'interactive') {
|
|
105
|
+
classes.push(`hds-dropdown-list-item--color-${this.color}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return classes.join(' ');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@action
|
|
112
|
+
copyCode() {
|
|
113
|
+
navigator.clipboard.writeText(this.args.text);
|
|
114
|
+
// this if statement resolves to [object Promise] so maybe some improvements
|
|
115
|
+
// could be made here
|
|
116
|
+
if (navigator.clipboard.readText()) {
|
|
117
|
+
this.isSuccess = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{{!
|
|
2
|
+
// >>>>>>>>>>> WARNING <<<<<<<<<<
|
|
3
|
+
//
|
|
4
|
+
// Notice: in this component we're directly using the `Hds::Button` component
|
|
5
|
+
// (and adding a specialized class for the "toggle-button" variant, see below)
|
|
6
|
+
// If you need to change the styling of the `Button` component, remember that this will impact also
|
|
7
|
+
// this component too.
|
|
8
|
+
// If instead you need to change only the styling of the `toggle-button` component, you can do it
|
|
9
|
+
// in the CSS file using the specialized class declared there.
|
|
10
|
+
// This is NOT a standard approach that we use in the HDS design system implementation, but it's been
|
|
11
|
+
// the least worst option we could find to solve the problem of sharing the exact same style of the
|
|
12
|
+
// `Button (primary)` with other components.
|
|
13
|
+
}}
|
|
14
|
+
|
|
15
|
+
<Hds::Button
|
|
16
|
+
@text={{this.text}}
|
|
17
|
+
@icon="chevron-{{if @isOpen 'up' 'down'}}"
|
|
18
|
+
@iconPosition="trailing"
|
|
19
|
+
@color={{this.color}}
|
|
20
|
+
class="hds-dropdown-toggle--with-button-component"
|
|
21
|
+
{{on "click" this.onClick}}
|
|
22
|
+
...attributes
|
|
23
|
+
/>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { assert } from '@ember/debug';
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_COLOR = 'primary';
|
|
5
|
+
export const COLORS = ['primary', 'secondary'];
|
|
6
|
+
|
|
7
|
+
export default class HdsDropdownToggleButtonComponent extends Component {
|
|
8
|
+
/**
|
|
9
|
+
* @param text
|
|
10
|
+
* @type {string}
|
|
11
|
+
* @description The text of the button. If no text value is defined an error will be thrown.
|
|
12
|
+
*/
|
|
13
|
+
get text() {
|
|
14
|
+
let { text } = this.args;
|
|
15
|
+
|
|
16
|
+
assert(
|
|
17
|
+
'@text for "Hds::Dropdown::ToggleButton" must have a valid value',
|
|
18
|
+
text !== undefined
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return text;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param color
|
|
26
|
+
* @type {string}
|
|
27
|
+
* @default primary
|
|
28
|
+
* @description Determines the color of button to be used; acceptable values are `primary` and `secondary`
|
|
29
|
+
*/
|
|
30
|
+
get color() {
|
|
31
|
+
let { color = DEFAULT_COLOR } = this.args;
|
|
32
|
+
|
|
33
|
+
assert(
|
|
34
|
+
`@color for "Hds::Dropdown::ToggleButton" must be one of the following: ${COLORS.join(
|
|
35
|
+
', '
|
|
36
|
+
)}; received: ${color}`,
|
|
37
|
+
COLORS.includes(color)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return color;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param onClick
|
|
45
|
+
* @type {function}
|
|
46
|
+
* @default () => {}
|
|
47
|
+
*/
|
|
48
|
+
get onClick() {
|
|
49
|
+
let { onClick } = this.args;
|
|
50
|
+
|
|
51
|
+
// notice: this is a guard used in case the toggle is used as standalone element (eg. in the showcase)
|
|
52
|
+
// in reality it's always used inside the Dropdown main component as yielded component, so the onClick handler is always defined
|
|
53
|
+
if (typeof onClick === 'function') {
|
|
54
|
+
return onClick;
|
|
55
|
+
} else {
|
|
56
|
+
return () => {};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<button
|
|
2
|
+
class="hds-dropdown-toggle-icon"
|
|
3
|
+
aria-label={{this.text}}
|
|
4
|
+
...attributes
|
|
5
|
+
{{on "click" this.onClick}}
|
|
6
|
+
type="button"
|
|
7
|
+
>
|
|
8
|
+
<div class="hds-dropdown-toggle-icon__wrapper">
|
|
9
|
+
{{#if @imageSrc}}
|
|
10
|
+
<img src={{@imageSrc}} alt="" role="presentation" height="32" width="32" />
|
|
11
|
+
{{else}}
|
|
12
|
+
<FlightIcon @name={{this.icon}} @size="24" />
|
|
13
|
+
{{/if}}
|
|
14
|
+
</div>
|
|
15
|
+
{{#if this.hasChevron}}
|
|
16
|
+
<FlightIcon
|
|
17
|
+
@name="chevron-{{if @isOpen 'up' 'down'}}"
|
|
18
|
+
class="hds-dropdown-toggle-icon__chevron"
|
|
19
|
+
@isInlineBlock={{false}}
|
|
20
|
+
/>
|
|
21
|
+
{{/if}}
|
|
22
|
+
</button>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { assert } from '@ember/debug';
|
|
3
|
+
|
|
4
|
+
export default class HdsDropdownToggleIconComponent extends Component {
|
|
5
|
+
/**
|
|
6
|
+
* @param text
|
|
7
|
+
* @type {string}
|
|
8
|
+
* @description The text of the `aria-label` applied to the toggle
|
|
9
|
+
*/
|
|
10
|
+
get text() {
|
|
11
|
+
let { text } = this.args;
|
|
12
|
+
|
|
13
|
+
assert(
|
|
14
|
+
'@text for "Hds::Dropdown::ToggleIcon" must have a valid value',
|
|
15
|
+
text !== undefined
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
return text;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Indicates if a dropdown chevron icon should be displayed; should be displayed unless the "more-horizontal" icon is used.
|
|
23
|
+
*
|
|
24
|
+
* @param hasChevron
|
|
25
|
+
* @type {boolean}
|
|
26
|
+
* @default true
|
|
27
|
+
*/
|
|
28
|
+
get hasChevron() {
|
|
29
|
+
return this.args.hasChevron ?? true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Sets the icon name
|
|
34
|
+
*
|
|
35
|
+
* @param icon
|
|
36
|
+
* @type {string}
|
|
37
|
+
* @default user
|
|
38
|
+
*/
|
|
39
|
+
get icon() {
|
|
40
|
+
return this.args.icon ?? 'user';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param onClick
|
|
45
|
+
* @type {function}
|
|
46
|
+
* @default () => {}
|
|
47
|
+
*/
|
|
48
|
+
get onClick() {
|
|
49
|
+
let { onClick } = this.args;
|
|
50
|
+
|
|
51
|
+
// notice: this is a guard used in case the toggle is used as standalone element (eg. in the showcase)
|
|
52
|
+
// in reality it's always used inside the Dropdown main component as yielded component, so the onClick handler is always defined
|
|
53
|
+
if (typeof onClick === 'function') {
|
|
54
|
+
return onClick;
|
|
55
|
+
} else {
|
|
56
|
+
return () => {};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -112,19 +112,6 @@ export default class HdsLinkCtaComponent extends Component {
|
|
|
112
112
|
return this.args.isFullWidth ?? false;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
@action
|
|
116
|
-
didInsert(el) {
|
|
117
|
-
// we need to register the element to compare it with the one that triggered the "key/space" event
|
|
118
|
-
this.el = el;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@action
|
|
122
|
-
onKeySpace(event) {
|
|
123
|
-
if (event.target === this.el) {
|
|
124
|
-
event.target.click();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
115
|
/**
|
|
129
116
|
* Get the class names to apply to the component.
|
|
130
117
|
* @method classNames
|
|
@@ -147,4 +134,17 @@ export default class HdsLinkCtaComponent extends Component {
|
|
|
147
134
|
|
|
148
135
|
return classes.join(' ');
|
|
149
136
|
}
|
|
137
|
+
|
|
138
|
+
@action
|
|
139
|
+
didInsert(el) {
|
|
140
|
+
// we need to register the element to compare it with the one that triggered the "key/space" event
|
|
141
|
+
this.el = el;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@action
|
|
145
|
+
onKeySpace(event) {
|
|
146
|
+
if (event.target === this.el) {
|
|
147
|
+
event.target.click();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
150
|
}
|
|
@@ -127,19 +127,6 @@ export default class HdsLinkToCtaComponent extends Component {
|
|
|
127
127
|
return route;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
@action
|
|
131
|
-
didInsert(el) {
|
|
132
|
-
// we need to register the element to compare it with the one that triggered the "key/space" event
|
|
133
|
-
this.el = el;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
@action
|
|
137
|
-
onKeySpace(event) {
|
|
138
|
-
if (event.target === this.el) {
|
|
139
|
-
event.target.click();
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
130
|
/**
|
|
144
131
|
* Get the class names to apply to the component.
|
|
145
132
|
* @method classNames
|
|
@@ -162,4 +149,17 @@ export default class HdsLinkToCtaComponent extends Component {
|
|
|
162
149
|
|
|
163
150
|
return classes.join(' ');
|
|
164
151
|
}
|
|
152
|
+
|
|
153
|
+
@action
|
|
154
|
+
didInsert(el) {
|
|
155
|
+
// we need to register the element to compare it with the one that triggered the "key/space" event
|
|
156
|
+
this.el = el;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@action
|
|
160
|
+
onKeySpace(event) {
|
|
161
|
+
if (event.target === this.el) {
|
|
162
|
+
event.target.click();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
165
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@hashicorp/design-system-components/components/hds/dropdown/index';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@hashicorp/design-system-components/components/hds/dropdown/list-item';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@hashicorp/design-system-components/components/hds/dropdown/toggle-button';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@hashicorp/design-system-components/components/hds/dropdown/toggle-icon';
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
// >>>>>>>>>> WARNING <<<<<<<<<<<
|
|
2
|
+
//
|
|
3
|
+
// Notice: in this component we're using directly the `Hds::Button` component
|
|
4
|
+
// (and adding a specialized class for the "toggle-button" variant, see below)
|
|
5
|
+
// If you need to change the styling of the `Button` component, remember that this will impact also
|
|
6
|
+
// this component too.
|
|
7
|
+
// If instead you need to change only the styling of the `toggle-button` component, you can do it here using
|
|
8
|
+
// the specialized class declared below.
|
|
9
|
+
// This is NOT a standard approach that we use in the HDS design system implementation, but it's been
|
|
10
|
+
// the least worst option we could find to solve the problem of sharing the exact same style of the
|
|
11
|
+
// `Button (primary)` with other components.
|
|
12
|
+
|
|
13
|
+
//
|
|
14
|
+
// DROPDOWN COMPONENT
|
|
15
|
+
//
|
|
16
|
+
// properties within each class are sorted alphabetically
|
|
17
|
+
// notice: pseudo-classes for the states *must* follow the order link > visited > hover > focus > active
|
|
18
|
+
// see https://github.com/hashicorp/design-system-components/issues/132
|
|
19
|
+
//
|
|
20
|
+
//
|
|
21
|
+
|
|
22
|
+
@use '../mixins/focus-ring' as *;
|
|
23
|
+
|
|
24
|
+
// TOGGLE/ICON
|
|
25
|
+
.hds-dropdown-toggle-icon {
|
|
26
|
+
align-items: center;
|
|
27
|
+
background-color: transparent;
|
|
28
|
+
border: 1px solid transparent; // We need this to be transparent for a11y
|
|
29
|
+
border-radius: 5px;
|
|
30
|
+
display: flex;
|
|
31
|
+
height: 36px;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
outline-style: solid; // used to avoid double outline+focus-ring in Safari (see https://github.com/hashicorp/design-system-components/issues/161#issuecomment-1031548656)
|
|
34
|
+
outline-color: transparent; // We need this to be transparent for a11y
|
|
35
|
+
padding: 1px;
|
|
36
|
+
min-width: 36px;
|
|
37
|
+
|
|
38
|
+
&:hover,
|
|
39
|
+
&.is-hover {
|
|
40
|
+
background-color: var(--token-color-surface-interactive);
|
|
41
|
+
border-color: var(--token-color-border-strong);
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// this is the :focus
|
|
46
|
+
@include hds-focus-ring-with-pseudo-element($top: -1px, $right: -1px, $bottom: -1px, $left: -1px, $radius: 5px);
|
|
47
|
+
|
|
48
|
+
&:active,
|
|
49
|
+
&.is-active {
|
|
50
|
+
background-color: var(--token-color-surface-interactive-active);
|
|
51
|
+
border-color: var(--token-color-border-strong);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.hds-dropdown-toggle-icon__wrapper {
|
|
56
|
+
align-items: center;
|
|
57
|
+
border-radius: 3px; // 5px- 1px padding - 1px border
|
|
58
|
+
display: flex;
|
|
59
|
+
justify-content: center;
|
|
60
|
+
height: 32px;
|
|
61
|
+
padding: 1px;
|
|
62
|
+
width: 32px;
|
|
63
|
+
|
|
64
|
+
img {
|
|
65
|
+
border-radius: inherit;
|
|
66
|
+
object-fit: cover; // this will make sure it's correct even if the item isn't square
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.hds-dropdown-toggle-icon__chevron {
|
|
71
|
+
margin-left: 0.25rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// TOGGLE/BUTTON
|
|
75
|
+
.hds-dropdown-toggle--with-button-component {
|
|
76
|
+
box-shadow: none; // we override this to remove the elevation style
|
|
77
|
+
|
|
78
|
+
.hds-button__icon {
|
|
79
|
+
margin-right: -6px; // we apply a negative margin to counter the padding-right of the button and reduce the visual space between the icon and the right border
|
|
80
|
+
margin-left: 0.5rem; // this overrides the rule `.hds-button__text + .hds-button__icon`
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
// LIST
|
|
86
|
+
// UL ELEMENT
|
|
87
|
+
// GOES INSIDE HDS::DISCLOSURE's :content block
|
|
88
|
+
|
|
89
|
+
.hds-dropdown-list {
|
|
90
|
+
background-color: var(--token-color-surface-primary);
|
|
91
|
+
border-radius: 6px;
|
|
92
|
+
box-shadow: var(--token-surface-high-box-shadow);
|
|
93
|
+
list-style: none;
|
|
94
|
+
margin: 0;
|
|
95
|
+
max-width: 25rem;
|
|
96
|
+
min-width: 12.5rem;
|
|
97
|
+
padding: 4px 0;
|
|
98
|
+
&.hds-dropdown-list--position-right {
|
|
99
|
+
position: absolute;
|
|
100
|
+
right: 0;
|
|
101
|
+
top: calc(100% + 0.25rem);
|
|
102
|
+
z-index: 2; // https://github.com/hashicorp/design-system/issues/114
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&.hds-dropdown-list--position-left {
|
|
106
|
+
position: absolute;
|
|
107
|
+
left: 0;
|
|
108
|
+
top: calc(100% + 0.25rem);
|
|
109
|
+
z-index: 2; // https://github.com/hashicorp/design-system/issues/114
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// LIST > LIST-ITEM
|
|
114
|
+
// HDS::DROPDOWN::LIST-ITEM
|
|
115
|
+
|
|
116
|
+
.hds-dropdown-list-item--title {
|
|
117
|
+
color: var(--token-color-foreground-strong);
|
|
118
|
+
font-family: var(--token-typography-body-100-font-family);
|
|
119
|
+
font-size: var(--token-typography-body-100-font-size);
|
|
120
|
+
font-weight: var(--token-typography-font-weight-semibold);
|
|
121
|
+
line-height: var(--token-typography-body-100-line-height);
|
|
122
|
+
padding: 10px 16px 4px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.hds-dropdown-list-item--generic {
|
|
126
|
+
padding-left: 16px;
|
|
127
|
+
padding-right: 16px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.hds-dropdown-list-item--description {
|
|
131
|
+
color: var(--token-color-foreground-faint);
|
|
132
|
+
font-family: var(--token-typography-body-100-font-family);
|
|
133
|
+
font-size: var(--token-typography-body-100-font-size);
|
|
134
|
+
font-weight: var(--token-typography-font-weight-regular);
|
|
135
|
+
line-height: var(--token-typography-body-100-line-height);
|
|
136
|
+
padding: 2px 16px 4px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.hds-dropdown-list-item--separator {
|
|
140
|
+
position: relative;
|
|
141
|
+
height: 4px;
|
|
142
|
+
width: 100%;
|
|
143
|
+
|
|
144
|
+
&::before {
|
|
145
|
+
position: absolute;
|
|
146
|
+
right: 6px;
|
|
147
|
+
left: 6px;
|
|
148
|
+
bottom: 0;
|
|
149
|
+
border-bottom: 1px solid var(--token-color-border-primary);
|
|
150
|
+
content: '';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.hds-dropdown-list-item__copy-item-title {
|
|
155
|
+
color: var(--token-color-foreground-faint);
|
|
156
|
+
font-family: var(--token-typography-body-100-font-family);
|
|
157
|
+
font-size: var(--token-typography-body-100-font-size); // 13
|
|
158
|
+
font-weight: var(--token-typography-font-weight-semibold);
|
|
159
|
+
line-height: var(--token-typography-body-100-line-height); // 18
|
|
160
|
+
padding: 2px 0 4px;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.hds-dropdown-list-item--copy-item {
|
|
164
|
+
padding: 10px 16px 12px;
|
|
165
|
+
width: 100%;
|
|
166
|
+
|
|
167
|
+
button {
|
|
168
|
+
background-color: transparent;
|
|
169
|
+
border-radius: 5px;
|
|
170
|
+
border: 1px solid var(--token-color-border-primary);
|
|
171
|
+
color: var(--token-color-foreground-primary);
|
|
172
|
+
display: flex;
|
|
173
|
+
font-family: var(--token-typography-font-stack-code);
|
|
174
|
+
justify-content: space-between;
|
|
175
|
+
padding: 12px 8px;
|
|
176
|
+
width: 100%;
|
|
177
|
+
|
|
178
|
+
&:hover,
|
|
179
|
+
&.is-hover {
|
|
180
|
+
background-color: var(--token-color-surface-interactive-hover);
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@include hds-focus-ring-basic();
|
|
185
|
+
|
|
186
|
+
&:focus,
|
|
187
|
+
&.is-focus {
|
|
188
|
+
//TODO this focus is just way too complex
|
|
189
|
+
background-color: var(--token-color-surface-action);
|
|
190
|
+
border-color: var(--token-color-focus-action-internal);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
&:active,
|
|
194
|
+
&.is-active {
|
|
195
|
+
background-color: var(--token-color-surface-interactive-active);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
&.is-success {
|
|
199
|
+
border-color: var(--token-color-border-success);
|
|
200
|
+
background-color: var(--token-color-surface-success);
|
|
201
|
+
|
|
202
|
+
.hds-dropdown-list-item__copy-item-icon {
|
|
203
|
+
color: var(--token-color-foreground-success);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.hds-dropdown-list-item__copy-item-text {
|
|
208
|
+
font-size: var(--token-typography-code-100-font-size);
|
|
209
|
+
font-weight: var(--token-typography-font-weight-regular);
|
|
210
|
+
line-height: var(--token-typography-code-100-line-height);
|
|
211
|
+
// max-width: 250px; // TODO we should be able to figure out the proportions here
|
|
212
|
+
overflow: hidden;
|
|
213
|
+
text-align: left;
|
|
214
|
+
text-overflow: ellipsis;
|
|
215
|
+
white-space: nowrap;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.hds-dropdown-list-item__copy-item-icon {
|
|
219
|
+
color: var(--token-color-foreground-action);
|
|
220
|
+
margin-left: 0.5rem;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.hds-dropdown-list-item--interactive {
|
|
226
|
+
position: relative;
|
|
227
|
+
isolation: isolate; // used to create a new stacking context (needed to have the pseudo element below text/icon but not the parent container)
|
|
228
|
+
min-height: 36px;
|
|
229
|
+
|
|
230
|
+
// need to reset a few extra things to make the button visually appear the same as the links
|
|
231
|
+
// TODO this is 0.125rem taller than the link...
|
|
232
|
+
button {
|
|
233
|
+
border: 1px inset transparent; // cause of the extra height
|
|
234
|
+
background-color: transparent;
|
|
235
|
+
width: 100%;
|
|
236
|
+
|
|
237
|
+
&:hover {
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// shared styles for links and buttons
|
|
244
|
+
a, button {
|
|
245
|
+
align-items: center;
|
|
246
|
+
display: flex;
|
|
247
|
+
outline-style: solid; // used to avoid double outline+focus-ring in Safari (see https://github.com/hashicorp/design-system-components/issues/161#issuecomment-1031548656)
|
|
248
|
+
outline-color: transparent;
|
|
249
|
+
padding: 8px 10px 8px 16px;
|
|
250
|
+
text-decoration: none;
|
|
251
|
+
|
|
252
|
+
// this is used for the left "hover" indicator
|
|
253
|
+
&::before {
|
|
254
|
+
border-radius: 1px;
|
|
255
|
+
bottom: 6px;
|
|
256
|
+
content: '';
|
|
257
|
+
left: 0.25rem;
|
|
258
|
+
position: absolute;
|
|
259
|
+
top: 6px;
|
|
260
|
+
width: 2px;
|
|
261
|
+
z-index: -1;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Notice: this is used for the active/focus states which have very specific positions
|
|
265
|
+
// and also has a background color, so we can't use the existing focus-ring mixins
|
|
266
|
+
&::after {
|
|
267
|
+
border-radius: 5px;
|
|
268
|
+
bottom: 0px;
|
|
269
|
+
content: '';
|
|
270
|
+
left: 0.625rem;
|
|
271
|
+
position: absolute;
|
|
272
|
+
right: 0.25rem;
|
|
273
|
+
z-index: -1;
|
|
274
|
+
top: 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Notice: to avoid too much duplication we define two local CSS variables
|
|
278
|
+
// and define their values in the color variants below
|
|
279
|
+
|
|
280
|
+
// default focus for browsers that still rely on ":focus"
|
|
281
|
+
&:focus,
|
|
282
|
+
&.is-focus {
|
|
283
|
+
&::after {
|
|
284
|
+
background-color: var(--current-background-color);
|
|
285
|
+
box-shadow: var(--current-focus-ring-box-shadow);
|
|
286
|
+
left: 0.25rem;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// undo the previous declaration for browsers that support ":focus-visible" but wouldn't normally show default focus styles
|
|
290
|
+
&:focus:not(:focus-visible) {
|
|
291
|
+
&::after {
|
|
292
|
+
box-shadow: none;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// set focus for browsers that support ":focus-visible"
|
|
296
|
+
&:focus-visible {
|
|
297
|
+
&::after {
|
|
298
|
+
background-color: var(--current-background-color);
|
|
299
|
+
box-shadow: var(--current-focus-ring-box-shadow);
|
|
300
|
+
left: 0.25rem;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// remove the focus ring on "active + focused" state (by design)
|
|
304
|
+
&:focus:active,
|
|
305
|
+
&.is-focus.is-active {
|
|
306
|
+
&::after {
|
|
307
|
+
box-shadow: none;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.hds-dropdown-list-item__interactive-text {
|
|
314
|
+
font-family: var(--token-typography-body-200-font-family);
|
|
315
|
+
font-size: var(--token-typography-body-200-font-size);
|
|
316
|
+
font-weight: var(--token-typography-font-weight-medium);
|
|
317
|
+
line-height: var(--token-typography-body-200-line-height);
|
|
318
|
+
text-align: left; // the button element was centering text
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.hds-dropdown-list-item__interactive-icon {
|
|
322
|
+
margin-right: 0.5rem;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.hds-dropdown-list-item--color-action {
|
|
326
|
+
a, button {
|
|
327
|
+
color: var(--token-color-foreground-primary);
|
|
328
|
+
|
|
329
|
+
// assign the values to the local CSS variables used above
|
|
330
|
+
&::after {
|
|
331
|
+
--current-background-color: var(--token-color-surface-action);
|
|
332
|
+
--current-focus-ring-box-shadow: var(
|
|
333
|
+
--token-focus-ring-action-box-shadow
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
&:hover,
|
|
338
|
+
&.is-hover {
|
|
339
|
+
color: var(--token-color-foreground-action-hover);
|
|
340
|
+
&::before {
|
|
341
|
+
background-color: currentColor;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
&:active,
|
|
346
|
+
&.is-active {
|
|
347
|
+
color: var(--token-color-foreground-action-active);
|
|
348
|
+
&::before {
|
|
349
|
+
background-color: currentColor;
|
|
350
|
+
}
|
|
351
|
+
&::after {
|
|
352
|
+
background-color: var(--token-color-surface-action);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.hds-dropdown-list-item--color-critical {
|
|
359
|
+
a, button {
|
|
360
|
+
color: var(--token-color-foreground-critical);
|
|
361
|
+
|
|
362
|
+
// assign the values to the local CSS variables used above
|
|
363
|
+
&::after {
|
|
364
|
+
--current-background-color: var(--token-color-surface-critical);
|
|
365
|
+
--current-focus-ring-box-shadow: var(
|
|
366
|
+
--token-focus-ring-critical-box-shadow
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
&:hover,
|
|
371
|
+
&.is-hover {
|
|
372
|
+
color: var(
|
|
373
|
+
--token-color-palette-red-300
|
|
374
|
+
); // TODO we need to add this token to the design system
|
|
375
|
+
&::before {
|
|
376
|
+
background-color: currentColor;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
&:active,
|
|
380
|
+
&.is-active {
|
|
381
|
+
color: var(
|
|
382
|
+
--token-color-palette-red-400
|
|
383
|
+
); // TODO we need to add this token to the design system
|
|
384
|
+
&::before {
|
|
385
|
+
background-color: currentColor;
|
|
386
|
+
}
|
|
387
|
+
&::after {
|
|
388
|
+
background-color: var(--token-color-surface-critical);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hashicorp/design-system-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "HashiCorp Design System Components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hashicorp",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@hashicorp/design-system-tokens": "^0.8.0",
|
|
38
|
-
"@hashicorp/ember-flight-icons": "^2.0.
|
|
38
|
+
"@hashicorp/ember-flight-icons": "^2.0.4",
|
|
39
39
|
"ember-auto-import": "^2.2.3",
|
|
40
40
|
"ember-cli-babel": "^7.26.6",
|
|
41
41
|
"ember-cli-htmlbars": "^5.7.1",
|