@operato/menu 8.0.0-beta.0 → 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,91 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
-
3
- import { html, LitElement, nothing, TemplateResult } from 'lit'
4
- import { customElement, property } from 'lit/decorators.js'
5
-
6
- import { navigate } from '@operato/shell'
7
- import { ScrollbarStyles } from '@operato/styles'
8
-
9
- import { Menu } from './types'
10
- import { MenuPortraitStyles } from './menu-portrait-styles'
11
-
12
- @customElement('ox-menu-portrait')
13
- export class OxMenuPortrait extends LitElement {
14
- static styles = [ScrollbarStyles, MenuPortraitStyles]
15
-
16
- @property({ type: Array }) menus?: Menu[]
17
- @property({ type: Object }) activeTopLevel?: Menu
18
- @property({ type: Object }) activeMenu?: Menu
19
- @property({ type: String }) path!: string
20
-
21
- renderMenus(menus: Menu[], activeTopLevel?: Menu, activeMenu?: Menu): TemplateResult | typeof nothing {
22
- if (menus.length == 0) {
23
- return nothing
24
- }
25
-
26
- return html`
27
- <ul>
28
- ${menus.map(menu => {
29
- var { type, name, active, path, label = name, badge, icon, menus = [] } = menu
30
- active = active && typeof active === 'function' ? active.call(menu, { path: this.path }) : false
31
- badge = typeof badge === 'function' ? badge.call(menu) : badge ?? false
32
-
33
- return type == 'group'
34
- ? html`<li group-label>${label}</li>`
35
- : html`
36
- <li ?active=${activeTopLevel ? menu === activeTopLevel : active} .menu=${menu} menu>
37
- <a href=${path || '#'}>
38
- <md-icon filled>${icon || 'fiber_manual_record'}</md-icon>
39
- <span>${label}</span>${badge !== false ? html`<div badge>${badge}</div>` : html``}
40
- ${menus.length > 0 ? html` <md-icon filled submenu-button></md-icon> ` : html``}
41
- </a>
42
- ${menus && this.renderMenus(menus || [], activeMenu)}
43
- </li>
44
- `
45
- })}
46
- </ul>
47
- `
48
- }
49
-
50
- render() {
51
- const { menus, activeTopLevel, activeMenu } = this
52
- return this.renderMenus(menus || [], activeTopLevel, activeMenu)
53
- }
54
-
55
- firstUpdated() {
56
- this.renderRoot.addEventListener('click', (e: Event) => {
57
- const menuElement = (e.target as Element)!.closest('[menu]')
58
-
59
- //@ts-ignore
60
- if (menuElement?.menu) {
61
- //@ts-ignore
62
- let menu = menuElement.menu
63
-
64
- if (!menu.path) {
65
- /* protect to act move to href. */
66
- e.stopPropagation()
67
- e.preventDefault()
68
- }
69
-
70
- this.dispatchEvent(
71
- new CustomEvent('active-toplevel', {
72
- bubbles: true,
73
- detail: this.activeTopLevel === menu ? undefined : menu
74
- })
75
- )
76
- }
77
-
78
- /* to respond even if current acting menu is selected */
79
- let href = (e.target as HTMLAnchorElement)!.href
80
- href && location.href === href && navigate(href + '#force', true)
81
- })
82
-
83
- /* to hide scrollbar during transition */
84
- this.renderRoot.addEventListener('transitionstart', e => {
85
- ;(e.target as Element).removeAttribute('settled')
86
- })
87
- this.renderRoot.addEventListener('transitionend', e => {
88
- ;(e.target as Element).setAttribute('settled', '')
89
- })
90
- }
91
- }
@@ -1,147 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
-
3
- import { css, html, LitElement, PropertyValueMap, TemplateResult } from 'lit'
4
- import { customElement, property, query, state } from 'lit/decorators.js'
5
- import { connect } from 'pwa-helpers'
6
-
7
- import { toggleOverlay } from '@operato/layout'
8
- import { store } from '@operato/shell'
9
-
10
- import { Menu } from './types'
11
-
12
- @customElement('ox-top-menu-bar')
13
- export class OxTopMenuBar extends connect(store)(LitElement) {
14
- static styles = [
15
- css`
16
- :host {
17
- display: flex;
18
- flex-direction: row;
19
- }
20
-
21
- span {
22
- flex: 1;
23
- }
24
-
25
- ul {
26
- display: flex;
27
- align-items: center;
28
- list-style: none;
29
- margin: 0;
30
- padding: 0;
31
- }
32
-
33
- li {
34
- display: inline-flex;
35
- flex-direction: row nowrap;
36
- float: left;
37
- overflow: none;
38
- }
39
-
40
- a {
41
- display: inline-block;
42
- white-space: nowrap;
43
- overflow: hidden;
44
- text-overflow: ellipsis;
45
- padding: var(--padding-default) var(--spacing-large) var(--spacing-small) var(--spacing-large);
46
- text-decoration: none;
47
- color: white;
48
- }
49
- a * {
50
- vertical-align: middle;
51
- }
52
- a md-icon {
53
- opacity: 0.5;
54
- position: relative;
55
- top: -2px;
56
- font-size: 1em;
57
- }
58
-
59
- li[active] a {
60
- font-weight: bold;
61
- }
62
- li[active] a md-icon {
63
- opacity: 1;
64
- }
65
- `
66
- ]
67
-
68
- @property({ type: String }) page?: string
69
- @property({ type: String }) resourceId?: string
70
- @property({ type: Array }) menus?: Menu[]
71
- @property({ type: Object }) slotTemplate!: Node
72
-
73
- @state() private _activeTopLevel?: Menu
74
-
75
- render() {
76
- const { menus = [], _activeTopLevel } = this
77
-
78
- return html`
79
- <slot name="head"></slot>
80
- <ul>
81
- ${menus.map(menu =>
82
- menu.type == 'group'
83
- ? html``
84
- : html`
85
- <li ?active=${menu === _activeTopLevel}>
86
- <a
87
- href="#"
88
- @click=${(e: MouseEvent) => {
89
- e.preventDefault()
90
- toggleOverlay('ox-menu-part', {
91
- backdrop: true
92
- })
93
- }}
94
- >
95
- ${menu.name}
96
- <md-icon>expand_more</md-icon>
97
- </a>
98
- </li>
99
- `
100
- )}
101
- </ul>
102
- <slot name="tail"></slot>
103
- `
104
- }
105
-
106
- stateChanged(state: any): void {
107
- this.page = state.route.page
108
- this.resourceId = state.route.resourceId
109
- this.menus = state.liteMenu.menus || []
110
- this.slotTemplate = state.liteMenu.slotTemplate
111
- }
112
-
113
- // firstUpdated() {
114
- // this.addEventListener('mousewheel', this.onWheelEvent.bind(this), false)
115
- // }
116
-
117
- updated(changes: PropertyValueMap<this>) {
118
- if (changes.has('menus') || changes.has('page') || changes.has('resourceId')) {
119
- this._findActivePage()
120
- }
121
-
122
- if (changes.has('slotTemplate')) {
123
- this.replaceChild(this.slotTemplate, this.renderRoot)
124
- }
125
- }
126
-
127
- // onWheelEvent(e) {
128
- // var delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail))
129
- // this.scrollLeft -= delta * 40
130
- // console.log('delta', this.scrollLeft, delta, e.wheelDelta || -e.detail)
131
-
132
- // e.preventDefault()
133
- // }
134
-
135
- _findActivePage() {
136
- var path = this.resourceId ? `${this.page}/${this.resourceId}` : this.page
137
- var menus = this.menus || []
138
-
139
- this._activeTopLevel = menus.find(menu => {
140
- if (menu.path?.split('?')[0] === path) {
141
- return true
142
- } else if (menu.menus) {
143
- return !!menu.menus.find(menu => menu.path?.split('?')[0] === path)
144
- }
145
- })
146
- }
147
- }
package/src/types.ts DELETED
@@ -1,11 +0,0 @@
1
- export type Menu = {
2
- type?: 'group' | 'board'
3
- name?: string
4
- description?: string
5
- path?: string
6
- icon?: string
7
- label?: string
8
- badge?: boolean | string | (() => boolean)
9
- active?: boolean | ((menu?: Menu) => boolean)
10
- menus?: Menu[]
11
- }
@@ -1,97 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '../src/ox-menu-portrait'
3
-
4
- import { css, html, LitElement, PropertyValues } from 'lit'
5
- import { customElement, property, query, state } from 'lit/decorators.js'
6
-
7
- import { ScrollbarStyles } from '@operato/styles'
8
-
9
- import { Menu } from '../src/types'
10
-
11
- function isActiveMenu(menu: Menu, path?: string) {
12
- return (
13
- menu.path?.split('?')[0] === path ||
14
- (menu.active && typeof menu.active === 'function' && menu.active.call(menu, { path }))
15
- )
16
- }
17
-
18
- @customElement('ox-menu-container')
19
- export class OxMenuContainer extends LitElement {
20
- static styles = [
21
- ScrollbarStyles,
22
- css`
23
- :host {
24
- display: flex;
25
- overflow-y: auto;
26
- flex-direction: column;
27
- background-color: var(--md-sys-color-surface);
28
- }
29
- `
30
- ]
31
-
32
- @property({ type: Array }) menus?: Menu[]
33
-
34
- @state() activeMenu?: Menu
35
- @state() activeTopLevel?: Menu
36
- @state() path?: string
37
-
38
- render() {
39
- return html`
40
- <div
41
- @click=${(e: MouseEvent) => {
42
- if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey) {
43
- return
44
- }
45
- const anchor: HTMLAnchorElement = e.composedPath().filter((n: any) => n.tagName === 'A')[0] as any
46
- if (!anchor) {
47
- return
48
- }
49
- const href = anchor.href
50
- e.preventDefault()
51
-
52
- var [_, path, ...others] = new URL(href).pathname.split('/')
53
- this.path = path
54
- }}
55
- >
56
- <ox-menu-portrait
57
- .menus=${this.menus}
58
- .activeTopLevel=${this.activeTopLevel}
59
- .activeMenu=${this.activeMenu}
60
- .path=${this.path || ''}
61
- ></ox-menu-portrait>
62
- </div>
63
- `
64
- }
65
-
66
- firstUpdated() {
67
- this.renderRoot.addEventListener('active-toplevel', (e: Event) => {
68
- e.stopPropagation()
69
- e.preventDefault()
70
-
71
- this.activeTopLevel = (e as CustomEvent).detail
72
- })
73
- }
74
-
75
- updated(changes: PropertyValues<this>) {
76
- if (changes.has('menus') || changes.has('path')) {
77
- this.findActivePage()
78
- }
79
- }
80
-
81
- private findActivePage() {
82
- var menus = this.menus || []
83
- var activeMenu
84
-
85
- this.activeTopLevel = menus.find(menu => {
86
- if (isActiveMenu(menu, this.path)) {
87
- activeMenu = menu
88
- return true
89
- } else if (menu.menus) {
90
- activeMenu = menu.menus.find((menu: Menu) => isActiveMenu(menu, this.path))
91
- return !!activeMenu
92
- }
93
- })
94
-
95
- this.activeMenu = activeMenu || this.activeTopLevel
96
- }
97
- }
@@ -1,65 +0,0 @@
1
- import '../src/ox-menu-portrait'
2
-
3
- import { html, TemplateResult } from 'lit'
4
- import { menus } from './test-menus'
5
- import './ox-menu-container'
6
-
7
- export default {
8
- title: 'OxMenuPortrait',
9
- component: 'ox-menu-portrait',
10
- argTypes: {}
11
- }
12
-
13
- interface Story<T> {
14
- (args: T): TemplateResult
15
- args?: Partial<T>
16
- argTypes?: Record<string, unknown>
17
- }
18
-
19
- interface ArgTypes {}
20
-
21
- const Template: Story<ArgTypes> = ({}: ArgTypes) => html`
22
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet" />
23
-
24
- <link
25
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL@20..48,100..700,0..1"
26
- rel="stylesheet"
27
- />
28
- <link
29
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL@20..48,100..700,0..1"
30
- rel="stylesheet"
31
- />
32
- <link
33
- href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL@20..48,100..700,0..1"
34
- rel="stylesheet"
35
- />
36
-
37
- <link href="/themes/app-theme.css" rel="stylesheet" />
38
- <link href="/themes/light.css" rel="stylesheet" />
39
- <link href="/themes/dark.css" rel="stylesheet" />
40
- <link href="/themes/spacing.css" rel="stylesheet" />
41
- <link href="/themes/grist-theme.css" rel="stylesheet" />
42
- <link href="/themes/form-theme.css" rel="stylesheet" />
43
-
44
- <style>
45
- body {
46
- background-color: cyan;
47
- }
48
-
49
- .container {
50
- display: flex;
51
- width: 260px;
52
- height: 500px;
53
- }
54
-
55
- ox-menu-container {
56
- flex: 1;
57
- }
58
- </style>
59
-
60
- <div class="container">
61
- <ox-menu-container .menus=${menus}></ox-menu-container>
62
- </div>
63
- `
64
-
65
- export const Regular = Template.bind({})
@@ -1,180 +0,0 @@
1
- import { Menu } from '../src/types'
2
-
3
- export const menus: Menu[] = [
4
- {
5
- name: '나의 업무 공간',
6
- type: 'group'
7
- },
8
- {
9
- name: '내 업무함',
10
- icon: 'format_list_bulleted',
11
- path: 'todo-list',
12
- badge: '777'
13
- },
14
- {
15
- name: '내 결재함',
16
- icon: 'approval',
17
- path: 'approval-pending-list',
18
- badge: '3'
19
- },
20
- {
21
- name: '업무 이력 캘린더',
22
- icon: 'calendar_month',
23
- path: 'done-list-calendar',
24
- description: '업무와 관련된 수행 또는 결재 이력을 캘린더에서 조회할 수 있습니다.',
25
- menus: [
26
- {
27
- name: '업무 이력',
28
- icon: 'history',
29
- path: 'done-list'
30
- },
31
- {
32
- name: '결재 수행 이력',
33
- icon: 'history',
34
- path: 'approval-done-list'
35
- }
36
- ]
37
- },
38
- {
39
- name: 'DATASET',
40
- type: 'group'
41
- },
42
- {
43
- name: '데이타 셋 관리',
44
- icon: 'display_settings',
45
- path: 'data-set-list'
46
- },
47
- {
48
- name: '데이타 키셋 관리',
49
- icon: 'display_settings',
50
- path: 'data-key-set-list'
51
- },
52
- {
53
- name: '데이타 센서 관리',
54
- icon: 'sensors',
55
- path: 'data-sensor-list'
56
- },
57
- {
58
- name: 'DATA ENTRY',
59
- type: 'group'
60
- },
61
- {
62
- icon: 'post_add',
63
- name: 'LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG LONG Named MENU',
64
- path: 'data-entry-list'
65
- },
66
- {
67
- name: 'DATA REPORT',
68
- type: 'group'
69
- },
70
- {
71
- name: '데이타 샘플 조회 - 데이타 샘플 조회 - 데이타 샘플 조회 - 데이타 샘플 조회 - 데이타 샘플 조회',
72
- icon: 'checklist',
73
- path: 'data-sample-list',
74
- menus: [
75
- {
76
- name: 'TEST',
77
- icon: 'checklist',
78
- path: 'data-sample-search/bfb95a47-5cbd-4415-9032-14789e27c819'
79
- },
80
- {
81
- name: '자숙 공정',
82
- icon: 'checklist',
83
- path: 'data-sample-search/5aad9fe2-56a3-4796-afd9-6c044ab39990'
84
- },
85
- {
86
- name: '탕비실 관리',
87
- icon: 'checklist',
88
- path: 'data-sample-search/0176fcc3-0dbf-4fe6-9bd0-ca9f9ae91cf4'
89
- },
90
- {
91
- name: '파일 관리',
92
- icon: 'checklist',
93
- path: 'data-sample-search/9c6ce158-e28c-4b00-92b9-59739d906609'
94
- },
95
- {
96
- name: '화장실 관리',
97
- icon: 'checklist',
98
- path: 'data-sample-search/3998c6a1-b39d-45cf-a0cb-2b11b49331a0'
99
- }
100
- ]
101
- },
102
- {
103
- name: '데이타 이탈점 조회',
104
- icon: 'playlist_remove',
105
- path: 'data-ooc-list'
106
- },
107
- {
108
- name: '데이터 수집 마감 조회',
109
- icon: 'functions',
110
- path: 'data-summary-list',
111
- menus: [
112
- {
113
- name: '자숙 공정',
114
- icon: 'checklist',
115
- path: 'data-summary-period/5aad9fe2-56a3-4796-afd9-6c044ab39990'
116
- },
117
- {
118
- name: '탕비실 관리',
119
- icon: 'checklist',
120
- path: 'data-summary-period/0176fcc3-0dbf-4fe6-9bd0-ca9f9ae91cf4'
121
- },
122
- {
123
- name: '화장실 관리',
124
- icon: 'checklist',
125
- path: 'data-summary-period/3998c6a1-b39d-45cf-a0cb-2b11b49331a0'
126
- }
127
- ]
128
- },
129
- {
130
- icon: 'newspaper',
131
- name: '데이타 리포트',
132
- path: 'data-report-list'
133
- },
134
- {
135
- icon: 'archive',
136
- name: '데이타 아카이브 조회',
137
- path: 'data-archive-list'
138
- },
139
- {
140
- name: '조직',
141
- type: 'group'
142
- },
143
- {
144
- name: '조직',
145
- icon: 'account_tree',
146
- description: '조직 구성과 결재선 관리를 위한 메뉴 구성입니다.',
147
- menus: [
148
- {
149
- name: '연락처 관리',
150
- path: 'contact-list'
151
- },
152
- {
153
- name: '직원 목록',
154
- path: 'employee-list'
155
- },
156
- {
157
- name: '부서 조직도',
158
- path: 'department-tree'
159
- },
160
- {
161
- name: '부서 목록',
162
- path: 'department-list'
163
- },
164
- {
165
- name: '나의 결재라인 템플릿 목록',
166
- path: 'my-approval-line-templates-page'
167
- },
168
- {
169
- name: '공통 결재라인 템플릿 목록',
170
- path: 'common-approval-line-templates-page'
171
- }
172
- ]
173
- },
174
- {
175
- name: '모니터링',
176
- type: 'board',
177
- path: 'board-viewer/85f69924-cdff-438a-9691-569b3542c38e?title=모니터링',
178
- icon: 'dashboard'
179
- }
180
- ]
package/tsconfig.json DELETED
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2018",
4
- "module": "esnext",
5
- "moduleResolution": "node",
6
- "noEmitOnError": true,
7
- "lib": ["es2017", "dom"],
8
- "strict": true,
9
- "esModuleInterop": false,
10
- "allowSyntheticDefaultImports": true,
11
- "experimentalDecorators": true,
12
- "useDefineForClassFields": false,
13
- "importHelpers": true,
14
- "outDir": "dist",
15
- "sourceMap": true,
16
- "inlineSources": true,
17
- "rootDir": "./",
18
- "declaration": true,
19
- "incremental": true,
20
- "skipLibCheck": true,
21
- "types": ["node", "mocha"]
22
- },
23
- "include": ["**/*.ts"]
24
- }
@@ -1,27 +0,0 @@
1
- // import { hmrPlugin, presets } from '@open-wc/dev-server-hmr';
2
-
3
- /** Use Hot Module replacement by adding --hmr to the start command */
4
- const hmr = process.argv.includes('--hmr');
5
-
6
- export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
7
- open: '/demo/',
8
- /** Use regular watch mode if HMR is not enabled. */
9
- watch: !hmr,
10
- /** Resolve bare module imports */
11
- nodeResolve: {
12
- exportConditions: ['browser', 'development'],
13
- },
14
-
15
- /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
16
- // esbuildTarget: 'auto'
17
-
18
- /** Set appIndex to enable SPA routing */
19
- // appIndex: 'demo/index.html',
20
-
21
- plugins: [
22
- /** Use Hot Module Replacement by uncommenting. Requires @open-wc/dev-server-hmr plugin */
23
- // hmr && hmrPlugin({ exclude: ['**/*/node_modules/**/*'], presets: [presets.litElement] }),
24
- ],
25
-
26
- // See documentation for all available options
27
- });
@@ -1,41 +0,0 @@
1
- // import { playwrightLauncher } from '@web/test-runner-playwright';
2
-
3
- const filteredLogs = ['Running in dev mode', 'lit-html is in dev mode'];
4
-
5
- export default /** @type {import("@web/test-runner").TestRunnerConfig} */ ({
6
- /** Test files to run */
7
- files: 'dist/test/**/*.test.js',
8
-
9
- /** Resolve bare module imports */
10
- nodeResolve: {
11
- exportConditions: ['browser', 'development'],
12
- },
13
-
14
- /** Filter out lit dev mode logs */
15
- filterBrowserLogs(log) {
16
- for (const arg of log.args) {
17
- if (typeof arg === 'string' && filteredLogs.some(l => arg.includes(l))) {
18
- return false;
19
- }
20
- }
21
- return true;
22
- },
23
-
24
- /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
25
- // esbuildTarget: 'auto',
26
-
27
- /** Amount of browsers to run concurrently */
28
- // concurrentBrowsers: 2,
29
-
30
- /** Amount of test files per browser to test concurrently */
31
- // concurrency: 1,
32
-
33
- /** Browsers to run tests on */
34
- // browsers: [
35
- // playwrightLauncher({ product: 'chromium' }),
36
- // playwrightLauncher({ product: 'firefox' }),
37
- // playwrightLauncher({ product: 'webkit' }),
38
- // ],
39
-
40
- // See documentation for all available options
41
- });