@operato/context 7.1.31 → 7.1.33

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.
@@ -1,222 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@material/web/chips/assist-chip.js'
3
- import './ox-page-action-overlay-template.js'
4
-
5
- import { css, html, LitElement, nothing, PropertyValues } from 'lit'
6
- import { customElement, property, query, state } from 'lit/decorators.js'
7
- import throttle from 'lodash-es/throttle'
8
- import { connect } from 'pwa-helpers/connect-mixin'
9
-
10
- import { openOverlay } from '@operato/layout'
11
- import { store } from '@operato/shell'
12
- import { sleep } from '@operato/utils'
13
- import { ButtonContainerStyles } from '@operato/styles'
14
-
15
- @customElement('ox-page-action-context-bar')
16
- export class PageActionContextBar extends connect(store)(LitElement) {
17
- static get styles() {
18
- return [
19
- ButtonContainerStyles,
20
- css`
21
- :host {
22
- display: flex;
23
- position: relative;
24
- overflow-x: clip;
25
- overflow-y: hidden;
26
-
27
- text-transform: capitalize;
28
- }
29
-
30
- :host *:focus {
31
- outline: none;
32
- }
33
-
34
- [container] {
35
- flex: 1;
36
-
37
- display: flex;
38
- overflow: hidden;
39
- gap: var(--spacing-small);
40
- padding: 2px; /* only for button shadow */
41
- align-items: center;
42
- }
43
-
44
- [more] {
45
- position: absolute;
46
- bottom: 0;
47
- right: 0;
48
- color: var(--md-sys-color-inverse-on-surface);
49
- background-color: var(--md-sys-color-inverse-surface);
50
- box-shadow: -2px 0px 2px 1px rgba(0, 0, 0, 0.2);
51
- padding: var(--context-action-bar-more-button-padding, 11px 10px);
52
- font-size: 2rem;
53
- z-index: 10;
54
- }
55
-
56
- select,
57
- input[type='text'] {
58
- box-sizing: border-box;
59
- padding: 0 var(--spacing-small);
60
- height: var(--form-element-height-medium);
61
- border: 1px solid var(--md-sys-color-outline);
62
- border-radius: var(--md-sys-shape-corner-small);
63
- color: var(--md-sys-color-on-primary);
64
- background-color: var(--md-sys-color-on-primary);
65
- font-size: var(--md-sys-typescale-label-large-size, 0.875rem);
66
- outline: none;
67
- }
68
-
69
- md-icon[working] {
70
- animation: rotate 1s linear infinite;
71
- }
72
-
73
- md-assist-chip[icon-only] {
74
- --md-assist-chip-trailing-space: 0;
75
- }
76
-
77
- @media screen and (max-width: 460px) {
78
- [container] {
79
- padding: 0;
80
- gap: 0;
81
- --md-assist-chip-container-shape: 0;
82
- }
83
- }
84
-
85
- @keyframes rotate {
86
- from {
87
- transform: rotate(0deg);
88
- }
89
- to {
90
- transform: rotate(360deg);
91
- }
92
- }
93
- `
94
- ]
95
- }
96
-
97
- @property({ type: Array }) actions: {
98
- title: string
99
- action: () => void
100
- icon: string
101
- type: string
102
- select: string[]
103
- href?: string
104
- emphasis?: boolean
105
- }[] = []
106
-
107
- @state() private overflown: boolean = false
108
- @query('[container]') container?: HTMLDivElement
109
-
110
- private overflownUpdaterThrottle: any
111
- private resizeCallback: any
112
-
113
- get overflownUpdater() {
114
- if (!this.overflownUpdaterThrottle) {
115
- this.overflownUpdaterThrottle = throttle(() => {
116
- this.overflown = !!this.container && this.container.scrollWidth > this.container.clientWidth
117
- }, 100)
118
- }
119
-
120
- return this.overflownUpdaterThrottle
121
- }
122
-
123
- connectedCallback() {
124
- super.connectedCallback()
125
-
126
- this.resizeCallback = () => this.overflownUpdater()
127
-
128
- window.addEventListener('resize', this.resizeCallback)
129
- }
130
-
131
- disconnectedCallback() {
132
- super.disconnectedCallback()
133
-
134
- window.removeEventListener('resize', this.resizeCallback)
135
- }
136
-
137
- render() {
138
- return this.actions.length > 0
139
- ? html`
140
- <div container>${this.renderActions()}</div>
141
- ${this.overflown ? html` <md-icon more @click=${this.showMore.bind(this)}>more_horiz</md-icon> ` : html``}
142
- `
143
- : nothing
144
- }
145
-
146
- renderActions() {
147
- return html`
148
- ${this.actions.map(($action, idx) => {
149
- let { type, title, icon, action, select, href, emphasis } = $action as any
150
- let { raised, danger } = emphasis || {}
151
-
152
- return type == 'text'
153
- ? html` <span @click=${action}>${title}</span> `
154
- : type == 'select' || (select && select.length > 0)
155
- ? html`
156
- <select @change=${action}>
157
- ${select.map((option: any) => html` <option>${option}</option> `)}
158
- </select>
159
- `
160
- : type == 'link'
161
- ? html` <a href=${href}>${title}</a> `
162
- : html`
163
- <md-assist-chip
164
- ?icon-only=${type == 'icon' || !title}
165
- ?elevated=${raised}
166
- ?danger=${danger}
167
- label=${title}
168
- @click=${async (e: MouseEvent) => {
169
- /* all actions should be bloked for a second */
170
- const button = e.currentTarget as any
171
- const iconElement = button.querySelector('[slot="icon"]')
172
- if (iconElement) {
173
- icon = this.actions[idx].icon
174
- this.actions[idx].icon = 'refresh'
175
- this.requestUpdate()
176
- iconElement.setAttribute('working', true)
177
- }
178
- button.disabled = true
179
-
180
- try {
181
- await action()
182
- } finally {
183
- await sleep(1000) /* waiting for additionla 1 second */
184
-
185
- /* because icon can be changed after sleep, don't use closure 'icon'. */
186
- if (iconElement) {
187
- this.actions[idx].icon = icon
188
- this.requestUpdate()
189
- iconElement.removeAttribute('working')
190
- }
191
-
192
- button.disabled = false
193
- }
194
- }}
195
- >
196
- <md-icon slot="icon">${this.actions[idx].icon}</md-icon>
197
- </md-assist-chip>
198
- `
199
- })}
200
- `
201
- }
202
-
203
- async updated(changed: PropertyValues<this>) {
204
- if (changed.has('actions')) {
205
- requestAnimationFrame(this.overflownUpdater)
206
- }
207
- }
208
-
209
- stateChanged(state: any) {
210
- const { actions } = state.route?.context || {}
211
-
212
- if (actions) {
213
- this.actions = actions.filter((action: any) => !!action)
214
- }
215
- }
216
-
217
- showMore() {
218
- openOverlay('context-toolbar-overlay', {
219
- template: html`<ox-page-action-overlay-template></ox-page-action-overlay-template>`
220
- })
221
- }
222
- }
@@ -1,111 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@material/web/chips/assist-chip.js'
3
-
4
- import { css, html, LitElement } from 'lit'
5
- import { customElement, property, query, state } from 'lit/decorators.js'
6
- import { connect } from 'pwa-helpers'
7
-
8
- import { store } from '@operato/shell'
9
- import { ButtonContainerStyles, ScrollbarStyles } from '@operato/styles'
10
- import { sleep } from '@operato/utils'
11
-
12
- import { ContextToolbarOverlayStyle } from '../styles/ox-context-toolbar-overlay-style.js'
13
-
14
- @customElement('ox-page-action-overlay-template')
15
- export class PageActionOverlayTemplate extends connect(store)(LitElement) {
16
- static get styles() {
17
- return [
18
- ScrollbarStyles,
19
- ButtonContainerStyles,
20
- ContextToolbarOverlayStyle,
21
- css`
22
- :host {
23
- display: flex;
24
- flex-wrap: wrap;
25
- gap: var(--spacing-small);
26
- align-items: center;
27
- }
28
-
29
- :host *:focus {
30
- outline: none;
31
- }
32
-
33
- md-icon[working] {
34
- animation: rotate 1s linear infinite;
35
- }
36
-
37
- md-assist-chip[icon-only] {
38
- --md-assist-chip-trailing-space: 0;
39
- }
40
-
41
- @keyframes rotate {
42
- from {
43
- transform: rotate(0deg);
44
- }
45
- to {
46
- transform: rotate(360deg);
47
- }
48
- }
49
- `
50
- ]
51
- }
52
-
53
- @state() private actions: any[] = []
54
-
55
- render() {
56
- return html`
57
- ${this.actions.map(($action, idx) => {
58
- let { type, title, icon, action, select, href, emphasis } = $action as any
59
- let { raised, danger } = emphasis || {}
60
-
61
- return type == 'text'
62
- ? html` <span>${title}</span> `
63
- : type == 'select' || (select && select.length > 0)
64
- ? html`
65
- <select @change=${action}>
66
- ${select.map((option: any) => html` <option>${option}</option> `)}
67
- </select>
68
- `
69
- : type == 'link'
70
- ? html` <a href=${href}>${title}</a> `
71
- : html`
72
- <md-assist-chip
73
- ?icon-only=${type == 'icon' || !title}
74
- ?elevated=${raised}
75
- ?danger=${danger}
76
- label=${title}
77
- @click=${async (e: MouseEvent) => {
78
- /* all actions should be bloked for a second */
79
- const button = e.currentTarget as any
80
- const iconElement = button.querySelector('[slot="icon"]')
81
- if (iconElement) {
82
- iconElement.textContent = 'refresh'
83
- iconElement.setAttribute('working', true)
84
- }
85
- button.disabled = true
86
-
87
- try {
88
- await action()
89
- } finally {
90
- await sleep(1000) /* waiting for additionla 1 second */
91
-
92
- /* because icon can be changed after sleep, don't use closure 'icon'. */
93
- if (iconElement) {
94
- iconElement.textContent = this.actions[idx]?.icon
95
- iconElement.removeAttribute('working')
96
- }
97
- button.disabled = false
98
- }
99
- }}
100
- >
101
- <md-icon slot="icon">${icon || 'done_all'}</md-icon>
102
- </md-assist-chip>
103
- `
104
- })}
105
- `
106
- }
107
-
108
- stateChanged(state: any) {
109
- this.actions = state.route?.context?.actions?.filter((action: any) => !!action) || []
110
- }
111
- }
@@ -1,125 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@operato/input/ox-input-search.js'
3
-
4
- import { css, html, LitElement } from 'lit'
5
- import { customElement, state } from 'lit/decorators.js'
6
- import { connect } from 'pwa-helpers'
7
-
8
- import { openHelp } from '@operato/help'
9
- import { store } from '@operato/shell'
10
- import { isMobileDevice } from '@operato/utils'
11
-
12
- @customElement('ox-page-title-bar')
13
- export class PageTitleBar extends connect(store)(LitElement) {
14
- static styles = [
15
- css`
16
- :host {
17
- display: flex;
18
- align-items: center;
19
- text-transform: capitalize;
20
-
21
- --help-icon-color: var(--md-sys-color-on-surface);
22
- --help-icon-hover-color: var(--md-sys-color-on-surface-variant);
23
- --help-icon-opacity: 0.2;
24
- --help-icon-size: 20px;
25
-
26
- --md-icon-size: 20px;
27
-
28
- --input-search-padding: var(--spacing-small);
29
- --input-search-font: normal 14px var(--theme-font);
30
- }
31
-
32
- [titler] {
33
- display: flex;
34
- padding: var(--spacing-small);
35
- padding-top: var(--spacing-medium);
36
- font: var(--context-toolbar-title);
37
- color: var(--title-text-color);
38
- }
39
-
40
- [titler] > * {
41
- align-self: center;
42
- }
43
-
44
- [help] {
45
- opacity: var(--help-icon-opacity);
46
- font-size: var(--help-icon-size);
47
- color: var(--help-icon-color);
48
- }
49
-
50
- [help]:hover {
51
- cursor: help;
52
- opacity: var(--help-icon-hover-opacity);
53
- color: var(--help-icon-hover-color);
54
- }
55
-
56
- [search] [help] {
57
- color: var(--md-sys-color-on-secondary-container);
58
- }
59
-
60
- [search] {
61
- display: flex;
62
- justify-content: space-between;
63
- align-items: center;
64
-
65
- align-self: center;
66
- color: var(--md-sys-color-primary);
67
- background-color: var(--md-sys-color-on-primary);
68
- border-radius: var(--md-sys-shape-corner-full);
69
- margin-left: auto;
70
- padding: 0 var(--spacing-medium);
71
- }
72
-
73
- [search] > * {
74
- align-self: center;
75
- }
76
- `
77
- ]
78
-
79
- @state() private context: any
80
-
81
- mobile = isMobileDevice()
82
-
83
- render() {
84
- var { search, filter, title, help } = this.context || ({} as any)
85
- var { value = '', handler: searchHandler, placeholder = '', autofocus = true } = search || ({} as any)
86
- const { handler: filterHandler } = filter || ({} as any)
87
- const { icon, text = title } = typeof title === 'object' ? title : ({} as any)
88
- const searchable = typeof searchHandler == 'function'
89
- const filterable = typeof filterHandler == 'function'
90
-
91
- if (this.mobile && searchable && title) {
92
- placeholder ||= title
93
- title = ''
94
- }
95
-
96
- return html`
97
- ${title ? html` <div titler>${icon ? html`<md-icon>${icon}</md-icon>&nbsp;` : html``}${text}</div> ` : html``}
98
- ${help && title ? html` &nbsp;<md-icon @click=${(e: MouseEvent) => openHelp(help)} help>help</md-icon> ` : html``}
99
- ${searchable || filterable
100
- ? html`
101
- <div search>
102
- ${searchable
103
- ? html` <ox-input-search
104
- .placeholder=${placeholder}
105
- .value=${value || ''}
106
- ?autofocus=${autofocus}
107
- @change=${(e: Event) => {
108
- searchHandler((e.target as any).value)
109
- }}
110
- ></ox-input-search>`
111
- : html``}
112
- ${!title && help && searchable
113
- ? html`<md-icon @click=${(e: MouseEvent) => openHelp(help)} help>help</md-icon>`
114
- : html``}
115
- ${filterable ? html`<md-icon @click=${(e: MouseEvent) => filterHandler()}>unfold_more</md-icon>` : html``}
116
- </div>
117
- `
118
- : html``}
119
- `
120
- }
121
-
122
- stateChanged(state: any) {
123
- this.context = state.route.context
124
- }
125
- }
@@ -1,121 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@operato/input/ox-input-search.js'
3
-
4
- import { css, html, LitElement } from 'lit'
5
- import { customElement, state } from 'lit/decorators.js'
6
- import { connect } from 'pwa-helpers'
7
-
8
- import { openHelp } from '@operato/help'
9
- import { store } from '@operato/shell'
10
-
11
- @customElement('ox-title-bar')
12
- export class TitleBar extends connect(store)(LitElement) {
13
- static styles = [
14
- css`
15
- :host {
16
- display: flex;
17
- align-items: center;
18
- text-transform: capitalize;
19
-
20
- --help-icon-color: var(--md-sys-color-primary);
21
- --help-icon-hover-color: var(--md-sys-color-primary);
22
- --help-icon-opacity: 0.2;
23
- --help-icon-size: 20px;
24
-
25
- --md-icon-size: 20px;
26
-
27
- --input-search-padding: var(--spacing-medium);
28
- --input-search-focus-border-bottom: none;
29
- --input-search-font: normal 16px var(--theme-font);
30
- }
31
-
32
- [titler] {
33
- display: flex;
34
- font: var(--context-toolbar-title);
35
- }
36
-
37
- [titler] > * {
38
- align-self: center;
39
- }
40
-
41
- [help] {
42
- opacity: 0.5;
43
- font-size: var(--help-icon-size);
44
- color: var(--context-toolbar-title);
45
- }
46
-
47
- [help]:hover {
48
- cursor: help;
49
- opacity: var(--help-icon-hover-opacity);
50
- color: var(--context-toolbar-title);
51
- }
52
-
53
- [search] {
54
- display: flex;
55
- justify-content: space-between;
56
- align-items: center;
57
-
58
- align-self: center;
59
- color: var(--md-sys-color-primary);
60
- background-color: var(--md-sys-color-on-primary);
61
- border-radius: var(--md-sys-shape-corner-full);
62
- padding: 0 var(--spacing-medium);
63
- }
64
-
65
- [search] > * {
66
- align-self: center;
67
- }
68
-
69
- ox-input-search {
70
- color: var(--md-sys-color-primary);
71
- background-color: transparent;
72
- border-radius: var(--md-sys-shape-corner-full);
73
- }
74
- `
75
- ]
76
-
77
- @state() private context: any
78
-
79
- render() {
80
- var { search, filter, title, help } = this.context || ({} as any)
81
- var { value = '', handler: searchHandler, placeholder = '', autofocus = true } = search || ({} as any)
82
- const { handler: filterHandler } = filter || ({} as any)
83
- const { icon, text = title } = typeof title === 'object' ? title : ({} as any)
84
- const searchable = typeof searchHandler == 'function'
85
- const filterable = typeof filterHandler == 'function'
86
-
87
- if (searchable && title) {
88
- placeholder ||= text
89
- title = ''
90
- }
91
-
92
- return html`
93
- ${title ? html` <div titler>${icon ? html`<md-icon>${icon}</md-icon>&nbsp;` : html``}${text}</div> ` : html``}
94
- ${help && title ? html` &nbsp;<md-icon @click=${(e: MouseEvent) => openHelp(help)} help>help</md-icon> ` : html``}
95
- ${searchable || filterable
96
- ? html`
97
- <div search>
98
- ${searchable
99
- ? html` <ox-input-search
100
- .placeholder=${placeholder}
101
- .value=${value || ''}
102
- ?autofocus=${autofocus}
103
- @change=${(e: Event) => {
104
- searchHandler((e.target as any).value)
105
- }}
106
- ></ox-input-search>`
107
- : html``}
108
- ${!title && help && searchable
109
- ? html`<md-icon @click=${(e: MouseEvent) => openHelp(help)} help>help</md-icon>`
110
- : html``}
111
- ${filterable ? html`<md-icon @click=${(e: MouseEvent) => filterHandler()}>tune</md-icon>` : html``}
112
- </div>
113
- `
114
- : html``}
115
- `
116
- }
117
-
118
- stateChanged(state: any) {
119
- this.context = state.route.context
120
- }
121
- }