@things-factory/menu-ui 8.0.0-beta.9 → 8.0.0

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 (57) hide show
  1. package/client/apptools/favorite-tool.js +130 -0
  2. package/client/bootstrap.js +57 -0
  3. package/client/components/child-menus-selector.js +150 -0
  4. package/client/components/role-select-popup.js +179 -0
  5. package/client/pages/menu-list-page.js +200 -0
  6. package/client/pages/menu-management-detail.js +384 -0
  7. package/client/pages/menu-management.js +294 -0
  8. package/client/pages/role-menus-management.js +215 -0
  9. package/client/route.js +15 -0
  10. package/client/viewparts/menu-bar.js +114 -0
  11. package/client/viewparts/menu-tile-list.js +222 -0
  12. package/client/viewparts/menu-tree-bar.js +295 -0
  13. package/dist-server/tsconfig.tsbuildinfo +1 -1
  14. package/package.json +18 -21
  15. package/server/index.ts +0 -0
  16. package/things-factory.config.js +14 -5
  17. package/dist-client/apptools/favorite-tool.d.ts +0 -28
  18. package/dist-client/apptools/favorite-tool.js +0 -139
  19. package/dist-client/apptools/favorite-tool.js.map +0 -1
  20. package/dist-client/bootstrap.d.ts +0 -4
  21. package/dist-client/bootstrap.js +0 -52
  22. package/dist-client/bootstrap.js.map +0 -1
  23. package/dist-client/components/child-menus-selector.d.ts +0 -1
  24. package/dist-client/components/child-menus-selector.js +0 -146
  25. package/dist-client/components/child-menus-selector.js.map +0 -1
  26. package/dist-client/components/role-select-popup.d.ts +0 -1
  27. package/dist-client/components/role-select-popup.js +0 -180
  28. package/dist-client/components/role-select-popup.js.map +0 -1
  29. package/dist-client/index.js +0 -2
  30. package/dist-client/index.js.map +0 -1
  31. package/dist-client/pages/menu-list-page.d.ts +0 -2
  32. package/dist-client/pages/menu-list-page.js +0 -204
  33. package/dist-client/pages/menu-list-page.js.map +0 -1
  34. package/dist-client/pages/menu-management-detail.d.ts +0 -2
  35. package/dist-client/pages/menu-management-detail.js +0 -376
  36. package/dist-client/pages/menu-management-detail.js.map +0 -1
  37. package/dist-client/pages/menu-management.d.ts +0 -3
  38. package/dist-client/pages/menu-management.js +0 -280
  39. package/dist-client/pages/menu-management.js.map +0 -1
  40. package/dist-client/pages/role-menus-management.d.ts +0 -4
  41. package/dist-client/pages/role-menus-management.js +0 -215
  42. package/dist-client/pages/role-menus-management.js.map +0 -1
  43. package/dist-client/route.d.ts +0 -1
  44. package/dist-client/route.js +0 -14
  45. package/dist-client/route.js.map +0 -1
  46. package/dist-client/tsconfig.tsbuildinfo +0 -1
  47. package/dist-client/viewparts/menu-bar.d.ts +0 -12
  48. package/dist-client/viewparts/menu-bar.js +0 -108
  49. package/dist-client/viewparts/menu-bar.js.map +0 -1
  50. package/dist-client/viewparts/menu-tile-list.d.ts +0 -13
  51. package/dist-client/viewparts/menu-tile-list.js +0 -234
  52. package/dist-client/viewparts/menu-tile-list.js.map +0 -1
  53. package/dist-client/viewparts/menu-tree-bar.d.ts +0 -28
  54. package/dist-client/viewparts/menu-tree-bar.js +0 -307
  55. package/dist-client/viewparts/menu-tree-bar.js.map +0 -1
  56. /package/{dist-client/index.d.ts → client/index.js} +0 -0
  57. /package/{dist-client → client}/themes/menu-theme.css +0 -0
@@ -0,0 +1,215 @@
1
+ import '@material/mwc-button'
2
+ import '@things-factory/component-ui'
3
+ import '../components/role-select-popup'
4
+ import '../components/child-menus-selector'
5
+
6
+ import gql from 'graphql-tag'
7
+ import { css, html } from 'lit'
8
+
9
+ import { openPopup } from '@operato/layout'
10
+ import { i18next, localize } from '@operato/i18n'
11
+ import { client, PageView } from '@things-factory/shell'
12
+
13
+ class RoleMenusManagement extends localize(i18next)(PageView) {
14
+ static get styles() {
15
+ return [
16
+ css`
17
+ :host {
18
+ display: flex;
19
+ flex-direction: column;
20
+ background-color: var(--md-sys-color-background);
21
+ padding: var(--spacing-large);
22
+ overflow: auto;
23
+ }
24
+ [input-container] {
25
+ display: flex;
26
+ background-color: var(--md-sys-color-surface);
27
+ margin: var(--spacing-large) 0;
28
+ padding: var(--spacing-large);
29
+ border-radius: var(--border-radius);
30
+ box-shadow: var(--box-shadow);
31
+ clear: both;
32
+ gap: 10px;
33
+ }
34
+ mwc-buton {
35
+ margin: auto;
36
+ }
37
+ h2 {
38
+ margin: var(--title-margin);
39
+ font: var(--title-font);
40
+ color: var(--title-text-color);
41
+ }
42
+ [subtitle] {
43
+ padding: var(--subtitle-padding);
44
+ font: var(--subtitle-font);
45
+ color: var(--subtitle-text-color);
46
+ }
47
+ input {
48
+ border: var(--border-dim-color);
49
+ border-radius: var(--border-radius);
50
+ padding: var(--input-padding);
51
+ min-width: 250px;
52
+ font: var(--input-font);
53
+ }
54
+ label {
55
+ display: flex;
56
+ }
57
+ @media screen and (max-width: 480px) {
58
+ input {
59
+ min-width: 0;
60
+ }
61
+ }
62
+ `
63
+ ]
64
+ }
65
+
66
+ static get properties() {
67
+ return {
68
+ selectedRole: Object,
69
+ parentMenus: Array,
70
+ roleMenus: Array,
71
+ menuSet: Object,
72
+ targetRole: Object
73
+ }
74
+ }
75
+
76
+ get context() {
77
+ return {
78
+ title: i18next.t('title.role_menus_management')
79
+ }
80
+ }
81
+
82
+ render() {
83
+ const data = { menus: this.parentMenus || [] }
84
+ const menuSet = this.menuSet || {}
85
+
86
+ return html`
87
+ <h2>${i18next.t('title.menus_by_role')}</h2>
88
+ <div subtitle>${i18next.t('field.role')}</div>
89
+ <div input-container>
90
+ <label><input name="name" type="text" disabled /></label>
91
+ <mwc-button @click=${() => this.onSelectRole()} label="${i18next.t('button.select')}" raised></mwc-button>
92
+ </div>
93
+
94
+ <div subtitle>${i18next.t('title.menus')}</div>
95
+ <div id="children-menus">
96
+ <div>
97
+ <quick-find-list
98
+ .data="${data}"
99
+ .contentRenderer="${(item, tabKey) =>
100
+ this.targetRole
101
+ ? html`<child-menus-selector
102
+ @updateRoleMenus="${async () => {
103
+ await this.getMenus(this.targetRole)
104
+ }}"
105
+ .parentMenu=${item}
106
+ .roleMenus=${menuSet[item.name]}
107
+ .targetRole=${this.targetRole}
108
+ ></child-menus-selector>`
109
+ : this.showToast(i18next.t('error.value is empty', { value: i18next.t('field.role') }))}"
110
+ ></quick-find-list>
111
+ </div>
112
+ </div>
113
+ `
114
+ }
115
+
116
+ get roleInput() {
117
+ return this.shadowRoot.querySelector('input')
118
+ }
119
+
120
+ async pageUpdated(changes) {
121
+ if (this.active) {
122
+ this.refresh()
123
+ }
124
+ }
125
+
126
+ async refresh() {
127
+ const response = await client.query({
128
+ query: gql`
129
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
130
+ menus(filters: $filters, pagination: $pagination, sortings: $sortings) {
131
+ items {
132
+ id
133
+ name
134
+ description
135
+ children {
136
+ id
137
+ name
138
+ }
139
+ }
140
+ total
141
+ }
142
+ }
143
+ `,
144
+ variables: {
145
+ filters: [],
146
+ pagination: {},
147
+ sortings: [{ name: 'name' }]
148
+ }
149
+ })
150
+
151
+ if (!response.errors?.length) {
152
+ const menus = response.data.menus.items || []
153
+ this.parentMenus = menus.filter(menu => menu.children?.length)
154
+ }
155
+ }
156
+
157
+ async getMenus(targetRole) {
158
+ const response = await client.query({
159
+ query: gql`
160
+ query ($roleId: String!) {
161
+ roleMenus(roleId: $roleId) {
162
+ items {
163
+ id
164
+ name
165
+ description
166
+ parent {
167
+ id
168
+ name
169
+ }
170
+ }
171
+ }
172
+ }
173
+ `,
174
+ variables: {
175
+ roleId: targetRole.id
176
+ }
177
+ })
178
+
179
+ if (!response.errors?.length) {
180
+ this.roleMenus = response.data.roleMenus.items || []
181
+ this.menuSet = (this.roleMenus || []).reduce((menuSet, menu) => {
182
+ const parentMenu = menu.parent.name
183
+ if (!menuSet[parentMenu]) menuSet[parentMenu] = []
184
+ menuSet[parentMenu].push(menu)
185
+
186
+ return menuSet
187
+ }, {})
188
+ }
189
+ }
190
+
191
+ onSelectRole() {
192
+ openPopup(
193
+ html`
194
+ <role-select-popup
195
+ @selected="${e => {
196
+ this.selectedRole = e.detail
197
+ this.roleInput.value = this.selectedRole.name
198
+ this.targetRole = this.selectedRole || {}
199
+ this.getMenus(this.targetRole)
200
+ }}"
201
+ ></role-select-popup>
202
+ `,
203
+ {
204
+ size: 'large',
205
+ title: `${i18next.t('title.role_select')}`
206
+ }
207
+ )
208
+ }
209
+
210
+ showToast(message) {
211
+ document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1000 } } }))
212
+ }
213
+ }
214
+
215
+ window.customElements.define('role-menus-management', RoleMenusManagement)
@@ -0,0 +1,15 @@
1
+ export default function route(page) {
2
+ switch (page) {
3
+ case 'menu-list':
4
+ import('./pages/menu-list-page')
5
+ return page
6
+
7
+ case 'menus':
8
+ import('./pages/menu-management')
9
+ return page
10
+
11
+ case 'role-menus':
12
+ import('./pages/role-menus-management')
13
+ return page
14
+ }
15
+ }
@@ -0,0 +1,114 @@
1
+ import '@material/web/icon/icon.js'
2
+
3
+ import { css, html, LitElement } from 'lit'
4
+
5
+ import ScrollBooster from 'scrollbooster'
6
+
7
+ export default class MenuBar extends LitElement {
8
+ static get styles() {
9
+ return [
10
+ css`
11
+ :host {
12
+ background-color: var(--menu-bar-background-color, #242d30);
13
+
14
+ overflow-x: hidden;
15
+ }
16
+
17
+ ul {
18
+ display: flex;
19
+ list-style: none;
20
+ margin: 0;
21
+ padding: 0;
22
+ white-space: nowrap;
23
+ }
24
+
25
+ li {
26
+ display: inline-block;
27
+ padding: 0px 3px;
28
+
29
+ border-bottom: var(--menu-bar-line);
30
+ }
31
+
32
+ li[active] {
33
+ border-color: var(--menu-bar-active-line-color, red);
34
+ }
35
+
36
+ li a {
37
+ display: block;
38
+ padding: 5px 4px 1px 4px;
39
+ text-decoration: none;
40
+ font: var(--menu-bar-textbutton);
41
+ color: rgba(255, 255, 255, 0.8);
42
+ }
43
+
44
+ li[active] a {
45
+ font: var(--menu-bar-textbutton-active);
46
+ color: rgba(255, 255, 255, 1);
47
+ }
48
+ `
49
+ ]
50
+ }
51
+
52
+ static get properties() {
53
+ return {
54
+ menus: Array,
55
+ menuId: String
56
+ }
57
+ }
58
+
59
+ render() {
60
+ var topmenus = this.menus || []
61
+
62
+ return html`
63
+ <ul>
64
+ <li ?active=${this.menuId !== 0 && !this.menuId}>
65
+ <a href="menu-list"><md-icon>star</md-icon></a>
66
+ </li>
67
+
68
+ ${topmenus.map(
69
+ (menu, idx) => html`
70
+ <li ?active=${this.menuId === String(idx)}>
71
+ <a href=${`${menu.routing || 'menu-list'}/${idx}`}>${menu.name}</a>
72
+ </li>
73
+ `
74
+ )}
75
+ </ul>
76
+ `
77
+ }
78
+
79
+ _onWheelEvent(e) {
80
+ var delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail))
81
+ this.scrollLeft -= delta * 40
82
+
83
+ e.preventDefault()
84
+ }
85
+
86
+ updated(change) {
87
+ if (change.has('menus')) {
88
+ /* menus가 바뀔 때마다, contents의 폭이 달라지므로, 다시 폭을 계산해준다. */
89
+ this.__sb && this.__sb.updateMetrics()
90
+ }
91
+
92
+ if (change.has('menuId')) {
93
+ var active = this.shadowRoot.querySelector('li[active]')
94
+ active && active.scrollIntoView()
95
+ }
96
+ }
97
+
98
+ firstUpdated() {
99
+ var scrollTarget = this.shadowRoot.querySelector('ul')
100
+
101
+ scrollTarget.addEventListener('mousewheel', this._onWheelEvent.bind(this), false)
102
+
103
+ this.__sb = new ScrollBooster({
104
+ viewport: this,
105
+ content: scrollTarget,
106
+ mode: 'x',
107
+ onUpdate: data => {
108
+ this.scrollLeft = data.position.x
109
+ }
110
+ })
111
+ }
112
+ }
113
+
114
+ window.customElements.define('menu-bar', MenuBar)
@@ -0,0 +1,222 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@operato/oops'
3
+
4
+ import { css, html, LitElement } from 'lit'
5
+
6
+ export default class MenuTileList extends LitElement {
7
+ static get styles() {
8
+ return [
9
+ css`
10
+ :host {
11
+ display: block;
12
+ box-sizing: border-box;
13
+
14
+ position: relative;
15
+ }
16
+
17
+ ul {
18
+ display: grid;
19
+ grid-template-columns: 1fr 1fr;
20
+ grid-auto-rows: 110px;
21
+ list-style: none;
22
+ padding: 0;
23
+ margin: 0;
24
+ }
25
+
26
+ ul > li {
27
+ margin: var(--menu-tile-list-item-margin);
28
+ padding: 10px;
29
+
30
+ position: relative;
31
+ }
32
+
33
+ md-icon {
34
+ cursor: pointer;
35
+ position: absolute;
36
+ right: 8px;
37
+ top: 8px;
38
+
39
+ color: var(--menu-tile-list-favorite-color);
40
+ font-size: 1em;
41
+ }
42
+
43
+ md-icon[selected] {
44
+ color: white;
45
+ text-shadow: 1px 1px 1px var(--menu-tile-list-favorite-color);
46
+ }
47
+
48
+ li.text a {
49
+ color: #fff;
50
+ text-decoration: none;
51
+
52
+ font-size: 1.4em;
53
+ line-height: 1.3;
54
+ word-wrap: break-word;
55
+ word-break: keep-all;
56
+
57
+ margin: 0px;
58
+ display: block;
59
+ width: 100%;
60
+ height: 100%;
61
+ }
62
+
63
+ li.text:nth-child(7n + 1) {
64
+ background-color: #4397de;
65
+ }
66
+
67
+ li.text:nth-child(7n + 2) {
68
+ background-color: #33b8d0;
69
+ }
70
+
71
+ li.text:nth-child(7n + 3) {
72
+ background-color: #4ab75f;
73
+ }
74
+
75
+ li.text:nth-child(7n + 4) {
76
+ background-color: #93796f;
77
+ }
78
+
79
+ li.text:nth-child(7n + 5) {
80
+ background-color: #f1ac42;
81
+ }
82
+
83
+ li.text:nth-child(7n + 6) {
84
+ background-color: #ea6361;
85
+ }
86
+
87
+ li.text:nth-child(7n + 7) {
88
+ background-color: #7386c3;
89
+ }
90
+
91
+ ox-oops-note {
92
+ display: block;
93
+ position: absolute;
94
+ left: 50%;
95
+ top: 50%;
96
+ transform: translate(-50%, -50%);
97
+ }
98
+
99
+ ox-oops-spinner {
100
+ display: none;
101
+ position: absolute;
102
+ left: 50%;
103
+ top: 50%;
104
+ transform: translate(-50%, -50%);
105
+ }
106
+
107
+ ox-oops-spinner[show] {
108
+ display: block;
109
+ }
110
+
111
+ @media (min-width: 600px) {
112
+ ul {
113
+ grid-template-columns: 1fr 1fr 1fr;
114
+ grid-auto-rows: 120px;
115
+ }
116
+ }
117
+ @media (min-width: 1200px) {
118
+ ul {
119
+ grid-template-columns: 1fr 1fr 1fr 1fr;
120
+ grid-auto-rows: 130px;
121
+ }
122
+ }
123
+ @media (min-width: 1800px) {
124
+ ul {
125
+ grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
126
+ grid-auto-rows: 140px;
127
+ }
128
+ }
129
+ @media (min-width: 2400px) {
130
+ ul {
131
+ grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
132
+ grid-auto-rows: 150px;
133
+ }
134
+ }
135
+ `
136
+ ]
137
+ }
138
+
139
+ static get properties() {
140
+ return {
141
+ menuId: String,
142
+ menus: Array,
143
+ routingTypes: Object,
144
+ favorites: Array,
145
+ showSpinner: Boolean
146
+ }
147
+ }
148
+
149
+ render() {
150
+ var topmenus = this.menus || []
151
+ var menuId = this.menuId
152
+
153
+ if (menuId !== 0 && !menuId) {
154
+ /* favorite menus */
155
+ var submenus = topmenus.reduce((allmenu, topmenu) => {
156
+ let menus = (topmenu && topmenu.children) || []
157
+ menus.forEach(menu => {
158
+ if (this.favorites.includes(this._getFullRouting(menu))) {
159
+ allmenu.push(menu)
160
+ }
161
+ })
162
+ return allmenu
163
+ }, [])
164
+ } else {
165
+ var menu = topmenus[menuId]
166
+ var submenus = (menu && menu.children) || []
167
+ }
168
+
169
+ return html`
170
+ <ul>
171
+ ${submenus.map(subMenu => {
172
+ const routing = this._getFullRouting(subMenu)
173
+
174
+ return html`
175
+ <li
176
+ class="${subMenu.class} text"
177
+ style="grid-row: span ${subMenu.routingType.toUpperCase() === 'STATIC' ? 1 : 2}"
178
+ >
179
+ <a href=${routing}>${subMenu.name}</a>
180
+
181
+ <md-icon ?selected=${(this.favorites || []).includes(routing)}
182
+ >${this.favorites || [].includes(routing) ? 'star' : 'star_border'}</md-icon
183
+ >
184
+ </li>
185
+ `
186
+ })}
187
+ </ul>
188
+
189
+ ${submenus.length == 0 && !this.showSpinner
190
+ ? typeof menuId == 'number'
191
+ ? html`
192
+ <ox-oops-note
193
+ icon="apps"
194
+ title="No Menu Found"
195
+ description="Seems like you are not authorised to view this menu"
196
+ ></ox-oops-note>
197
+ `
198
+ : html`
199
+ <ox-oops-note
200
+ icon="star_border"
201
+ title="No Favourite Found"
202
+ description="Click ☆ icon to add new favourite menu"
203
+ ></ox-oops-note>
204
+ `
205
+ : html``}
206
+
207
+ <ox-oops-spinner ?show=${this.showSpinner}></ox-oops-spinner>
208
+ `
209
+ }
210
+
211
+ _getFullRouting(menu) {
212
+ var { routingType, titleField, name, resourceUrl } = menu
213
+ if (routingType.toUpperCase() === 'STATIC') {
214
+ return resourceUrl
215
+ }
216
+
217
+ var { page } = this.routingTypes[routingType]
218
+ return titleField ? `${page}/${menu[titleField]}` : `${page}/${name}`
219
+ }
220
+ }
221
+
222
+ window.customElements.define('menu-tile-list', MenuTileList)