@operato/shell 7.1.27 → 7.1.32

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/src/app/app.ts DELETED
@@ -1,274 +0,0 @@
1
- import { html, LitElement, PropertyValues } from 'lit'
2
- import { customElement, property, query, state } from 'lit/decorators.js'
3
- import { connect } from 'pwa-helpers/connect-mixin.js'
4
- import { installRouter } from 'pwa-helpers/router.js'
5
-
6
- import { ScrollbarStyles } from '@operato/styles'
7
- import { setContextPathPrefix, getPathInfo } from '@operato/utils'
8
-
9
- import { navigateWithSilence, UPDATE_ACTIVE_PAGE, UPDATE_CONTEXT_PATH, UPDATE_MODULES } from '../actions'
10
- import { store } from '../store'
11
- import { AppStyle } from './app-style'
12
- import { PageView } from './pages/page-view'
13
-
14
- enum MODULES_STATE {
15
- NOT_INITIALIZED,
16
- INITIALIZING,
17
- INITIALIZED
18
- }
19
-
20
- @customElement('things-app')
21
- export class ThingsApp extends connect(store)(LitElement) {
22
- static styles = [ScrollbarStyles, AppStyle]
23
-
24
- static moduleInitialized: MODULES_STATE = MODULES_STATE.NOT_INITIALIZED
25
- static modules: Array<any> = []
26
-
27
- /*
28
- 모든 모듈의 routes 리스트가 수집될 때까지, routeToPage(..) 를 hold 시키기 위해서 ThingsApp.pages를 Promise로 정의한다.
29
- */
30
- static pagesResolver: (
31
- value:
32
- | {
33
- [path: string]: string
34
- }
35
- | PromiseLike<{
36
- [path: string]: string
37
- }>
38
- ) => void
39
- static pages: Promise<{ [path: string]: string }>
40
-
41
- static callbacks: Array<any> = []
42
- static contextPath?: string
43
-
44
- @property({ type: String, attribute: 'context-path-prefix' }) contextPathPrefix?: string
45
-
46
- @state() resourceId?: string
47
- @state() page?: string
48
- @state() params?: any
49
- @state() activePage?: PageView
50
- @state() context: any
51
- @state() contextPath?: string = ThingsApp.contextPath
52
- @state() modules: Array<any> = ThingsApp.modules
53
-
54
- @query('main') private main!: HTMLElement
55
-
56
- render() {
57
- var params = this.params || {}
58
- var fullbleed = (this.context && this.context.fullbleed) || (params.fullbleed && params.fullbleed == 'Y')
59
- var widebleed = (this.context && this.context.widebleed) || (params.widebleed && params.widebleed == 'Y')
60
-
61
- return html`
62
- <div>
63
- <ox-page-header-bar header></ox-page-header-bar>
64
- <main></main>
65
- <ox-page-footer-bar footer></ox-page-footer-bar>
66
- </div>
67
-
68
- <ox-header-bar ?fullbleed=${fullbleed}></ox-header-bar>
69
-
70
- <ox-nav-bar ?fullbleed=${fullbleed || widebleed}></ox-nav-bar>
71
-
72
- <ox-aside-bar ?fullbleed=${fullbleed || widebleed}></ox-aside-bar>
73
-
74
- <ox-footer-bar ?fullbleed=${fullbleed}></ox-footer-bar>
75
-
76
- <ox-snack-bar></ox-snack-bar>
77
- `
78
- }
79
-
80
- connectedCallback() {
81
- super.connectedCallback()
82
-
83
- this.setBase()
84
-
85
- if (ThingsApp.moduleInitialized != MODULES_STATE.NOT_INITIALIZED) {
86
- /* 첫번째 이후로 생성되는 경우에는 강제로 'popstate'를 발생시켜서, routing 기회를 준다 */
87
- window.dispatchEvent(new CustomEvent('popstate'))
88
-
89
- return
90
- }
91
-
92
- ThingsApp.moduleInitialized = MODULES_STATE.INITIALIZING
93
-
94
- /* 모듈 임포트를 동적으로 처리한다. */
95
- import(
96
- /* webpackPrefetch: true */
97
- /* webpackPreload: true */
98
- /* webpackChunkName: "modules" */
99
- '../module-importer.import'
100
- ).then(module => {
101
- var modules: {
102
- name: string
103
- bootstrap: (m?: any /* self */) => void
104
- }[] = module.modules
105
-
106
- /* lifecycle - bootstrapping */
107
- modules.forEach(async (m, idx) => {
108
- try {
109
- m.bootstrap && (await m.bootstrap(m))
110
- } catch (e) {
111
- console.error(`[${idx} BOOTSTRAP ERROR -${m.name}]`, e)
112
- }
113
- })
114
-
115
- /* shouldUpdate를 활성화한다. */
116
- ThingsApp.moduleInitialized = MODULES_STATE.INITIALIZED
117
-
118
- /* modules를 store에 dispatch 함으로써, update를 invoke 시킨다. */
119
- store.dispatch({
120
- type: UPDATE_MODULES,
121
- modules
122
- })
123
-
124
- var lastPathName: string = ''
125
-
126
- installRouter(async (location, e) => {
127
- var { pathname } = location
128
- var { contextPath } = getPathInfo(pathname)
129
-
130
- /* 페이지를 나가기 전에 옮기지 않도록 개입할 기회를 준다 */
131
- if (
132
- lastPathName &&
133
- lastPathName != pathname &&
134
- this.activePage &&
135
- this.activePage.canDeactivate &&
136
- !(await this.activePage.canDeactivate())
137
- ) {
138
- history.pushState({}, this.activePage.title, lastPathName)
139
- return
140
- }
141
-
142
- lastPathName = pathname
143
-
144
- if (ThingsApp.contextPath !== contextPath) {
145
- store.dispatch({
146
- type: UPDATE_CONTEXT_PATH,
147
- contextPath
148
- })
149
- }
150
-
151
- store.dispatch(navigateWithSilence(location) as any)
152
-
153
- ThingsApp.callbacks &&
154
- ThingsApp.callbacks.forEach(callback => {
155
- try {
156
- callback.call(this, location, e)
157
- } catch (ex) {
158
- console.error(ex)
159
- }
160
- })
161
- })
162
- })
163
- }
164
-
165
- disconnectedCallback(): void {
166
- super.disconnectedCallback()
167
- }
168
-
169
- async routeToPage() {
170
- const activePages = this.renderRoot.querySelectorAll('main > .page[active]')
171
- activePages.forEach(page => {
172
- page.removeAttribute('active')
173
- })
174
-
175
- this.activePage = this.renderRoot.querySelector(`main > .page[data-page=${this.page}]`) as PageView
176
-
177
- if (!this.activePage) {
178
- /* 해당 route에 연결된 page가 없는 경우에 main 섹션에 해당 element를 추가해준다. */
179
- const tagname = (await ThingsApp.pages)[this.page!]
180
- if (tagname) {
181
- const el = document.createElement(tagname) as PageView
182
- el.setAttribute('class', 'page')
183
- el.setAttribute('data-page', this.page!)
184
-
185
- this.main.appendChild(el)
186
-
187
- this.activePage = el
188
- }
189
- }
190
-
191
- if (this.activePage) {
192
- this.activePage.setAttribute('active', '')
193
- this.activePage.setAttribute('context-path', this.contextPath!)
194
- this.activePage.lifecycle = {
195
- ...(this.activePage.lifecycle || {}),
196
- active: true,
197
- params: this.params,
198
- resourceId: this.resourceId,
199
- page: this.page
200
- }
201
- }
202
-
203
- store.dispatch({
204
- type: UPDATE_ACTIVE_PAGE,
205
- activePage: this.activePage
206
- })
207
- }
208
-
209
- async updated(changes: PropertyValues<this>) {
210
- if (changes.has('contextPathPrefix')) {
211
- setContextPathPrefix(this.contextPathPrefix)
212
- }
213
-
214
- if (changes.has('modules')) {
215
- ThingsApp.registerPages()
216
- }
217
-
218
- if (changes.has('page') || changes.has('resourceId') || changes.has('params')) {
219
- this.routeToPage()
220
- }
221
-
222
- if (changes.has('contextPath')) {
223
- this.setBase()
224
- }
225
- }
226
-
227
- shouldUpdate() {
228
- return ThingsApp.moduleInitialized == 2
229
- }
230
-
231
- stateChanged(state: any) {
232
- this.page = state.route.page
233
- this.params = state.route.params
234
- this.resourceId = state.route.resourceId
235
- this.context = state.route.context
236
-
237
- ThingsApp.modules = this.modules = state.app.modules
238
- ThingsApp.contextPath = this.contextPath = state.app.contextPath
239
- ThingsApp.callbacks = state.route.callbacks
240
- }
241
-
242
- static async registerPages() {
243
- ThingsApp.pages = new Promise<{ [path: string]: string }>(resolve => (ThingsApp.pagesResolver = resolve))
244
-
245
- var reversedModules = [...ThingsApp.modules].reverse()
246
- const pages: { [path: string]: string } = {}
247
-
248
- /* 모듈 참조 순서 역순으로 page를 추가한다. (for overidable) */
249
- for (const m of reversedModules) {
250
- if (!m.routes) {
251
- continue
252
- }
253
-
254
- /*
255
- 각 모듈의 routes가 모두 완성될 때까지 ThingsApp.pages 구성을 지연한다.
256
- 각 모듈의 routes를 동적으로도 구성할 수 있도록 하기 위해서이다.
257
- */
258
- const routes = await m.routes
259
-
260
- routes.forEach((route: any) => {
261
- if (!pages[route.page]) {
262
- pages[route.page] = route.tagname
263
- }
264
- })
265
- }
266
-
267
- ThingsApp.pagesResolver(pages)
268
- }
269
-
270
- setBase() {
271
- const base = document.querySelector('base')
272
- base?.setAttribute('href', this.contextPath ? `${this.contextPath}/` : '/')
273
- }
274
- }
@@ -1,62 +0,0 @@
1
- import { css, html } from 'lit'
2
- import { customElement } from 'lit/decorators.js'
3
-
4
- import { PageView } from './page-view'
5
-
6
- @customElement('page-404')
7
- export class Page404 extends PageView {
8
- static styles = css`
9
- :host {
10
- display: block;
11
- box-sizing: border-box;
12
- background-color: var(--md-sys-color-background);
13
- --spacing-large: 15%;
14
- }
15
-
16
- section {
17
- padding: var(--spacing-large) 0 0 0;
18
- text-align: center;
19
- color: var(--md-sys-color-on-background);
20
- }
21
-
22
- md-icon {
23
- --md-icon-size: 120px;
24
- color: var(--md-sys-color-error);
25
- text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
26
- }
27
-
28
- h2 {
29
- margin: 0 auto;
30
- font-size: 2.5em;
31
- text-transform: capitalize;
32
- color: var(--md-sys-color-primary);
33
- }
34
-
35
- @media only screen and (max-width: 460px) {
36
- md-icon {
37
- padding-top: 25%;
38
- --md-icon-size: 90px;
39
- }
40
-
41
- h2 {
42
- font-size: 2em;
43
- }
44
- }
45
- `
46
-
47
- get context() {
48
- return {
49
- title: 'Page Not Found'
50
- }
51
- }
52
-
53
- render() {
54
- return html`
55
- <section>
56
- <md-icon>error_outline</md-icon>
57
- <h2>page not found!</h2>
58
- The page you requested cannot be found.
59
- </section>
60
- `
61
- }
62
- }
@@ -1,215 +0,0 @@
1
- import { LitElement, PropertyValues } from 'lit'
2
- import { property } from 'lit/decorators.js'
3
- import isEqual from 'lodash-es/isEqual'
4
-
5
- import { UPDATE_CONTEXT } from '../../actions/const'
6
- import { store } from '../../store'
7
-
8
- export type PageLifecycle = {
9
- active: boolean
10
- params: object
11
- resourceId?: string
12
- page?: string
13
- }
14
-
15
- function diff(after: any, before: any): any {
16
- var changed = false
17
- var changes: { [key: string]: any } = {}
18
-
19
- Object.getOwnPropertyNames(after).forEach(function (key) {
20
- let before_val = before[key]
21
- let after_val = after[key]
22
-
23
- if (!isEqual(before_val, after_val)) {
24
- changes[key] = after_val
25
- changed = true
26
- }
27
- })
28
-
29
- return changed && changes
30
- }
31
-
32
- /**
33
- * PageView is a base class for creating page elements with lifecycle management.
34
- * Subclasses can extend PageView to define custom behavior and handle page lifecycle events.
35
- */
36
- export class PageView extends LitElement {
37
- /**
38
- * Determines whether the page can be deactivated. Subclasses can override this method
39
- * to implement custom deactivation logic.
40
- * @returns A Promise that resolves to true if the page can be deactivated, or false otherwise.
41
- */
42
- async canDeactivate(): Promise<boolean> {
43
- return true
44
- }
45
-
46
- /**
47
- * Determines whether the page should update. This method is called whenever there are
48
- * changes to the page's properties.
49
- * @param changes - A map of changed property values.
50
- * @returns True if the page should update, or false otherwise.
51
- */
52
- shouldUpdate(changes: PropertyValues<this>) {
53
- var active = String(this.active) == 'true'
54
- var { active: oldActive = false } = this._oldLifecycleInfo$ || {}
55
-
56
- /*
57
- * page lifecycle
58
- * case 1. page가 새로 activate 되었다.
59
- * case 2. page가 active 상태에서 lifecycle 정보가 바뀌었다.
60
- **/
61
- if (active) {
62
- this.pageUpdate({
63
- active
64
- })
65
- } else if (oldActive) {
66
- this.pageUpdate({
67
- active
68
- })
69
- }
70
-
71
- return active
72
- }
73
-
74
- /**
75
- * Indicates whether the page is currently active.
76
- */
77
- @property({ type: Boolean }) active: boolean = false
78
-
79
- /**
80
- * Stores information about the page's lifecycle.
81
- */
82
- @property({ type: Object }) lifecycle!: PageLifecycle
83
-
84
- /**
85
- * The context path for the page.
86
- */
87
- @property({ type: String, attribute: 'context-path' }) contextPath?: string
88
-
89
- _oldLifecycleInfo$: any
90
-
91
- /* lifecycle */
92
-
93
- /**
94
- * Handles page updates and lifecycle events. Subclasses can override this method
95
- * to implement custom logic for initializing, updating, and disposing of the page.
96
- * @param changes - A map of changed properties.
97
- * @param force - If true, forces an update of the page.
98
- */
99
- async pageUpdate(changes: any = {}, force: boolean = false) {
100
- var before = this._oldLifecycleInfo$ || {}
101
-
102
- var after = {
103
- ...before,
104
- ...this.lifecycle,
105
- contextPath: this.contextPath,
106
- ...changes
107
- }
108
-
109
- if (!('initialized' in changes) && after.active && !before.initialized) {
110
- after.initialized = true
111
- }
112
-
113
- if (force) {
114
- after.updated = Date.now()
115
- }
116
-
117
- var changed = diff(after, before)
118
- if (!changed) {
119
- return
120
- }
121
-
122
- this._oldLifecycleInfo$ = after
123
-
124
- /* page의 이미 초기화된 상태에서 contextPath가 바뀐다면, 무조건 page가 리셋되어야 한다. */
125
- if (before.initialized && changed.contextPath) {
126
- await this.pageReset()
127
- return
128
- }
129
-
130
- if (changed.initialized) {
131
- await this.pageInitialized(after)
132
- }
133
-
134
- if ('initialized' in changed) {
135
- if (changed.initialized) {
136
- /*
137
- * 방금 초기화된 경우라면, 엘리먼트들이 만들어지지 않았을 가능성이 있으므로,
138
- * 다음 animationFrame에서 pageUpdated 콜백을 호출한다.
139
- */
140
- requestAnimationFrame(async () => {
141
- await this.pageUpdated(changed, after, before)
142
- /* active page인 경우에는, page Context 갱신도 필요할 것이다. */
143
- after.active && this.updateContext()
144
- })
145
- } else {
146
- await this.pageDisposed(after)
147
- }
148
- } else {
149
- await this.pageUpdated(changed, after, before)
150
- /* active page인 경우에는, page Context 갱신도 필요할 것이다. */
151
- after.active && this.updateContext()
152
- }
153
- }
154
-
155
- /**
156
- * Resets the page. Subclasses can override this method to perform custom reset logic.
157
- */
158
- async pageReset() {
159
- var { initialized } = this._oldLifecycleInfo$ || {}
160
-
161
- if (initialized) {
162
- await this.pageDispose()
163
- await this.pageUpdate({}, true)
164
- }
165
- }
166
-
167
- /**
168
- * Disposes of the page. Subclasses can override this method to perform custom disposal logic.
169
- */
170
- async pageDispose() {
171
- await this.pageUpdate({
172
- initialized: false
173
- })
174
- }
175
-
176
- /**
177
- * Initializes the page. Subclasses can override this method to perform custom initialization logic.
178
- * @param pageInfo - Information about the page's state.
179
- */
180
- pageInitialized(pageInfo: any) {}
181
-
182
- /**
183
- * Handles page updates and changes in properties.
184
- * Subclasses can override this method to implement custom update logic.
185
- * @param changes - A map of changed properties.
186
- * @param after - The current state of the page.
187
- * @param before - The previous state of the page.
188
- */
189
- pageUpdated(changes: any, after: any, before: any) {}
190
-
191
- /**
192
- * Handles the disposal of the page. Subclasses can override this method
193
- * to implement custom disposal logic.
194
- * @param pageInfo - Information about the page's state.
195
- */
196
- pageDisposed(pageInfo: any) {}
197
-
198
- /* context */
199
- updateContext(override?: any) {
200
- store.dispatch({
201
- type: UPDATE_CONTEXT,
202
- context: override ? { ...this.context, ...override } : this.context
203
- })
204
- }
205
-
206
- /**
207
- * Updates the context of the page. Subclasses can override the `context` getter
208
- * to provide specific context information for the page. The context will be updated
209
- * using the `updateContext` method inherited from PageView.
210
- * @param override - An optional object with context properties to override.
211
- */
212
- get context() {
213
- return {}
214
- }
215
- }
@@ -1,43 +0,0 @@
1
- import { OxPrompt } from '@operato/popup'
2
-
3
- const TYPES_ICON: { [type: string]: string } = {
4
- success: 'verified',
5
- error: 'error',
6
- warning: 'warning',
7
- info: 'info',
8
- question: 'question_mark'
9
- }
10
-
11
- export async function CustomAlert({
12
- type,
13
- icon,
14
- title,
15
- text,
16
- confirmButton,
17
- cancelButton,
18
- callback
19
- }: {
20
- type?: 'info' | 'success' | 'error' | 'warning' | 'question'
21
- icon?: string
22
- title?: string
23
- text?: string
24
- confirmButton?: { color?: string; text: string } | string
25
- cancelButton?: { color?: string; text: string } | string
26
- callback?: (val: { isConfirmed: boolean; isDismissed: boolean; value: boolean }) => any
27
- }) {
28
- const result = await OxPrompt.open({
29
- type: type || 'info',
30
- icon: (icon && TYPES_ICON[icon]) || icon,
31
- title,
32
- text,
33
- confirmButton: typeof confirmButton == 'string' ? { text: confirmButton } : confirmButton,
34
- cancelButton: typeof cancelButton == 'string' ? { text: cancelButton } : cancelButton
35
- })
36
-
37
- const val = { isConfirmed: result, isDismissed: !result, value: result }
38
- if (callback && typeof callback === 'function') {
39
- callback(val)
40
- } else {
41
- return val
42
- }
43
- }
@@ -1,94 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
-
3
- import { css, html, LitElement } from 'lit'
4
- import { customElement } from 'lit/decorators.js'
5
-
6
- @customElement('home-page')
7
- export class HomePage extends LitElement {
8
- static styles = css`
9
- :host {
10
- background-color: var(--md-sys-color-background);
11
-
12
- display: block;
13
- position: relative;
14
- }
15
-
16
- [home] {
17
- position: absolute;
18
- left: 20px;
19
- top: 20px;
20
- font-size: 2em;
21
- color: white;
22
- }
23
-
24
- [message] {
25
- background-color: rgba(50, 66, 97, 0.8);
26
- padding: 60px 50px 0 50px;
27
- color: #fff;
28
- text-align: center;
29
- font-size: 20px;
30
-
31
- strong {
32
- display: block;
33
- font-size: 2.5rem;
34
- }
35
-
36
- img {
37
- width: 450px;
38
- max-width: 90%;
39
- display: block;
40
- margin: auto;
41
- margin-top: 20px;
42
- }
43
- }
44
-
45
- @media screen and (max-width: 460px) {
46
- [message] {
47
- padding: 60px 30px 0 30px;
48
- color: #fff;
49
- text-align: center;
50
- font-size: 15px;
51
- }
52
-
53
- [message] strong {
54
- font-size: 1.6rem;
55
- }
56
- }
57
- `
58
-
59
- _applicationMeta?: {
60
- icon: string
61
- title: string
62
- description: string
63
- }
64
-
65
- render() {
66
- var { title, description } = this.applicationMeta
67
-
68
- return html`
69
- <md-icon home @click=${() => (window.location.href = '/')}>home</md-icon>
70
-
71
- <div message>
72
- <strong>${title}</strong>
73
- ${description}
74
- <img src="/assets/images/home.png" />
75
- </div>
76
- `
77
- }
78
-
79
- get applicationMeta() {
80
- if (!this._applicationMeta) {
81
- var iconLink = document.querySelector('link[rel="application-icon"]') as HTMLLinkElement
82
- var titleMeta = document.querySelector('meta[name="application-name"]') as HTMLMetaElement
83
- var descriptionMeta = document.querySelector('meta[name="application-description"]') as HTMLMetaElement
84
-
85
- this._applicationMeta = {
86
- icon: iconLink?.href,
87
- title: titleMeta ? titleMeta.content : 'Things Factory',
88
- description: descriptionMeta ? descriptionMeta.content : 'Reimagining Software'
89
- }
90
- }
91
-
92
- return this._applicationMeta
93
- }
94
- }
package/src/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export * from './types'
2
- export * from './store'
3
- export * from './actions'
4
- export * from './app/pages/page-view'
5
- export * from './object-store'
6
- export * from './custom-alert'
File without changes