@operato/context 2.0.0-alpha.9 → 2.0.0-alpha.90

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 (62) hide show
  1. package/CHANGELOG.md +462 -0
  2. package/demo/index.html +13 -2
  3. package/dist/src/layouts/ox-context-page-toolbar.d.ts +9 -4
  4. package/dist/src/layouts/ox-context-page-toolbar.js +30 -10
  5. package/dist/src/layouts/ox-context-page-toolbar.js.map +1 -1
  6. package/dist/src/layouts/ox-context-toolbar.d.ts +5 -3
  7. package/dist/src/layouts/ox-context-toolbar.js +139 -73
  8. package/dist/src/layouts/ox-context-toolbar.js.map +1 -1
  9. package/dist/src/styles/ox-context-toolbar-overlay-style.js +2 -2
  10. package/dist/src/styles/ox-context-toolbar-overlay-style.js.map +1 -1
  11. package/dist/src/tools/ox-page-action-context-bar.d.ts +12 -5
  12. package/dist/src/tools/ox-page-action-context-bar.js +43 -48
  13. package/dist/src/tools/ox-page-action-context-bar.js.map +1 -1
  14. package/dist/src/tools/ox-page-action-overlay-template.d.ts +2 -2
  15. package/dist/src/tools/ox-page-action-overlay-template.js +22 -31
  16. package/dist/src/tools/ox-page-action-overlay-template.js.map +1 -1
  17. package/dist/src/tools/ox-page-title-bar.d.ts +2 -2
  18. package/dist/src/tools/ox-page-title-bar.js +6 -8
  19. package/dist/src/tools/ox-page-title-bar.js.map +1 -1
  20. package/dist/src/tools/ox-title-bar.d.ts +2 -2
  21. package/dist/src/tools/ox-title-bar.js +6 -8
  22. package/dist/src/tools/ox-title-bar.js.map +1 -1
  23. package/dist/stories/ox-context-page-toolbar-select-buttons.stories.d.ts +25 -0
  24. package/dist/stories/ox-context-page-toolbar-select-buttons.stories.js +127 -0
  25. package/dist/stories/ox-context-page-toolbar-select-buttons.stories.js.map +1 -0
  26. package/dist/stories/ox-context-page-toolbar-select.stories.d.ts +24 -0
  27. package/dist/stories/ox-context-page-toolbar-select.stories.js +85 -0
  28. package/dist/stories/ox-context-page-toolbar-select.stories.js.map +1 -0
  29. package/dist/stories/ox-context-page-toolbar.stories.d.ts +24 -0
  30. package/dist/stories/ox-context-page-toolbar.stories.js +93 -0
  31. package/dist/stories/ox-context-page-toolbar.stories.js.map +1 -0
  32. package/dist/stories/ox-context-toolbar-complex.stories.d.ts +19 -0
  33. package/dist/stories/ox-context-toolbar-complex.stories.js +150 -0
  34. package/dist/stories/ox-context-toolbar-complex.stories.js.map +1 -0
  35. package/dist/stories/ox-context-toolbar-empty.stories.d.ts +19 -0
  36. package/dist/stories/ox-context-toolbar-empty.stories.js +88 -0
  37. package/dist/stories/ox-context-toolbar-empty.stories.js.map +1 -0
  38. package/dist/stories/ox-context-toolbar-overflow.stories.d.ts +19 -0
  39. package/dist/stories/ox-context-toolbar-overflow.stories.js +131 -0
  40. package/dist/stories/ox-context-toolbar-overflow.stories.js.map +1 -0
  41. package/dist/stories/ox-context-toolbar.stories.d.ts +19 -0
  42. package/dist/stories/ox-context-toolbar.stories.js +188 -0
  43. package/dist/stories/ox-context-toolbar.stories.js.map +1 -0
  44. package/dist/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +20 -20
  46. package/src/layouts/ox-context-page-toolbar.ts +33 -10
  47. package/src/layouts/ox-context-toolbar.ts +140 -70
  48. package/src/styles/ox-context-toolbar-overlay-style.ts +2 -2
  49. package/src/tools/ox-page-action-context-bar.ts +71 -67
  50. package/src/tools/ox-page-action-overlay-template.ts +42 -51
  51. package/src/tools/ox-page-title-bar.ts +6 -8
  52. package/src/tools/ox-title-bar.ts +6 -8
  53. package/stories/ox-context-page-toolbar-select-buttons.stories.ts +141 -0
  54. package/stories/ox-context-page-toolbar-select.stories.ts +99 -0
  55. package/stories/ox-context-page-toolbar.stories.ts +107 -0
  56. package/stories/ox-context-toolbar-complex.stories.ts +162 -0
  57. package/stories/ox-context-toolbar-empty.stories.ts +100 -0
  58. package/stories/ox-context-toolbar-overflow.stories.ts +143 -0
  59. package/stories/ox-context-toolbar.stories.ts +200 -0
  60. package/themes/app-theme.css +161 -0
  61. package/themes/context-theme.css +74 -0
  62. package/themes/material-theme.css +38 -0
@@ -1,6 +1,6 @@
1
- import '@material/mwc-icon'
1
+ import '@material/web/icon/icon.js'
2
2
 
3
- import { css, html, LitElement, nothing } from 'lit'
3
+ import { css, html, LitElement, TemplateResult, nothing } from 'lit'
4
4
  import { customElement, property, query, state } from 'lit/decorators.js'
5
5
  import { connect } from 'pwa-helpers/connect-mixin.js'
6
6
 
@@ -23,9 +23,7 @@ export class ContextPageToolbar extends connect(store)(LitElement) {
23
23
  --context-action-bar-button-margin: 0px;
24
24
  --context-action-bar-more-button-border-radius: 2px;
25
25
 
26
- --mdc-icon-size: 20px;
27
- --mdc-button-height: 20px;
28
- --mdc-button-horizontal-padding: 2px;
26
+ --md-icon-size: 20px;
29
27
  }
30
28
 
31
29
  :host > div {
@@ -33,24 +31,47 @@ export class ContextPageToolbar extends connect(store)(LitElement) {
33
31
  flex-direction: row;
34
32
 
35
33
  align-items: center;
34
+ gap: var(--padding-narrow);
36
35
  }
37
36
 
38
37
  * {
39
38
  cursor: pointer;
40
39
  }
41
40
 
42
- mwc-icon {
43
- background-color: var(--theme-white-color);
41
+ md-icon {
42
+ background-color: var(--button-background-color, #fafbfc);
44
43
  border: 1px solid rgba(var(--primary-color-rgb), 0.5);
45
- padding: 1px 2px;
44
+ border-radius: var(--button-border-radius);
45
+ padding: var(--iconbtn-padding, 4px);
46
+
46
47
  color: var(--primary-color);
48
+
49
+ &:hover {
50
+ background-color: var(--primary-color);
51
+
52
+ color: var(--theme-white-color);
53
+ }
54
+ }
55
+
56
+ @media screen and (max-width: 460px) {
57
+ :host > div {
58
+ gap: 0;
59
+ }
60
+
61
+ md-icon {
62
+ border-radius: 0;
63
+ }
47
64
  }
48
65
  `
49
66
  ]
50
67
  }
51
68
 
52
69
  @property({ type: Object }) context: any
53
- @state() private tools?: any[]
70
+ @property({ type: Array }) tools?: {
71
+ position: TOOL_POSITION
72
+ template: TemplateResult
73
+ context?: string
74
+ }[]
54
75
 
55
76
  render() {
56
77
  let contextKeys = Object.keys(this.context).filter(key => !!this.context[key])
@@ -84,6 +105,8 @@ export class ContextPageToolbar extends connect(store)(LitElement) {
84
105
  }
85
106
 
86
107
  stateChanged(state: any) {
87
- this.tools = state.context.tools
108
+ if (state.context) {
109
+ this.tools = state.context?.tools
110
+ }
88
111
  }
89
112
  }
@@ -1,6 +1,6 @@
1
- import '@material/mwc-icon'
1
+ import '@material/web/icon/icon.js'
2
2
 
3
- import { css, html, LitElement, nothing } from 'lit'
3
+ import { css, html, LitElement, nothing, PropertyValues } from 'lit'
4
4
  import { customElement, property, query, state } from 'lit/decorators.js'
5
5
  import { connect } from 'pwa-helpers/connect-mixin.js'
6
6
 
@@ -14,77 +14,107 @@ export class ContextToolbar extends connect(store)(LitElement) {
14
14
  css`
15
15
  :host {
16
16
  display: flex;
17
- position: relative;
18
17
 
19
18
  background-color: var(--context-toolbar-background-color);
20
- justify-content: space-between;
21
19
  box-shadow: var(--context-toolbar-shadow-line);
22
- }
23
- #front-end {
24
- padding-left: var(--context-toolbar-side-padding);
25
- }
26
- #rear-end {
27
- padding-right: var(--context-toolbar-side-padding);
28
- }
29
- #front-end mwc-icon,
30
- #front mwc-icon {
31
- background-color: var(--context-toolbar-function-button-background-color);
32
- border: var(--context-toolbar-function-button-border);
33
- height: var(--context-toolbar-function-button-height);
34
- margin: var(--context-toolbar-button-margin);
35
- padding: var(--context-toolbar-function-button-padding);
36
- color: var(--context-toolbar-function-button-color);
37
- line-height: var(--context-toolbar-function-button-lineheight);
38
- cursor: pointer;
39
- }
40
- #front-end mwc-icon:first-child,
41
- #front mwc-icon:first-child {
42
- border-top-left-radius: var(--context-toolbar-function-button-radius);
43
- border-bottom-left-radius: var(--context-toolbar-function-button-radius);
44
- }
45
- #front-end mwc-icon:last-child,
46
- #front mwc-icon:last-child {
47
- border-top-right-radius: var(--context-toolbar-function-button-radius);
48
- border-bottom-right-radius: var(--context-toolbar-function-button-radius);
49
- }
50
20
 
51
- #front-end mwc-icon {
52
- background-color: var(--primary-color);
53
- }
54
- #front-end mwc-icon:hover {
55
- background-color: var(--context-toolbar-function-button-background-color);
56
- }
57
- #front mwc-icon:hover {
58
- background-color: var(--context-toolbar-function-button-hover-background-color);
21
+ padding: 0;
22
+
23
+ --md-icon-size: 20px;
59
24
  }
60
25
 
61
- #center {
26
+ div[container] {
62
27
  flex: 1;
63
- align-items: center;
64
- justify-content: end;
65
- }
66
28
 
67
- #center > * {
68
- align-items: center;
69
- }
70
- #rear-end > *,
71
- #rear > * {
72
- margin: var(--context-toolbar-button-margin);
73
- }
74
- #rear-end {
75
- align-items: start;
76
- }
29
+ display: flex;
30
+ position: relative;
31
+ overflow: hidden;
32
+
33
+ justify-content: space-between;
77
34
 
78
- div > * {
79
- align-items: center;
35
+ gap: var(--padding-narrow);
36
+ padding: 6px 10px;
37
+
38
+ &:not(:has(> *)) {
39
+ padding: 0;
40
+ }
41
+
42
+ div {
43
+ display: flex;
44
+ flex-direction: row;
45
+ flex-wrap: nowrap;
46
+
47
+ align-items: center;
48
+ overflow: hidden;
49
+ padding: 0;
50
+ }
51
+
52
+ #front-end md-icon,
53
+ #front md-icon {
54
+ background-color: var(--context-toolbar-function-button-background-color);
55
+ border: var(--context-toolbar-function-button-border);
56
+ color: var(--context-toolbar-function-button-color);
57
+ }
58
+
59
+ #front-end md-icon {
60
+ background-color: var(--primary-color);
61
+ }
62
+ #front-end md-icon:hover {
63
+ background-color: var(--context-toolbar-function-button-background-color);
64
+ }
65
+ #front md-icon:hover {
66
+ background-color: var(--context-toolbar-function-button-hover-background-color);
67
+ }
68
+
69
+ md-icon {
70
+ background-color: var(--button-background-color, #fafbfc);
71
+ border: 1px solid rgba(var(--primary-color-rgb), 0.5);
72
+ border-radius: var(--button-border-radius);
73
+ padding: var(--iconbtn-padding, 2px);
74
+
75
+ color: var(--primary-color);
76
+
77
+ &:hover {
78
+ background-color: var(--primary-color);
79
+
80
+ color: var(--theme-white-color);
81
+ }
82
+ }
83
+
84
+ div md-icon {
85
+ border-radius: 0;
86
+ }
87
+
88
+ div md-icon:first-child {
89
+ border-top-left-radius: var(--context-toolbar-function-button-radius);
90
+ border-bottom-left-radius: var(--context-toolbar-function-button-radius);
91
+ }
92
+
93
+ div md-icon:last-child {
94
+ border-top-right-radius: var(--context-toolbar-function-button-radius);
95
+ border-bottom-right-radius: var(--context-toolbar-function-button-radius);
96
+ }
97
+
98
+ #center {
99
+ flex: 1;
100
+ justify-content: end;
101
+ }
102
+
103
+ * {
104
+ cursor: pointer;
105
+ }
80
106
  }
81
107
 
82
- div {
83
- display: flex;
84
- flex-wrap: nowrap;
85
- align-items: center;
86
- overflow: hidden;
87
- padding: 0;
108
+ @media only screen and (max-width: 460px) {
109
+ div[container] {
110
+ padding: 0;
111
+ gap: 0;
112
+ }
113
+
114
+ div[container] div md-icon {
115
+ border-radius: 0;
116
+ border-right-width: 0;
117
+ }
88
118
  }
89
119
  `
90
120
  ]
@@ -93,6 +123,8 @@ export class ContextToolbar extends connect(store)(LitElement) {
93
123
  @state() private context: any
94
124
  @state() private tools?: any[]
95
125
 
126
+ @query('[container]') private container!: HTMLDivElement
127
+
96
128
  render() {
97
129
  if (this.context.toolbar === false) {
98
130
  return nothing
@@ -112,20 +144,58 @@ export class ContextToolbar extends connect(store)(LitElement) {
112
144
  let rearEndTools = tools.filter(tool => tool.position == TOOL_POSITION.REAR_END)
113
145
 
114
146
  return html`
115
- <div id="front-end">${frontEndTools.map(tool => html` ${tool.template} `)}</div>
147
+ <div container>
148
+ ${frontEndTools.length > 0
149
+ ? html`<div id="front-end">${frontEndTools.map(tool => html` ${tool.template} `)}</div>`
150
+ : nothing}
151
+ ${frontTools.length > 0
152
+ ? html`<div id="front">${frontTools.map(tool => html` ${tool.template} `)}</div>`
153
+ : nothing}
154
+ ${centerTools.length > 0
155
+ ? html`<div id="center">${centerTools.map(tool => html` ${tool.template} `)}</div>`
156
+ : nothing}
157
+ ${rearTools.length > 0
158
+ ? html`<div id="rear">${rearTools.map(tool => html` ${tool.template} `)}</div>`
159
+ : nothing}
160
+ ${rearEndTools.length > 0
161
+ ? html`<div id="rear-end">${rearEndTools.map(tool => html` ${tool.template} `)}</div>`
162
+ : nothing}
163
+ </div>
164
+ `
165
+ }
116
166
 
117
- <div id="front">${frontTools.map(tool => html` ${tool.template} `)}</div>
167
+ updated(changes: PropertyValues<this>) {
168
+ requestAnimationFrame(() => {
169
+ /*
170
+ 하위 엘리먼트들이 실제로 보일 필요가 없을 때는, 이 엘리먼트도 숨김 처리함.
171
+ &:not(:has(> *)) 처리로 모두 해결되지 않으므로, 로직 처리로 보완함.
172
+ &:not(:has(> *)) 가 필요없으나, 남겨 두었음.
173
+ */
174
+ if (!this.container) {
175
+ return
176
+ }
118
177
 
119
- <div id="center">${centerTools.map(tool => html` ${tool.template} `)}</div>
178
+ const children = this.container.querySelectorAll('*')
120
179
 
121
- <div id="rear">${rearTools.map(tool => html` ${tool.template} `)}</div>
180
+ let zero = true
122
181
 
123
- <div id="rear-end">${rearEndTools.map(tool => html` ${tool.template} `)}</div>
124
- `
182
+ // 모든 하위 엘리먼트의 높이를 확인
183
+ for (const child of Array.from(children)) {
184
+ if ((child as HTMLElement).offsetHeight !== 0) {
185
+ zero = false
186
+ break
187
+ }
188
+ }
189
+
190
+ // 모든 하위 엘리먼트의 높이가 0이면 부모 엘리먼트의 display를 none으로 변경
191
+ this.container.style.padding = zero ? '0' : '6px 10px'
192
+ })
125
193
  }
126
194
 
127
195
  stateChanged(state: any) {
128
- this.tools = state.context.tools
129
- this.context = state.route.context
196
+ if (state.context) {
197
+ this.tools = state.context.tools || []
198
+ this.context = state.route.context || {}
199
+ }
130
200
  }
131
201
  }
@@ -23,7 +23,7 @@ export const ContextToolbarOverlayStyle = css`
23
23
  padding: var(--context-ui-list-padding);
24
24
  }
25
25
 
26
- li > mwc-icon {
26
+ li > md-icon {
27
27
  font-size: 1em;
28
28
 
29
29
  padding: var(--context-ui-padding);
@@ -37,7 +37,7 @@ export const ContextToolbarOverlayStyle = css`
37
37
  color: var(--context-ui-list-color);
38
38
  }
39
39
 
40
- li:hover mwc-icon,
40
+ li:hover md-icon,
41
41
  li:hover span {
42
42
  color: #fff;
43
43
  }
@@ -1,5 +1,4 @@
1
- import '@material/mwc-button'
2
- import '@material/mwc-icon'
1
+ import '@material/web/icon/icon.js'
3
2
  import './ox-page-action-overlay-template.js'
4
3
 
5
4
  import { css, html, LitElement, PropertyValues } from 'lit'
@@ -10,63 +9,54 @@ import { connect } from 'pwa-helpers/connect-mixin'
10
9
  import { openOverlay } from '@operato/layout'
11
10
  import { store } from '@operato/shell'
12
11
  import { sleep } from '@operato/utils'
12
+ import { ButtonContainerStyles } from '@operato/styles'
13
13
 
14
14
  @customElement('ox-page-action-context-bar')
15
15
  export class PageActionContextBar extends connect(store)(LitElement) {
16
16
  static get styles() {
17
17
  return [
18
+ ButtonContainerStyles,
18
19
  css`
19
20
  :host {
20
21
  display: flex;
21
22
  overflow: hidden;
22
- position: relative;
23
+ gap: var(--padding-narrow);
23
24
  }
24
25
 
25
26
  :host *:focus {
26
27
  outline: none;
27
28
  }
28
29
 
29
- mwc-button {
30
- white-space: nowrap;
31
- margin: var(--context-action-bar-button-margin);
32
- border-radius: var(--context-action-bar-more-button-border-radius);
33
- }
34
-
35
- [danger] {
36
- --mdc-theme-primary: var(--mdc-danger-button-primary-color);
37
- --mdc-button-outline-color: var(--mdc-danger-button-outline-color);
38
- }
39
-
40
30
  [more] {
41
31
  position: absolute;
42
32
  top: 0;
43
- right: 38px;
33
+ right: 0;
44
34
  background-color: var(--context-action-bar-more-button-background-color);
45
35
  box-shadow: -2px 0px 2px 1px rgba(0, 0, 0, 0.2);
46
36
  padding: var(--context-action-bar-more-button-padding);
47
37
  color: var(--context-action-bar-more-button-color);
38
+ padding: 11px 10px;
48
39
  font-size: 2rem;
49
40
  }
50
41
 
51
- mwc-icon {
52
- background-color: var(--theme-white-color);
42
+ select,
43
+ input[type='text'] {
44
+ background-color: var(--button-background-color, #fafbfc);
53
45
  border: 1px solid rgba(var(--primary-color-rgb), 0.5);
54
- padding: 1px 2px;
55
- color: var(--primary-color);
46
+ border-radius: var(--button-border-radius);
47
+ padding: 0 var(--padding-narrow);
48
+
49
+ font-size: var(--fontsize-default);
50
+ color: var(--secondary-color);
56
51
  }
57
52
 
58
53
  @media screen and (max-width: 460px) {
59
- mwc-button {
60
- --mdc-button-horizontal-padding: var(--padding-default);
61
- --mdc-shape-small: var(--context-action-bar-more-button-border-radius);
54
+ :host {
55
+ gap: 0;
62
56
  }
63
57
 
64
58
  [more] {
65
- position: absolute;
66
- margin-top: -10px;
67
- right: 0px;
68
- padding: var(--padding-default);
69
- font-size: 2rem;
59
+ margin-top: -7px;
70
60
  }
71
61
  }
72
62
 
@@ -82,7 +72,15 @@ export class PageActionContextBar extends connect(store)(LitElement) {
82
72
  ]
83
73
  }
84
74
 
85
- @state() actions: any[] = []
75
+ @property({ type: Array }) actions: {
76
+ title: string
77
+ action: () => void
78
+ icon: string
79
+ type: string
80
+ select: string[]
81
+ href?: string
82
+ emphasis?: boolean
83
+ }[] = []
86
84
  @state() private overflown: boolean = false
87
85
 
88
86
  private overflownUpdaterThrottle: any
@@ -115,7 +113,7 @@ export class PageActionContextBar extends connect(store)(LitElement) {
115
113
  render() {
116
114
  return html`
117
115
  ${this.renderActions()}
118
- ${this.overflown ? html` <mwc-icon more @click=${this.showMore.bind(this)}>more_horiz</mwc-icon> ` : html``}
116
+ ${this.overflown ? html` <md-icon more @click=${this.showMore.bind(this)}>more_horiz</md-icon> ` : html``}
119
117
  `
120
118
  }
121
119
 
@@ -123,47 +121,49 @@ export class PageActionContextBar extends connect(store)(LitElement) {
123
121
  return html`
124
122
  ${this.actions.map(($action, idx) => {
125
123
  let { type, title, icon, action, select, href, emphasis } = $action as any
126
- let { outlined, raised, dense, danger } = emphasis || {}
124
+ let { outlined, raised, dense, danger, filled } = emphasis || {}
127
125
 
128
126
  return type == 'text'
129
127
  ? html` <span @click=${action}>${title}</span> `
130
128
  : type == 'select' || (select && select.length > 0)
131
- ? html`
132
- <select @change=${action}>
133
- ${select.map((option: any) => html` <option>${option}</option> `)}
134
- </select>
135
- `
136
- : type == 'link'
137
- ? html` <a href=${href}>${title}</a> `
138
- : type == 'icon'
139
- ? html`<mwc-icon @click=${action}>${icon}</mwc-icon>`
140
- : html`
141
- <mwc-button
142
- label=${title}
143
- icon=${icon || 'done_all'}
144
- ?dense=${dense}
145
- ?raised=${raised}
146
- ?outlined=${outlined}
147
- ?danger=${danger}
148
- @click=${async (e: MouseEvent) => {
149
- /* all actions should be bloked for a second */
150
- const button = e.currentTarget as any
151
- button.icon = 'rotate_right'
152
- button.setAttribute('working', true)
153
- button.disabled = true
154
-
155
- try {
156
- await action()
157
- } finally {
158
- await sleep(1000)
159
- button.disabled = false
160
- /* because icon can be changed after sleep, don't use closure 'icon'. */
161
- button.icon = this.actions[idx]?.icon || 'done_all'
162
- button.removeAttribute('working')
163
- }
164
- }}
165
- ></mwc-button>
166
- `
129
+ ? html`
130
+ <select @change=${action}>
131
+ ${select.map((option: any) => html` <option>${option}</option> `)}
132
+ </select>
133
+ `
134
+ : type == 'link'
135
+ ? html` <a href=${href}>${title}</a> `
136
+ : type == 'icon'
137
+ ? html`<md-icon @click=${action}>${icon}</md-icon>`
138
+ : html`
139
+ <button
140
+ ?dense=${dense}
141
+ ?raised=${raised}
142
+ ?outlined=${outlined}
143
+ ?danger=${danger}
144
+ ?filled=${filled}
145
+ @click=${async (e: MouseEvent) => {
146
+ /* all actions should be bloked for a second */
147
+ const button = e.currentTarget as any
148
+ button.icon = 'rotate_right'
149
+ button.setAttribute('working', true)
150
+ button.disabled = true
151
+
152
+ try {
153
+ await action()
154
+ } finally {
155
+ await sleep(1000)
156
+ button.disabled = false
157
+ /* because icon can be changed after sleep, don't use closure 'icon'. */
158
+ button.icon = this.actions[idx]?.icon || 'done_all'
159
+ button.removeAttribute('working')
160
+ }
161
+ }}
162
+ >
163
+ <md-icon>${icon || 'done_all'}</md-icon>
164
+ ${title}
165
+ </button>
166
+ `
167
167
  })}
168
168
  `
169
169
  }
@@ -175,7 +175,11 @@ export class PageActionContextBar extends connect(store)(LitElement) {
175
175
  }
176
176
 
177
177
  stateChanged(state: any) {
178
- this.actions = state.route?.context?.actions?.filter((action: any) => !!action) || []
178
+ const { actions } = state.route?.context || {}
179
+
180
+ if (actions) {
181
+ this.actions = actions.filter((action: any) => !!action)
182
+ }
179
183
  }
180
184
 
181
185
  showMore() {