@operato/context 8.0.0-alpha.8 → 8.0.0-beta.1

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