@operato/layout 8.0.0-alpha.51 → 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.
@@ -1,60 +0,0 @@
1
- import { Action } from 'redux'
2
-
3
- export const OPEN_SNACKBAR = 'OPEN_SNACKBAR'
4
- export const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR'
5
-
6
- var snackbarTimer: number
7
-
8
- export type NotificationLevel = 'info' | 'warn' | 'error'
9
- export type Notification = {
10
- message: string
11
- level?: NotificationLevel
12
- option?: any
13
- ex?: any
14
- }
15
-
16
- export function notify(notification: string | Notification): void {
17
- if (typeof notification === 'string') {
18
- return notify({ message: notification })
19
- }
20
-
21
- var { message, level, option, ex } = notification as Notification
22
-
23
- document.dispatchEvent(
24
- new CustomEvent('notify', {
25
- detail: { message, level, ex, option }
26
- })
27
- )
28
- }
29
-
30
- export type SnackbarAction = {
31
- type: 'OPEN_SNACKBAR' | 'CLOSE_SNACKBAR'
32
- level?: NotificationLevel
33
- message?: string
34
- action?: any
35
- }
36
-
37
- export const showSnackbar =
38
- (
39
- level: NotificationLevel,
40
- {
41
- message,
42
- action,
43
- timer = 5000
44
- }: {
45
- message: string
46
- action?: any
47
- timer?: number
48
- }
49
- ) =>
50
- (dispatch: (param: SnackbarAction) => Action<any>) => {
51
- dispatch({
52
- type: OPEN_SNACKBAR,
53
- level,
54
- message,
55
- action
56
- })
57
- window.clearTimeout(snackbarTimer)
58
-
59
- if (timer != -1) snackbarTimer = window.setTimeout(() => dispatch({ type: CLOSE_SNACKBAR }), timer)
60
- }
@@ -1,137 +0,0 @@
1
- import { LitElement, css, html } from 'lit'
2
-
3
- import { customElement } from 'lit/decorators.js'
4
-
5
- @customElement('ox-resize-splitter')
6
- export class ResizeSplitter extends LitElement {
7
- static styles = css`
8
- :host {
9
- position: relative;
10
- opacity: 0.7;
11
- background-color: var(--splitter-background-color);
12
- }
13
-
14
- :host(:hover) {
15
- background-color: var(--splitter-hover-background-color);
16
- }
17
-
18
- div {
19
- position: absolute;
20
- width: 100%;
21
- height: 100%;
22
- }
23
- `
24
-
25
- private dragstart?: { x: number; y: number }
26
-
27
- connectedCallback() {
28
- super.connectedCallback()
29
-
30
- if (this.hasAttribute('vertical')) {
31
- this.style.width = '3px'
32
- this.style.height = '100%'
33
- this.style.cursor = 'col-resize'
34
- } else {
35
- this.style.width = '100%'
36
- this.style.height = '3px'
37
- this.style.cursor = 'row-resize'
38
- }
39
- }
40
-
41
- render() {
42
- return html` <div @mousedown=${(e: MouseEvent) => this.onMouseDown(e)}></div> `
43
- }
44
-
45
- // TODO onDrag 이벤트가 계속 발생하므로 처리하는 성능 저하됨. 그래서 throttling 하도록 함
46
- _throttled(delay: number, fn: (...args: any) => void) {
47
- let lastCall = 0
48
- return function (...args: any) {
49
- const now = new Date().getTime()
50
- if (now - lastCall < delay) {
51
- return
52
- }
53
- lastCall = now
54
- return fn(...args)
55
- }
56
- }
57
-
58
- private onMouseDown(e: MouseEvent) {
59
- this.dragstart = {
60
- x: e.clientX,
61
- y: e.clientY
62
- }
63
-
64
- this.dispatchEvent(
65
- new CustomEvent('splitter-dragstart', {
66
- bubbles: true,
67
- composed: true
68
- })
69
- )
70
-
71
- window.addEventListener('mousemove', this.onMouseMoveHandler)
72
- window.addEventListener('mouseup', this.onMouseUpHandler)
73
-
74
- e.stopPropagation()
75
- }
76
-
77
- onMouseMoveHandler = this._throttled(100, this.onMouseMove.bind(this)) // this.onMouseMove.bind(this)
78
- onMouseUpHandler = this.onMouseUp.bind(this)
79
-
80
- private onMouseMove(e: MouseEvent) {
81
- if (!this.dragstart) {
82
- return
83
- }
84
-
85
- e.preventDefault()
86
-
87
- this.dispatchEvent(
88
- new CustomEvent('splitter-drag', {
89
- bubbles: true,
90
- composed: true,
91
- detail: {
92
- x: e.clientX - this.dragstart!.x,
93
- y: e.clientY - this.dragstart!.y
94
- }
95
- })
96
- )
97
- requestAnimationFrame(() => {
98
- dispatchEvent(new Event('resize'))
99
- })
100
-
101
- if (this.hasAttribute('vertical')) {
102
- document.body.style.cursor = 'col-resize'
103
- } else {
104
- document.body.style.cursor = 'row-resize'
105
- }
106
-
107
- document.body.style.userSelect = 'none'
108
-
109
- e.stopPropagation()
110
- }
111
-
112
- private onMouseUp(e: MouseEvent) {
113
- window.removeEventListener('mousemove', this.onMouseMoveHandler)
114
- window.removeEventListener('mouseup', this.onMouseUpHandler)
115
-
116
- this.dispatchEvent(
117
- new CustomEvent('splitter-dragend', {
118
- bubbles: true,
119
- composed: true,
120
- detail: {
121
- x: e.clientX - this.dragstart!.x,
122
- y: e.clientY - this.dragstart!.y
123
- }
124
- })
125
- )
126
-
127
- requestAnimationFrame(() => {
128
- dispatchEvent(new Event('resize'))
129
- delete this.dragstart
130
- })
131
-
132
- document.body.style.cursor = ''
133
- document.body.style.userSelect = ''
134
-
135
- e.stopPropagation()
136
- }
137
- }
@@ -1,113 +0,0 @@
1
- import { LitElement, html, css, PropertyValues, nothing } from 'lit'
2
- import { customElement, property, query } from 'lit/decorators.js'
3
-
4
- @customElement('ox-split-pane')
5
- export class OxSplitPane extends LitElement {
6
- static styles = css`
7
- :host {
8
- display: flex;
9
- flex-direction: var(--flex-direction, column);
10
-
11
- width: 100%;
12
- height: 100%;
13
- }
14
-
15
- #splitter {
16
- background-color: #ccc;
17
- cursor: var(--cursor-shape, col-resize);
18
- }
19
-
20
- #splitter {
21
- background-color: #ccc;
22
- cursor: var(--cursor-shape, col-resize);
23
- color: var(--md-sys-color-on-primary-container, #ccc);
24
- }
25
-
26
- :host([direction='row']) #splitter {
27
- height: 100%;
28
- width: 4px;
29
- }
30
-
31
- :host([direction='column']) #splitter {
32
- width: 100%;
33
- height: 4px;
34
- }
35
-
36
- ::slotted(*) {
37
- overflow: hidden;
38
- }
39
-
40
- slot[name='front']::slotted(*) {
41
- flex: var(--split-ratio, 0.5);
42
- }
43
-
44
- slot[name='back']::slotted(*) {
45
- flex: calc(1 - var(--split-ratio, 0.5));
46
- }
47
- `
48
-
49
- @property({ type: String, attribute: true }) direction: string = 'row'
50
- @property({ type: Number, attribute: true }) ratio: number = 0.5
51
- @property({ type: Boolean, attribute: true }) resizable: boolean = false
52
-
53
- @query('#splitter') splitter!: HTMLDivElement
54
-
55
- render() {
56
- return html`
57
- <slot name="front"></slot>
58
- ${this.resizable
59
- ? html`<div id="splitter" @mousedown=${this.startDragging} direction=${this.direction}></div>`
60
- : nothing}
61
- <slot name="back"></slot>
62
- `
63
- }
64
-
65
- async updated(changes: PropertyValues<this>) {
66
- if (changes.has('direction')) {
67
- this.style.setProperty('--flex-direction', this.direction)
68
- this.style.setProperty('--cursor-shape', this.direction == 'row' ? 'col-resize' : 'row-resize')
69
- }
70
-
71
- if (changes.has('ratio')) {
72
- this.style.setProperty('--split-ratio', String(this.ratio))
73
- }
74
- }
75
-
76
- startDragging(e: MouseEvent) {
77
- e.stopPropagation()
78
- e.preventDefault()
79
-
80
- document.addEventListener('mousemove', this.drag)
81
- document.addEventListener('mouseup', this.stopDragging)
82
-
83
- document.body.style.cursor = this.direction == 'row' ? 'col-resize' : 'row-resize'
84
- }
85
-
86
- drag = (e: MouseEvent) => {
87
- e.stopPropagation()
88
- e.preventDefault()
89
-
90
- const { width, height, left, top } = this.getBoundingClientRect()
91
-
92
- const mouseX = e.clientX - left
93
- const mouseY = e.clientY - top
94
-
95
- if (this.direction == 'row') {
96
- const totalWidth = this.offsetWidth
97
- this.ratio = mouseX / width
98
- } else {
99
- const totalHeight = this.offsetHeight
100
- this.ratio = mouseY / height
101
- }
102
- }
103
-
104
- stopDragging = (e: MouseEvent) => {
105
- e.stopPropagation()
106
- e.preventDefault()
107
-
108
- document.removeEventListener('mousemove', this.drag)
109
- document.removeEventListener('mouseup', this.stopDragging)
110
-
111
- document.body.style.cursor = 'auto'
112
- }
113
- }
package/src/index.ts DELETED
@@ -1,16 +0,0 @@
1
- import './initializer'
2
-
3
- export { PopupHandle, PopupOptions } from '@operato/popup'
4
-
5
- export * from './layouts/ox-snack-bar.js'
6
- export * from './layouts/ox-header-bar.js'
7
- export * from './layouts/ox-nav-bar.js'
8
- export * from './layouts/ox-aside-bar.js'
9
- export * from './layouts/ox-footer-bar.js'
10
- export * from './layouts/ox-page-header-bar.js'
11
- export * from './layouts/ox-page-footer-bar.js'
12
-
13
- export * from './actions/layout'
14
- export * from './actions/snackbar'
15
-
16
- export * from './components/ox-split-pane'
@@ -1,113 +0,0 @@
1
- import { REGISTER_NAVIGATION_CALLBACK, store } from '@operato/shell'
2
- import { UPDATE_VIEWPART, closeOverlay, openOverlay, openPopup, toggleOverlay } from './actions/layout'
3
-
4
- import layout from './reducers/layout'
5
- import { showSnackbar } from './actions/snackbar'
6
- import snackbar from './reducers/snackbar'
7
-
8
- store.addReducers({
9
- layout,
10
- snackbar
11
- })
12
-
13
- document.addEventListener('open-overlay', event => {
14
- const { name, options } = (event as CustomEvent).detail
15
- openOverlay(name, options)
16
- })
17
-
18
- document.addEventListener('close-overlay', event => {
19
- const { name } = (event as CustomEvent).detail
20
- closeOverlay(name)
21
- })
22
-
23
- document.addEventListener('toggle-overlay', event => {
24
- const { name, options } = (event as CustomEvent).detail
25
- toggleOverlay(name, options)
26
- })
27
-
28
- document.addEventListener('open-popup', event => {
29
- const { template, options, callback } = (event as CustomEvent).detail
30
- var popup = openPopup(template, options)
31
- if (popup && callback) callback(popup)
32
- })
33
-
34
- document.addEventListener('notify', event => {
35
- let { message, level, ex = '', option = {} } = (event as CustomEvent).detail
36
-
37
- switch (level) {
38
- case 'error':
39
- console.error(message, ex)
40
- break
41
- case 'warn':
42
- console.warn(message, ex)
43
- break
44
- case 'info':
45
- console.info(message)
46
- break
47
- default:
48
- break
49
- }
50
-
51
- store.dispatch(
52
- showSnackbar(level, {
53
- message,
54
- ...(option as {
55
- action?: any
56
- timer?: number
57
- })
58
- }) as any
59
- )
60
- })
61
-
62
- /* overlay handling */
63
- var overlayStack: any[] = []
64
- function getLastSequence() {
65
- return overlayStack.length > 0 ? overlayStack[overlayStack.length - 1].overlay.sequence : -1
66
- }
67
-
68
- document.addEventListener('keydown', event => {
69
- if (overlayStack.length > 0 && (event.key === 'Escape' || event.key === 'Esc')) {
70
- history.state?.overlay?.escapable && history.back()
71
- }
72
- })
73
-
74
- const historyHandler = (location: string, event: Event) => {
75
- var navigated = event instanceof PopStateEvent
76
-
77
- var state = history.state
78
- var overlay = state?.overlay
79
- var sequence = overlay?.sequence || -1
80
-
81
- var lastSequence = getLastSequence()
82
-
83
- if (overlayStack.length > 0 && sequence < lastSequence) {
84
- /* overlay 관련 history가 아닌 경우. */
85
- do {
86
- let { overlay } = overlayStack.pop()
87
- store.dispatch({
88
- type: UPDATE_VIEWPART,
89
- name: overlay.name,
90
- override: { show: false }
91
- })
92
-
93
- lastSequence = getLastSequence()
94
- } while (sequence < lastSequence)
95
- }
96
-
97
- if (!navigated && overlay) {
98
- overlayStack.push({ ...state })
99
-
100
- store.dispatch({
101
- type: UPDATE_VIEWPART,
102
- name: overlay.name,
103
- override: { show: true }
104
- })
105
- }
106
- }
107
-
108
- store.dispatch({
109
- type: REGISTER_NAVIGATION_CALLBACK,
110
- callback: historyHandler
111
- })
112
-
113
- export default store
@@ -1,111 +0,0 @@
1
- import '@operato/popup/ox-floating-overlay.js'
2
- import '../components/ox-resize-splitter.js'
3
-
4
- import { css, html, LitElement } from 'lit'
5
- import { ifDefined } from 'lit/directives/if-defined.js'
6
- import { customElement, property, state } from 'lit/decorators.js'
7
- import { connect } from 'pwa-helpers/connect-mixin.js'
8
-
9
- import { ScrollbarStyles } from '@operato/styles'
10
-
11
- import { Viewpart, VIEWPART_LEVEL } from '../actions/layout.js'
12
- import store from '../initializer.js'
13
-
14
- @customElement('ox-aside-bar')
15
- class AsideBar extends connect(store)(LitElement) {
16
- static styles = [
17
- ScrollbarStyles,
18
- css`
19
- :host {
20
- display: flex;
21
- flex-flow: row-reverse nowrap;
22
- align-items: stretch;
23
-
24
- position: relative;
25
- }
26
-
27
- *[asidebar] {
28
- display: block;
29
- overflow-y: auto;
30
- }
31
- `
32
- ]
33
-
34
- @property({ type: Boolean, attribute: 'fullbleed' }) fullbleed: boolean = false
35
-
36
- @state() viewparts: { [name: string]: Viewpart } = {}
37
-
38
- private _startWidth: number = 0
39
-
40
- render() {
41
- var viewparts = this.viewparts
42
- var asidebars = Object.keys(viewparts)
43
- .map(name => {
44
- return {
45
- name,
46
- ...viewparts[name]
47
- }
48
- })
49
- .filter(viewpart => viewpart.position == 'asidebar' && (!this.fullbleed || viewpart.hovering))
50
-
51
- asidebars = [
52
- ...asidebars.filter(viewpart => viewpart.level == VIEWPART_LEVEL.TOPMOST),
53
- ...asidebars.filter(viewpart => viewpart.level !== VIEWPART_LEVEL.TOPMOST)
54
- ]
55
-
56
- return html`
57
- ${asidebars.map(asidebar =>
58
- !asidebar.show
59
- ? html``
60
- : asidebar.hovering
61
- ? html`
62
- <ox-floating-overlay
63
- .backdrop=${asidebar.backdrop}
64
- direction="left"
65
- .hovering=${asidebar.hovering}
66
- .name=${asidebar.name}
67
- .size=${asidebar.size}
68
- .title=${asidebar.title}
69
- .help=${asidebar.help}
70
- .closable=${asidebar.closable}
71
- .templateProperties=${asidebar.templateProperties}
72
- .historical=${true}
73
- .search=${asidebar.search}
74
- .filter=${asidebar.filter}
75
- z-index=${ifDefined(asidebar.zIndex)}
76
- >${asidebar.template}</ox-floating-overlay
77
- >
78
- `
79
- : html`
80
- <div asidebar>${asidebar.template}</div>
81
- ${asidebar.resizable
82
- ? html`
83
- <ox-resize-splitter
84
- @splitter-dragstart=${(e: CustomEvent) => this.resizeStart(e)}
85
- @splitter-drag=${(e: CustomEvent) => this.resizeDrag(e)}
86
- vertical
87
- ></ox-resize-splitter>
88
- `
89
- : html``}
90
- `
91
- )}
92
- `
93
- }
94
-
95
- resizeStart(e: CustomEvent) {
96
- this._startWidth = ((e.target as HTMLElement)?.previousElementSibling as HTMLElement)?.offsetWidth
97
- }
98
-
99
- resizeDrag(e: CustomEvent) {
100
- var delta = e.detail
101
-
102
- var x = ((e.target as HTMLElement)?.previousElementSibling as HTMLElement)?.children
103
- Array.from(x).forEach(ele => {
104
- ;(ele as HTMLElement).style.width = `${this._startWidth - delta.x}px`
105
- })
106
- }
107
-
108
- stateChanged(state: any) {
109
- this.viewparts = state.layout.viewparts || {}
110
- }
111
- }
@@ -1,112 +0,0 @@
1
- import '@operato/popup/ox-floating-overlay.js'
2
- import '../components/ox-resize-splitter.js'
3
-
4
- import { css, html, LitElement } from 'lit'
5
- import { ifDefined } from 'lit/directives/if-defined.js'
6
- import { customElement, property, state } from 'lit/decorators.js'
7
- import { connect } from 'pwa-helpers/connect-mixin.js'
8
-
9
- import { Viewpart, VIEWPART_LEVEL } from '../actions/layout.js'
10
- import store from '../initializer.js'
11
-
12
- @customElement('ox-footer-bar')
13
- class FooterBar extends connect(store)(LitElement) {
14
- static styles = [
15
- css`
16
- :host {
17
- display: flex;
18
- flex-flow: column-reverse nowrap;
19
- align-items: stretch;
20
- position: relative;
21
- }
22
-
23
- *[footerbar] {
24
- display: block;
25
- max-width: 100vw;
26
- }
27
-
28
- @media screen and (max-width: 460px) {
29
- :host {
30
- padding-bottom: 0;
31
- }
32
- }
33
- `
34
- ]
35
-
36
- @property({ type: Boolean, attribute: 'fullbleed' }) fullbleed: boolean = false
37
-
38
- @state() viewparts: { [name: string]: Viewpart } = {}
39
-
40
- private _startHeight: number = 0
41
-
42
- render() {
43
- var viewparts = this.viewparts
44
- var footerbars = Object.keys(viewparts)
45
- .map(name => {
46
- return {
47
- name,
48
- ...viewparts[name]
49
- }
50
- })
51
- .filter(viewpart => viewpart.position == 'footerbar' && (!this.fullbleed || viewpart.hovering))
52
-
53
- footerbars = [
54
- ...footerbars.filter(viewpart => viewpart.level == VIEWPART_LEVEL.TOPMOST),
55
- ...footerbars.filter(viewpart => viewpart.level !== VIEWPART_LEVEL.TOPMOST)
56
- ]
57
-
58
- return html`
59
- ${footerbars.map(footerbar =>
60
- !footerbar.show
61
- ? html``
62
- : footerbar.hovering
63
- ? html`
64
- <ox-floating-overlay
65
- .backdrop=${footerbar.backdrop}
66
- direction="up"
67
- .hovering=${footerbar.hovering}
68
- .name=${footerbar.name}
69
- .title=${footerbar.title || ''}
70
- .help=${footerbar.help}
71
- .size=${footerbar.size}
72
- .closable=${footerbar.closable}
73
- .templateProperties=${footerbar.templateProperties}
74
- .historical=${true}
75
- .search=${footerbar.search}
76
- .filter=${footerbar.filter}
77
- z-index=${ifDefined(footerbar.zIndex)}
78
- >${footerbar.template}</ox-floating-overlay
79
- >
80
- `
81
- : html`
82
- <div footerbar>${footerbar.template}</div>
83
- ${footerbar.resizable
84
- ? html`
85
- <ox-resize-splitter
86
- @splitter-dragstart=${(e: CustomEvent) => this.resizeStart(e)}
87
- @splitter-drag=${(e: CustomEvent) => this.resizeDrag(e)}
88
- ></ox-resize-splitter>
89
- `
90
- : html``}
91
- `
92
- )}
93
- `
94
- }
95
-
96
- resizeStart(e: CustomEvent) {
97
- this._startHeight = ((e.target as HTMLElement)?.previousElementSibling as HTMLElement).offsetHeight
98
- }
99
-
100
- resizeDrag(e: CustomEvent) {
101
- var delta = e.detail
102
-
103
- var x = ((e.target as HTMLElement)?.previousElementSibling as HTMLElement).children
104
- Array.from(x).forEach(ele => {
105
- ;(ele as HTMLElement).style.height = `${this._startHeight - delta.y}px`
106
- })
107
- }
108
-
109
- stateChanged(state: any) {
110
- this.viewparts = state.layout.viewparts || {}
111
- }
112
- }