@aquera/nile-elements 0.0.84 → 0.0.85

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
@@ -3,7 +3,7 @@
3
3
  "description": "Webcomponent nile-elements following open-wc recommendations",
4
4
  "license": "MIT",
5
5
  "author": "nile-elements",
6
- "version": "0.0.84",
6
+ "version": "0.0.85",
7
7
  "main": "dist/src/index.js",
8
8
  "type": "module",
9
9
  "module": "dist/src/index.js",
@@ -77,7 +77,8 @@
77
77
  "./nile-format-date": "./dist/src/nile-format-date/index.js",
78
78
  "./nile-split-panel": "./dist/src/nile-split-panel/index.js",
79
79
  "./nile-list": "./dist/src/nile-list/index.js",
80
- "./nile-list-item": "./dist/src/nile-list-item/index.js"
80
+ "./nile-list-item": "./dist/src/nile-list-item/index.js",
81
+ "./nile-detail": "./dist/src/nile-detail/index.js"
81
82
  },
82
83
  "scripts": {
83
84
  "analyze": "cem analyze --litelement",
package/src/index.ts CHANGED
@@ -69,4 +69,5 @@ export { NileTree } from './nile-tree';
69
69
  export { NileTreeItem } from './nile-tree-item';
70
70
  export { NileListItem } from './nile-list-item';
71
71
  export { NileList } from './nile-list';
72
- export { NileDivider } from './nile-divider';
72
+ export { NileDetail } from './nile-detail';
73
+ export { NileDivider } from './nile-divider';
@@ -0,0 +1 @@
1
+ export { NileDetail } from './nile-detail';
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Copyright Aquera Inc 2023
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {css} from 'lit-element';
9
+
10
+ /**
11
+ * Detail CSS
12
+ */
13
+ export const styles = css`
14
+
15
+ :host {
16
+ box-sizing: border-box;
17
+ }
18
+
19
+ :host *,
20
+ :host *::before,
21
+ :host *::after {
22
+ box-sizing: inherit;
23
+ }
24
+
25
+ [hidden] {
26
+ display: none !important;
27
+ }
28
+
29
+ :host {
30
+ display: block;
31
+ }
32
+
33
+ .details {
34
+ border: solid 1px hsl(240 5.9% 90%);
35
+ border-radius: 0.25rem;
36
+ background-color: #FFFFFF;
37
+ overflow-anchor: none;
38
+ }
39
+
40
+ .details--disabled {
41
+ opacity: 0.5;
42
+ }
43
+
44
+ .details__header {
45
+ display: flex;
46
+ align-items: center;
47
+ border-radius: inherit;
48
+ padding: 1rem;
49
+ user-select: none;
50
+ cursor: pointer;
51
+ }
52
+
53
+ .details__header:focus {
54
+ outline: none;
55
+ }
56
+
57
+ .details__header:focus-visible {
58
+ outline: solid 3px hsl(200.4, 98%, 39.4%);
59
+ outline-offset: calc(1px + 1px);
60
+ }
61
+
62
+ .details--disabled .details__header {
63
+ cursor: not-allowed;
64
+ }
65
+
66
+ .details--disabled .details__header:focus-visible {
67
+ outline: none;
68
+ box-shadow: none;
69
+ }
70
+
71
+ .details__summary {
72
+ flex: 1 1 auto;
73
+ display: flex;
74
+ align-items: center;
75
+ }
76
+
77
+ .details__summary-icon {
78
+ flex: 0 0 auto;
79
+ display: flex;
80
+ align-items: center;
81
+ transition: 250ms rotate ease;
82
+ }
83
+
84
+ .details--open .details__summary-icon {
85
+ rotate: 90deg;
86
+ }
87
+
88
+ .details--open.details--rtl .details__summary-icon {
89
+ rotate: -90deg;
90
+ }
91
+
92
+ .details--open slot[name='expand-icon'],
93
+ .details:not(.details--open) slot[name='collapse-icon'] {
94
+ display: none;
95
+ }
96
+
97
+ .details__body {
98
+ overflow: hidden;
99
+ }
100
+
101
+ .details__content {
102
+ display: block;
103
+ padding: 1rem;
104
+ }
105
+ `;
106
+
107
+ export default [styles];
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Copyright Aquera Inc 2023
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {LitElement, html, property, CSSResultArray, TemplateResult} from 'lit-element';
9
+ import {styles} from './nile-detail.css';
10
+ import NileElement from '../internal/nile-element';
11
+ import { animateTo, shimKeyframesHeightAuto, stopAnimations } from '../internal/animate';
12
+ import { classMap } from 'lit/directives/class-map.js';
13
+ import { customElement, query } from 'lit/decorators.js';
14
+ import { getAnimation, setDefaultAnimation } from '../utilities/animation-registry';
15
+ import { waitForEvent } from '../internal/event';
16
+ import { watch } from '../internal/watch';
17
+ import type { CSSResultGroup } from 'lit';
18
+
19
+ /**
20
+ * Nile detail component.
21
+ *
22
+ * @tag nile-accordion
23
+ *
24
+ */
25
+ @customElement('nile-accordion')
26
+ export class NileDetail extends NileElement {
27
+
28
+ /**
29
+ * @summary Details show a brief summary and expand to show additional content.
30
+ *
31
+ * @dependency nile-icon
32
+ *
33
+ * @slot - The details' main content.
34
+ * @slot summary - The details' summary. Alternatively, you can use the `summary` attribute.
35
+ * @slot expand-icon - Optional expand icon to use instead of the default. Works best with `<nile-icon>`.
36
+ * @slot collapse-icon - Optional collapse icon to use instead of the default. Works best with `<nile-icon>`.
37
+ *
38
+ * @event nile-show - Emitted when the details opens.
39
+ * @event nile-after-show - Emitted after the details opens and all animations are complete.
40
+ * @event nile-hide - Emitted when the details closes.
41
+ * @event nile-after-hide - Emitted after the details closes and all animations are complete.
42
+ *
43
+ * @csspart base - The component's base wrapper.
44
+ * @csspart header - The header that wraps both the summary and the expand/collapse icon.
45
+ * @csspart summary - The container that wraps the summary.
46
+ * @csspart summary-icon - The container that wraps the expand/collapse icons.
47
+ * @csspart content - The details content.
48
+ *
49
+ * @animation details.show - The animation to use when showing details. You can use `height: auto` with this animation.
50
+ * @animation details.hide - The animation to use when hiding details. You can use `height: auto` with this animation.
51
+ */
52
+
53
+ static styles: CSSResultGroup = styles;
54
+
55
+
56
+ @query('.details') details: HTMLElement;
57
+ @query('.details__header') header: HTMLElement;
58
+ @query('.details__body') body: HTMLElement;
59
+ @query('.details__expand-icon-slot') expandIconSlot: HTMLSlotElement;
60
+
61
+ /**
62
+ * Indicates whether or not the details is open. You can toggle this attribute to show and hide the details, or you
63
+ * can use the `show()` and `hide()` methods and this attribute will reflect the details' open state.
64
+ */
65
+ @property({ type: Boolean, reflect: true }) open = false;
66
+
67
+ /** The summary to show in the header. If you need to display HTML, use the `summary` slot instead. */
68
+ @property() summary: string;
69
+
70
+ /** Disables the details so it can't be toggled. */
71
+ @property({ type: Boolean, reflect: true }) disabled = false;
72
+
73
+ firstUpdated() {
74
+ this.body.hidden = !this.open;
75
+ this.body.style.height = this.open ? 'auto' : '0';
76
+ }
77
+
78
+ private handleSummaryClick() {
79
+ if (!this.disabled) {
80
+ if (this.open) {
81
+ this.hide();
82
+ } else {
83
+ this.show();
84
+ }
85
+
86
+ this.header.focus();
87
+ }
88
+ }
89
+
90
+ private handleSummaryKeyDown(event: KeyboardEvent) {
91
+ if (event.key === 'Enter' || event.key === ' ') {
92
+ event.preventDefault();
93
+
94
+ if (this.open) {
95
+ this.hide();
96
+ } else {
97
+ this.show();
98
+ }
99
+ }
100
+
101
+ if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {
102
+ event.preventDefault();
103
+ this.hide();
104
+ }
105
+
106
+ if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {
107
+ event.preventDefault();
108
+ this.show();
109
+ }
110
+ }
111
+
112
+ @watch('open', { waitUntilFirstUpdate: true })
113
+ async handleOpenChange() {
114
+ if (this.open) {
115
+ // Show
116
+ const nileShow = this.emit('nile-show', { cancelable: true });
117
+ if (nileShow.defaultPrevented) {
118
+ this.open = false;
119
+ return;
120
+ }
121
+
122
+ await stopAnimations(this.body);
123
+ this.body.hidden = false;
124
+
125
+ const { keyframes, options } = getAnimation(this, 'details.show', { dir: 'ltr' });
126
+ await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options);
127
+ this.body.style.height = 'auto';
128
+
129
+ this.emit('nile-after-show');
130
+ } else {
131
+ // Hide
132
+ const nileHide = this.emit('nile-hide', { cancelable: true });
133
+ if (nileHide.defaultPrevented) {
134
+ this.open = true;
135
+ return;
136
+ }
137
+
138
+ await stopAnimations(this.body);
139
+
140
+ const { keyframes, options } = getAnimation(this, 'details.hide', { dir: 'ltr' });
141
+ await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options);
142
+ this.body.hidden = true;
143
+ this.body.style.height = 'auto';
144
+
145
+ this.emit('nile-after-hide');
146
+ }
147
+ }
148
+
149
+ /** Shows the details. */
150
+ async show() {
151
+ if (this.open || this.disabled) {
152
+ return undefined;
153
+ }
154
+
155
+ this.open = true;
156
+ return waitForEvent(this, 'nile-after-show');
157
+ }
158
+
159
+ /** Hides the details */
160
+ async hide() {
161
+ if (!this.open || this.disabled) {
162
+ return undefined;
163
+ }
164
+
165
+ this.open = false;
166
+ return waitForEvent(this, 'nile-after-hide');
167
+ }
168
+
169
+ render() {
170
+ const isRtl = false;
171
+
172
+ return html`
173
+ <div
174
+ part="base"
175
+ class=${classMap({
176
+ details: true,
177
+ 'details--open': this.open,
178
+ 'details--disabled': this.disabled,
179
+ 'details--rtl': isRtl
180
+ })}
181
+ >
182
+ <div
183
+ part="header"
184
+ id="header"
185
+ class="details__header"
186
+ role="button"
187
+ aria-expanded=${this.open ? 'true' : 'false'}
188
+ aria-controls="content"
189
+ aria-disabled=${this.disabled ? 'true' : 'false'}
190
+ tabindex=${this.disabled ? '-1' : '0'}
191
+ @click=${this.handleSummaryClick}
192
+ @keydown=${this.handleSummaryKeyDown}
193
+ >
194
+ <slot name="summary" part="summary" class="details__summary">${this.summary}</slot>
195
+
196
+ <span part="summary-icon" class="details__summary-icon">
197
+ <slot name="expand-icon">
198
+ <nile-icon name="arrowright"></nile-icon>
199
+ </slot>
200
+ <slot name="collapse-icon">
201
+ <nile-icon name="arrowright"></nile-icon>
202
+ </slot>
203
+ </span>
204
+ </div>
205
+
206
+ <div class="details__body" role="region" aria-labelledby="header">
207
+ <slot part="content" id="content" class="details__content"></slot>
208
+ </div>
209
+ </div>
210
+ `;
211
+ }
212
+ }
213
+
214
+ setDefaultAnimation('details.show', {
215
+ keyframes: [
216
+ { height: '0', opacity: '0' },
217
+ { height: 'auto', opacity: '1' }
218
+ ],
219
+ options: { duration: 250, easing: 'linear' }
220
+ });
221
+
222
+ setDefaultAnimation('details.hide', {
223
+ keyframes: [
224
+ { height: 'auto', opacity: '1' },
225
+ { height: '0', opacity: '0' }
226
+ ],
227
+ options: { duration: 250, easing: 'linear' }
228
+ });
229
+
230
+ export default NileDetail;
231
+
232
+ declare global {
233
+ interface HTMLElementTagNameMap {
234
+ 'nile-accordion': NileDetail;
235
+ }
236
+ }