@things-factory/board-ui 10.1.40 → 10.1.42
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/dist-client/board-list/board-thumb.d.ts +33 -0
- package/dist-client/board-list/board-thumb.js +119 -0
- package/dist-client/board-list/board-thumb.js.map +1 -0
- package/dist-client/graphql/favorite-board.js +3 -0
- package/dist-client/graphql/favorite-board.js.map +1 -1
- package/dist-client/graphql/my-board.js +3 -0
- package/dist-client/graphql/my-board.js.map +1 -1
- package/dist-client/graphql/play-group.js +2 -0
- package/dist-client/graphql/play-group.js.map +1 -1
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +1 -0
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/board-list-page.d.ts +39 -42
- package/dist-client/pages/board-list-page.js +388 -284
- package/dist-client/pages/board-list-page.js.map +1 -1
- package/dist-client/pages/board-modeller-page.d.ts +2 -0
- package/dist-client/pages/board-modeller-page.js +18 -1
- package/dist-client/pages/board-modeller-page.js.map +1 -1
- package/dist-client/pages/board-template/board-template-list-page.d.ts +21 -10
- package/dist-client/pages/board-template/board-template-list-page.js +147 -136
- package/dist-client/pages/board-template/board-template-list-page.js.map +1 -1
- package/dist-client/pages/play-list-page.d.ts +22 -12
- package/dist-client/pages/play-list-page.js +264 -113
- package/dist-client/pages/play-list-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/setting-let/board-modeller-setting-let.d.ts +21 -0
- package/dist-client/setting-let/board-modeller-setting-let.js +144 -0
- package/dist-client/setting-let/board-modeller-setting-let.js.map +1 -0
- package/dist-client/styles/board-card-styles.d.ts +11 -0
- package/dist-client/styles/board-card-styles.js +90 -0
- package/dist-client/styles/board-card-styles.js.map +1 -0
- package/dist-client/themes/board-theme.css +32 -19
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/board-meta.d.ts +10 -0
- package/dist-client/utils/board-meta.js +34 -0
- package/dist-client/utils/board-meta.js.map +1 -0
- package/dist-client/viewparts/board-basic-info.d.ts +38 -7
- package/dist-client/viewparts/board-basic-info.js +378 -349
- package/dist-client/viewparts/board-basic-info.js.map +1 -1
- package/dist-client/viewparts/board-info-link.d.ts +5 -1
- package/dist-client/viewparts/board-info-link.js +79 -21
- package/dist-client/viewparts/board-info-link.js.map +1 -1
- package/dist-client/viewparts/board-info-template.d.ts +15 -0
- package/dist-client/viewparts/board-info-template.js +97 -0
- package/dist-client/viewparts/board-info-template.js.map +1 -0
- package/dist-client/viewparts/board-info.d.ts +5 -0
- package/dist-client/viewparts/board-info.js +24 -73
- package/dist-client/viewparts/board-info.js.map +1 -1
- package/dist-client/viewparts/board-template-builder.d.ts +1 -0
- package/dist-client/viewparts/board-template-builder.js +37 -65
- package/dist-client/viewparts/board-template-builder.js.map +1 -1
- package/dist-client/viewparts/board-template-info.d.ts +3 -0
- package/dist-client/viewparts/board-template-info.js +75 -134
- package/dist-client/viewparts/board-template-info.js.map +1 -1
- package/dist-client/viewparts/group-info-basic.d.ts +8 -3
- package/dist-client/viewparts/group-info-basic.js +54 -190
- package/dist-client/viewparts/group-info-basic.js.map +1 -1
- package/dist-client/viewparts/group-info-import.d.ts +3 -1
- package/dist-client/viewparts/group-info-import.js +29 -62
- package/dist-client/viewparts/group-info-import.js.map +1 -1
- package/dist-client/viewparts/group-info.d.ts +4 -0
- package/dist-client/viewparts/group-info.js +21 -55
- package/dist-client/viewparts/group-info.js.map +1 -1
- package/dist-client/viewparts/info-dialog-styles.d.ts +19 -0
- package/dist-client/viewparts/info-dialog-styles.js +194 -0
- package/dist-client/viewparts/info-dialog-styles.js.map +1 -0
- package/dist-client/viewparts/link-builder.d.ts +3 -0
- package/dist-client/viewparts/link-builder.js +87 -81
- package/dist-client/viewparts/link-builder.js.map +1 -1
- package/dist-client/viewparts/play-group-info-basic.d.ts +4 -3
- package/dist-client/viewparts/play-group-info-basic.js +49 -188
- package/dist-client/viewparts/play-group-info-basic.js.map +1 -1
- package/dist-client/viewparts/play-group-info.d.ts +4 -0
- package/dist-client/viewparts/play-group-info.js +22 -53
- package/dist-client/viewparts/play-group-info.js.map +1 -1
- package/package.json +4 -4
- package/translations/en.json +32 -0
- package/translations/ja.json +10 -0
- package/translations/ko.json +32 -0
- package/translations/ms.json +8 -0
- package/translations/zh.json +10 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame the three info dialogs share - board, group, play group.
|
|
3
|
+
*
|
|
4
|
+
* They open through openPopup (a centered card with its own title bar and close button), so this
|
|
5
|
+
* owns only what is inside the card: an optional tab strip and a scrolling body. The tabs are the
|
|
6
|
+
* underline kind the page title bar uses, not filled blocks - a filled tab strip inside a card
|
|
7
|
+
* reads as a second header fighting the card's own.
|
|
8
|
+
*
|
|
9
|
+
* Height follows the card. A small card (the group forms) is content-sized; a medium card (board)
|
|
10
|
+
* is viewport-sized, and the body scrolls inside it. Both work because the host takes 100% and
|
|
11
|
+
* the body is the one flex child allowed to shrink.
|
|
12
|
+
*/
|
|
13
|
+
export declare const InfoDialogStyles: import("lit").CSSResult;
|
|
14
|
+
/**
|
|
15
|
+
* The one form vocabulary the group and play-group dialogs use on top of CommonPopupStyles:
|
|
16
|
+
* a stack of fields, a quiet provenance line, and the action row. Kept next to the frame so the
|
|
17
|
+
* two forms cannot drift apart again (they had: same form, two sets of margins and button kinds).
|
|
18
|
+
*/
|
|
19
|
+
export declare const InfoFormStyles: import("lit").CSSResult;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* The frame the three info dialogs share - board, group, play group.
|
|
4
|
+
*
|
|
5
|
+
* They open through openPopup (a centered card with its own title bar and close button), so this
|
|
6
|
+
* owns only what is inside the card: an optional tab strip and a scrolling body. The tabs are the
|
|
7
|
+
* underline kind the page title bar uses, not filled blocks - a filled tab strip inside a card
|
|
8
|
+
* reads as a second header fighting the card's own.
|
|
9
|
+
*
|
|
10
|
+
* Height follows the card. A small card (the group forms) is content-sized; a medium card (board)
|
|
11
|
+
* is viewport-sized, and the body scrolls inside it. Both work because the host takes 100% and
|
|
12
|
+
* the body is the one flex child allowed to shrink.
|
|
13
|
+
*/
|
|
14
|
+
export const InfoDialogStyles = css `
|
|
15
|
+
:host {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
height: 100%;
|
|
19
|
+
min-height: 0;
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
/* One surface for every dialog; the theme sets --info-dialog-surface per scheme. */
|
|
22
|
+
background-color: var(--info-dialog-surface, var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface)));
|
|
23
|
+
color: var(--md-sys-color-on-surface);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
[tab] {
|
|
27
|
+
flex: none;
|
|
28
|
+
display: flex;
|
|
29
|
+
gap: var(--spacing-large, 16px);
|
|
30
|
+
padding: 0 var(--spacing-large, 16px);
|
|
31
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[tab] > span {
|
|
35
|
+
padding: 10px 2px 8px;
|
|
36
|
+
font-size: 0.78rem;
|
|
37
|
+
font-weight: 500;
|
|
38
|
+
letter-spacing: 0.03em;
|
|
39
|
+
text-transform: uppercase;
|
|
40
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
41
|
+
border-bottom: 2px solid transparent;
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
user-select: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
[tab] > span:hover {
|
|
47
|
+
color: var(--md-sys-color-on-surface);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[tab] > span[active] {
|
|
51
|
+
color: var(--md-sys-color-on-surface);
|
|
52
|
+
border-bottom-color: var(--md-sys-color-primary);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[content] {
|
|
56
|
+
flex: 1;
|
|
57
|
+
min-height: 0;
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
overflow-y: auto;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
[content] > * {
|
|
64
|
+
flex: 1;
|
|
65
|
+
min-height: 0;
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
/**
|
|
69
|
+
* The one form vocabulary the group and play-group dialogs use on top of CommonPopupStyles:
|
|
70
|
+
* a stack of fields, a quiet provenance line, and the action row. Kept next to the frame so the
|
|
71
|
+
* two forms cannot drift apart again (they had: same form, two sets of margins and button kinds).
|
|
72
|
+
*/
|
|
73
|
+
export const InfoFormStyles = css `
|
|
74
|
+
:host {
|
|
75
|
+
--popup-min-width: 0;
|
|
76
|
+
background: var(--info-dialog-surface, var(--ui-bg, #ffffff));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.fields {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
gap: var(--spacing-medium, 8px);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/*
|
|
86
|
+
* Inputs lighter than Material's filled field: a label over a thin-bordered box, the way the
|
|
87
|
+
* suite's own create-domain popup draws them. The filled field's grey slab and underline made a
|
|
88
|
+
* two-field dialog look heavier than the page behind it.
|
|
89
|
+
*/
|
|
90
|
+
.fields label {
|
|
91
|
+
display: flex;
|
|
92
|
+
flex-direction: column;
|
|
93
|
+
gap: 4px;
|
|
94
|
+
font-size: var(--popup-font-size-small);
|
|
95
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
96
|
+
/* Translations arrive in mixed case ("Name", "description"); one case for one form. */
|
|
97
|
+
text-transform: capitalize;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.fields label > input,
|
|
101
|
+
.fields label > textarea,
|
|
102
|
+
.fields label > select {
|
|
103
|
+
text-transform: none;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* A button whose label comes from a lower-case translation ("save") says so with [cap]; labels
|
|
107
|
+
* written in sentence case here ("Open in modeller") are left alone. */
|
|
108
|
+
md-filled-button[cap],
|
|
109
|
+
md-outlined-button[cap],
|
|
110
|
+
md-text-button[cap] {
|
|
111
|
+
text-transform: capitalize;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.fields input,
|
|
115
|
+
.fields textarea,
|
|
116
|
+
.fields select {
|
|
117
|
+
box-sizing: border-box;
|
|
118
|
+
width: 100%;
|
|
119
|
+
padding: 8px 10px;
|
|
120
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
121
|
+
border-radius: var(--md-sys-shape-corner-small, 6px);
|
|
122
|
+
background-color: var(--md-sys-color-surface);
|
|
123
|
+
color: var(--md-sys-color-on-surface);
|
|
124
|
+
font: inherit;
|
|
125
|
+
font-size: var(--popup-font-size);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.fields input:focus,
|
|
129
|
+
.fields textarea:focus,
|
|
130
|
+
.fields select:focus {
|
|
131
|
+
outline: none;
|
|
132
|
+
border-color: var(--md-sys-color-primary);
|
|
133
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--md-sys-color-primary) 18%, transparent);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* A section's name: small caps in the muted tone, never a heading competing with the title bar. */
|
|
137
|
+
.section {
|
|
138
|
+
margin-top: var(--spacing-medium, 8px);
|
|
139
|
+
font-size: 0.7rem;
|
|
140
|
+
font-weight: 600;
|
|
141
|
+
letter-spacing: 0.06em;
|
|
142
|
+
text-transform: uppercase;
|
|
143
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Read-only value with a copy affordance beside it (a link, an id). */
|
|
147
|
+
.copyable {
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
gap: var(--spacing-small, 4px);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.copyable input {
|
|
154
|
+
flex: 1;
|
|
155
|
+
min-width: 0;
|
|
156
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
157
|
+
font-size: 0.76rem;
|
|
158
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
159
|
+
background: var(--md-sys-color-surface-container-low);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.copyable md-icon-button {
|
|
163
|
+
--md-icon-button-icon-color: var(--md-sys-color-on-surface-variant);
|
|
164
|
+
--md-icon-button-icon-size: 18px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* Who made it and when - a side note under the fields, never a row of its own with icons. */
|
|
168
|
+
.provenance {
|
|
169
|
+
display: flex;
|
|
170
|
+
flex-wrap: wrap;
|
|
171
|
+
gap: 2px var(--spacing-large, 16px);
|
|
172
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
173
|
+
font-size: var(--popup-font-size-small);
|
|
174
|
+
line-height: var(--popup-line-height);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.provenance b {
|
|
178
|
+
font-weight: 500;
|
|
179
|
+
color: var(--md-sys-color-on-surface);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.buttons [filler] {
|
|
183
|
+
flex: 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* The irreversible action is a quiet text button on the far left, in the error colour. */
|
|
187
|
+
md-text-button[danger] {
|
|
188
|
+
--md-text-button-label-text-color: var(--md-sys-color-error);
|
|
189
|
+
--md-text-button-icon-color: var(--md-sys-color-error);
|
|
190
|
+
--md-text-button-hover-label-text-color: var(--md-sys-color-error);
|
|
191
|
+
--md-text-button-hover-state-layer-color: var(--md-sys-color-error);
|
|
192
|
+
}
|
|
193
|
+
`;
|
|
194
|
+
//# sourceMappingURL=info-dialog-styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info-dialog-styles.js","sourceRoot":"","sources":["../../client/viewparts/info-dialog-styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqDlC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwHhC,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * The frame the three info dialogs share - board, group, play group.\n *\n * They open through openPopup (a centered card with its own title bar and close button), so this\n * owns only what is inside the card: an optional tab strip and a scrolling body. The tabs are the\n * underline kind the page title bar uses, not filled blocks - a filled tab strip inside a card\n * reads as a second header fighting the card's own.\n *\n * Height follows the card. A small card (the group forms) is content-sized; a medium card (board)\n * is viewport-sized, and the body scrolls inside it. Both work because the host takes 100% and\n * the body is the one flex child allowed to shrink.\n */\nexport const InfoDialogStyles = css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n box-sizing: border-box;\n /* One surface for every dialog; the theme sets --info-dialog-surface per scheme. */\n background-color: var(--info-dialog-surface, var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface)));\n color: var(--md-sys-color-on-surface);\n }\n\n [tab] {\n flex: none;\n display: flex;\n gap: var(--spacing-large, 16px);\n padding: 0 var(--spacing-large, 16px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n\n [tab] > span {\n padding: 10px 2px 8px;\n font-size: 0.78rem;\n font-weight: 500;\n letter-spacing: 0.03em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant);\n border-bottom: 2px solid transparent;\n cursor: pointer;\n user-select: none;\n }\n\n [tab] > span:hover {\n color: var(--md-sys-color-on-surface);\n }\n\n [tab] > span[active] {\n color: var(--md-sys-color-on-surface);\n border-bottom-color: var(--md-sys-color-primary);\n }\n\n [content] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n }\n\n [content] > * {\n flex: 1;\n min-height: 0;\n }\n`\n\n/**\n * The one form vocabulary the group and play-group dialogs use on top of CommonPopupStyles:\n * a stack of fields, a quiet provenance line, and the action row. Kept next to the frame so the\n * two forms cannot drift apart again (they had: same form, two sets of margins and button kinds).\n */\nexport const InfoFormStyles = css`\n :host {\n --popup-min-width: 0;\n background: var(--info-dialog-surface, var(--ui-bg, #ffffff));\n }\n\n .fields {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n }\n\n /*\n * Inputs lighter than Material's filled field: a label over a thin-bordered box, the way the\n * suite's own create-domain popup draws them. The filled field's grey slab and underline made a\n * two-field dialog look heavier than the page behind it.\n */\n .fields label {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: var(--popup-font-size-small);\n color: var(--md-sys-color-on-surface-variant);\n /* Translations arrive in mixed case (\"Name\", \"description\"); one case for one form. */\n text-transform: capitalize;\n }\n\n .fields label > input,\n .fields label > textarea,\n .fields label > select {\n text-transform: none;\n }\n\n /* A button whose label comes from a lower-case translation (\"save\") says so with [cap]; labels\n * written in sentence case here (\"Open in modeller\") are left alone. */\n md-filled-button[cap],\n md-outlined-button[cap],\n md-text-button[cap] {\n text-transform: capitalize;\n }\n\n .fields input,\n .fields textarea,\n .fields select {\n box-sizing: border-box;\n width: 100%;\n padding: 8px 10px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-small, 6px);\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n font: inherit;\n font-size: var(--popup-font-size);\n }\n\n .fields input:focus,\n .fields textarea:focus,\n .fields select:focus {\n outline: none;\n border-color: var(--md-sys-color-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--md-sys-color-primary) 18%, transparent);\n }\n\n /* A section's name: small caps in the muted tone, never a heading competing with the title bar. */\n .section {\n margin-top: var(--spacing-medium, 8px);\n font-size: 0.7rem;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n /* Read-only value with a copy affordance beside it (a link, an id). */\n .copyable {\n display: flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n }\n\n .copyable input {\n flex: 1;\n min-width: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 0.76rem;\n color: var(--md-sys-color-on-surface-variant);\n background: var(--md-sys-color-surface-container-low);\n }\n\n .copyable md-icon-button {\n --md-icon-button-icon-color: var(--md-sys-color-on-surface-variant);\n --md-icon-button-icon-size: 18px;\n }\n\n /* Who made it and when - a side note under the fields, never a row of its own with icons. */\n .provenance {\n display: flex;\n flex-wrap: wrap;\n gap: 2px var(--spacing-large, 16px);\n color: var(--md-sys-color-on-surface-variant);\n font-size: var(--popup-font-size-small);\n line-height: var(--popup-line-height);\n }\n\n .provenance b {\n font-weight: 500;\n color: var(--md-sys-color-on-surface);\n }\n\n .buttons [filler] {\n flex: 1;\n }\n\n /* The irreversible action is a quiet text button on the far left, in the error colour. */\n md-text-button[danger] {\n --md-text-button-label-text-color: var(--md-sys-color-error);\n --md-text-button-icon-color: var(--md-sys-color-error);\n --md-text-button-hover-label-text-color: var(--md-sys-color-error);\n --md-text-button-hover-state-layer-color: var(--md-sys-color-error);\n }\n`\n"]}
|
|
@@ -2,6 +2,7 @@ import '@material/web/icon/icon.js';
|
|
|
2
2
|
import '@operato/i18n/ox-i18n.js';
|
|
3
3
|
import '@operato/input/ox-input-key-values.js';
|
|
4
4
|
import { LitElement, PropertyValues } from 'lit';
|
|
5
|
+
import '@material/web/iconbutton/icon-button.js';
|
|
5
6
|
export declare class LinkBuilder extends LitElement {
|
|
6
7
|
static styles: import("lit").CSSResult[];
|
|
7
8
|
command?: string;
|
|
@@ -17,6 +18,8 @@ export declare class LinkBuilder extends LitElement {
|
|
|
17
18
|
render(): import("lit-html").TemplateResult<1>;
|
|
18
19
|
firstUpdated(): void;
|
|
19
20
|
updated(changes: PropertyValues<this>): void;
|
|
21
|
+
/** Copies the current link; the tab's footer button calls this. */
|
|
22
|
+
copyLink(): Promise<void>;
|
|
20
23
|
objectToQueryString(obj: any): string;
|
|
21
24
|
getAppliances(): Promise<void>;
|
|
22
25
|
}
|
|
@@ -7,7 +7,9 @@ import Clipboard from 'clipboard';
|
|
|
7
7
|
import { css, html, LitElement } from 'lit';
|
|
8
8
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
9
9
|
import { client } from '@operato/graphql';
|
|
10
|
-
import { ScrollbarStyles } from '@operato/styles';
|
|
10
|
+
import { CommonPopupStyles, ScrollbarStyles } from '@operato/styles';
|
|
11
|
+
import { InfoFormStyles } from './info-dialog-styles.js';
|
|
12
|
+
import '@material/web/iconbutton/icon-button.js';
|
|
11
13
|
import { i18next } from '@operato/i18n';
|
|
12
14
|
import { hasPrivilege, privileged } from '@things-factory/auth-base/dist-client/index.js';
|
|
13
15
|
let LinkBuilder = class LinkBuilder extends LitElement {
|
|
@@ -18,120 +20,112 @@ let LinkBuilder = class LinkBuilder extends LitElement {
|
|
|
18
20
|
}
|
|
19
21
|
static { this.styles = [
|
|
20
22
|
ScrollbarStyles,
|
|
23
|
+
CommonPopupStyles,
|
|
24
|
+
InfoFormStyles,
|
|
21
25
|
css `
|
|
22
26
|
:host {
|
|
27
|
+
--popup-min-width: 0;
|
|
23
28
|
display: flex;
|
|
24
|
-
padding: var(--spacing-large);
|
|
25
|
-
color: var(--label-color, var(--md-sys-color-on-surface));
|
|
26
|
-
|
|
27
|
-
/* for narrow mode */
|
|
28
29
|
flex-direction: column;
|
|
30
|
+
gap: var(--spacing-medium, 8px);
|
|
31
|
+
color: var(--md-sys-color-on-surface);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
position: relative;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
label {
|
|
37
|
-
font: var(--label-font);
|
|
38
|
-
text-transform: capitalize;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
input,
|
|
42
|
-
select,
|
|
43
|
-
textarea,
|
|
44
|
-
ox-input-key-values {
|
|
45
|
-
border: var(--border-dim-color);
|
|
46
|
-
border-radius: var(--border-radius);
|
|
47
|
-
margin: var(--input-margin);
|
|
48
|
-
padding: var(--input-padding);
|
|
49
|
-
font: var(--input-font);
|
|
50
|
-
flex: 1 1 0%;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
textarea {
|
|
54
|
-
background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));
|
|
55
|
-
color: var(--input-field-color, var(--md-sys-color-on-surface));
|
|
56
|
-
min-height: 132px;
|
|
57
|
-
resize: none;
|
|
34
|
+
.section {
|
|
35
|
+
margin-top: 0;
|
|
58
36
|
}
|
|
59
37
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
outline: none;
|
|
38
|
+
.fields label[inline] {
|
|
39
|
+
flex-direction: row;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: var(--spacing-small, 4px);
|
|
65
42
|
}
|
|
66
43
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
44
|
+
.fields label[inline] input[type='checkbox'] {
|
|
45
|
+
width: auto;
|
|
46
|
+
margin: 0;
|
|
47
|
+
accent-color: var(--md-sys-color-primary);
|
|
70
48
|
}
|
|
71
49
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
50
|
+
ox-input-key-values {
|
|
51
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
52
|
+
border-radius: var(--md-sys-shape-corner-small, 6px);
|
|
53
|
+
padding: 4px;
|
|
77
54
|
background-color: var(--md-sys-color-surface);
|
|
78
|
-
opacity: 0.8;
|
|
79
|
-
color: var(--md-sys-color-primary);
|
|
80
55
|
}
|
|
81
56
|
|
|
82
57
|
[warn] {
|
|
83
|
-
color: var(--
|
|
58
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
59
|
+
font-size: var(--popup-font-size-small);
|
|
84
60
|
}
|
|
85
61
|
`
|
|
86
62
|
]; }
|
|
87
63
|
render() {
|
|
88
64
|
return html `
|
|
89
|
-
<div
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
65
|
+
<div class="section">
|
|
66
|
+
<ox-title-with-help topic="operato-board/headless-link" msgid="label.headless-link">headless link</ox-title-with-help>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="note">
|
|
69
|
+
${i18next.t('text.headless-link hint', {
|
|
70
|
+
defaultValue: 'Opens this board full screen with no application chrome - for a wall display or a kiosk.'
|
|
71
|
+
})}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div class="fields">
|
|
75
|
+
<div class="copyable">
|
|
76
|
+
<input id="link" type="text" .value=${this.link || ''} readonly @focus=${e => e.target.select()} />
|
|
77
|
+
<md-icon-button clipboard-copy title=${i18next.t('button.copy-url', { defaultValue: 'Copy link' })}>
|
|
78
|
+
<md-icon>content_copy</md-icon>
|
|
79
|
+
</md-icon-button>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="row">
|
|
83
|
+
<label>
|
|
84
|
+
${i18next.t('label.headless-link query-key')}
|
|
85
|
+
<input id="key" type="text" .value=${this.key || ''} @change=${e => (this.key = e.target.value)} />
|
|
86
|
+
</label>
|
|
87
|
+
<label>
|
|
88
|
+
${i18next.t('label.headless-link target-appliance')}
|
|
89
|
+
${privileged({ privilege: 'query', category: 'security', superUserGranted: true, domainOwnerGranted: true }, html `
|
|
90
|
+
<select
|
|
91
|
+
@change=${e => (this.targetAppliance = this.appliances.find(appliance => appliance.id == e.target.value))}
|
|
92
|
+
.value=${this.targetAppliance?.id}
|
|
93
|
+
>
|
|
94
|
+
<option value=""></option>
|
|
95
|
+
${this.appliances.map(appliance => html `
|
|
96
|
+
<option .value=${appliance.id} ?selected=${appliance.id == this.targetAppliance?.id}>
|
|
97
|
+
${appliance.name}
|
|
98
|
+
</option>
|
|
99
|
+
`)}
|
|
100
|
+
</select>
|
|
101
|
+
`, html `<span warn>
|
|
102
|
+
${i18next.t('text.appliance token needs security privilege', {
|
|
103
|
+
defaultValue: 'Adding an access token needs domain-owner or security query permission.'
|
|
104
|
+
})}
|
|
105
|
+
</span>`)}
|
|
106
|
+
</label>
|
|
107
|
+
</div>
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
.value=${this.targetAppliance?.id}
|
|
108
|
-
>
|
|
109
|
-
<option value=""></option>
|
|
110
|
-
${this.appliances.map(appliance => html `
|
|
111
|
-
<option .value=${appliance.id} ?selected=${appliance.id == this.targetAppliance?.id}>
|
|
112
|
-
${appliance.name}
|
|
113
|
-
</option>
|
|
114
|
-
`)}
|
|
115
|
-
</select>
|
|
116
|
-
`, html `
|
|
117
|
-
<span warn>To add an access token, you need to be a domain owner or have security query permissions.</span>
|
|
118
|
-
`)}
|
|
109
|
+
<label>
|
|
110
|
+
${i18next.t('label.headless-link query-parameters')}
|
|
111
|
+
<ox-input-key-values @change=${e => (this.keyvalues = e.detail)}></ox-input-key-values>
|
|
112
|
+
</label>
|
|
119
113
|
|
|
120
|
-
<
|
|
114
|
+
<label inline>
|
|
121
115
|
<input
|
|
122
116
|
type="checkbox"
|
|
123
117
|
id="by-name"
|
|
124
118
|
@change=${(e) => (this.byname = !!e.target?.checked)}
|
|
125
119
|
/>
|
|
126
|
-
|
|
127
|
-
</
|
|
120
|
+
${i18next.t('label.headless-link by-name')}
|
|
121
|
+
</label>
|
|
128
122
|
</div>
|
|
129
123
|
`;
|
|
130
124
|
}
|
|
131
125
|
firstUpdated() {
|
|
132
126
|
const copybuttons = this.renderRoot.querySelectorAll('[clipboard-copy]');
|
|
133
127
|
this.clipboard = new Clipboard(copybuttons, {
|
|
134
|
-
target: (trigger => trigger.parentElement.querySelector('
|
|
128
|
+
target: (trigger => trigger.parentElement.querySelector('input'))
|
|
135
129
|
});
|
|
136
130
|
this.getAppliances();
|
|
137
131
|
}
|
|
@@ -147,6 +141,18 @@ let LinkBuilder = class LinkBuilder extends LitElement {
|
|
|
147
141
|
const queryString = this.objectToQueryString(query);
|
|
148
142
|
this.link = fullUrl + (queryString ? '?' + queryString : '');
|
|
149
143
|
}
|
|
144
|
+
/** Copies the current link; the tab's footer button calls this. */
|
|
145
|
+
async copyLink() {
|
|
146
|
+
try {
|
|
147
|
+
await navigator.clipboard.writeText(this.link || '');
|
|
148
|
+
this.dispatchEvent(new CustomEvent('copied', { bubbles: true, composed: true }));
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
const input = this.renderRoot.querySelector('input#link');
|
|
152
|
+
input?.select();
|
|
153
|
+
document.execCommand('copy');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
150
156
|
objectToQueryString(obj) {
|
|
151
157
|
return Object.keys(obj)
|
|
152
158
|
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link-builder.js","sourceRoot":"","sources":["../../client/viewparts/link-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/i18n/ox-i18n.js'\nimport '@operato/input/ox-input-key-values.js'\n\nimport gql from 'graphql-tag'\nimport Clipboard from 'clipboard'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { i18next } from '@operato/i18n'\n\nimport { hasPrivilege, privileged } from '@things-factory/auth-base/dist-client/index.js'\n\n@customElement('link-builder')\nexport class LinkBuilder extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n padding: var(--spacing-large);\n color: var(--label-color, var(--md-sys-color-on-surface));\n\n /* for narrow mode */\n flex-direction: column;\n }\n\n div[link] {\n display: grid;\n position: relative;\n }\n\n label {\n font: var(--label-font);\n text-transform: capitalize;\n }\n\n input,\n select,\n textarea,\n ox-input-key-values {\n border: var(--border-dim-color);\n border-radius: var(--border-radius);\n margin: var(--input-margin);\n padding: var(--input-padding);\n font: var(--input-font);\n flex: 1 1 0%;\n }\n\n textarea {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n min-height: 132px;\n resize: none;\n }\n\n select:focus,\n input:focus,\n textarea:focus,\n button {\n outline: none;\n }\n\n button:hover {\n border: var(--button-activ-border);\n box-shadow: var(--button-active-box-shadow);\n }\n\n [button-in-field] {\n position: absolute;\n top: 64px;\n right: 6px;\n\n background-color: var(--md-sys-color-surface);\n opacity: 0.8;\n color: var(--md-sys-color-primary);\n }\n\n [warn] {\n color: var(--status-warning-color, red);\n }\n `\n ]\n\n @property({ type: String, attribute: true }) command?: string\n @property({ type: String, attribute: 'target-id' }) targetId?: string\n @property({ type: String, attribute: 'target-name' }) targetName?: string\n\n @state() link?: string\n @state() keyvalues: any\n @state() appliances: any[] = []\n @state() targetAppliance: any\n @state() key?: string\n @state() byname: boolean = false\n\n private clipboard?: Clipboard\n\n render() {\n return html`\n <div link>\n <h3>\n <ox-title-with-help topic=\"operato-board/headless-link\" msgid=\"label.headless-link\"\n >headless-link</ox-title-with-help\n >\n </h3>\n <textarea id=\"link\" .value=${this.link || ''} readonly></textarea>\n <md-icon button-in-field clipboard-copy>content_copy</md-icon>\n <label for=\"key\">${i18next.t('label.headless-link query-key')}</label>\n <input id=\"key\" type=\"text\" .value=${this.key || ''} @change=${e => (this.key = e.target.value)} />\n <label>${i18next.t('label.headless-link query-parameters')}</label>\n <ox-input-key-values @change=${e => (this.keyvalues = e.detail)}></ox-input-key-values>\n <label>${i18next.t('label.headless-link target-appliance')}</label>\n\n ${privileged(\n { privilege: 'query', category: 'security', superUserGranted: true, domainOwnerGranted: true },\n html`\n <select\n @change=${e => (this.targetAppliance = this.appliances.find(appliance => appliance.id == e.target.value))}\n placeholder=\"choose appliance ...\"\n .value=${this.targetAppliance?.id}\n >\n <option value=\"\"></option>\n ${this.appliances.map(\n appliance => html`\n <option .value=${appliance.id} ?selected=${appliance.id == this.targetAppliance?.id}>\n ${appliance.name}\n </option>\n `\n )}\n </select>\n `,\n html`\n <span warn>To add an access token, you need to be a domain owner or have security query permissions.</span>\n `\n )}\n\n <div>\n <input\n type=\"checkbox\"\n id=\"by-name\"\n @change=${(e: Event) => (this.byname = !!(e.target as HTMLInputElement)?.checked)}\n />\n <label for=\"by-name\">${i18next.t('label.headless-link by-name')}</label>\n </div>\n </div>\n `\n }\n\n firstUpdated() {\n const copybuttons = this.renderRoot.querySelectorAll('[clipboard-copy]')\n\n this.clipboard = new Clipboard(copybuttons, {\n target: (trigger => trigger.parentElement.querySelector('textarea')) as any\n })\n\n this.getAppliances()\n }\n\n updated(changes: PropertyValues<this>) {\n const command = (this.command || '') + (this.byname ? '-by-name' : '')\n const target = encodeURIComponent((this.byname ? this.targetName : this.targetId) || '')\n\n const baseUrl = document.querySelector('base')?.href\n const fullUrl = new URL(command + '/' + target, baseUrl).href\n\n const query = {\n ...(this.key ? { [this.key]: JSON.stringify(this.keyvalues) } : this.keyvalues),\n ...(this.targetAppliance ? { access_token: this.targetAppliance?.accessToken } : {})\n }\n\n const queryString = this.objectToQueryString(query)\n\n this.link = fullUrl + (queryString ? '?' + queryString : '')\n }\n\n objectToQueryString(obj) {\n return Object.keys(obj)\n .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)\n .join('&')\n }\n\n async getAppliances() {\n this.appliances = []\n this.targetAppliance = null\n\n if (\n await hasPrivilege({ privilege: 'query', category: 'security', superUserGranted: true, domainOwnerGranted: true })\n ) {\n const response = (\n await client.query({\n query: gql`\n query {\n appliances {\n items {\n id\n name\n description\n accessToken\n }\n }\n }\n `\n })\n ).data\n\n this.appliances = response.appliances.items\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"link-builder.js","sourceRoot":"","sources":["../../client/viewparts/link-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/i18n/ox-i18n.js'\nimport '@operato/input/ox-input-key-values.js'\n\nimport gql from 'graphql-tag'\nimport Clipboard from 'clipboard'\n\nimport { css, html, LitElement, PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { CommonPopupStyles, ScrollbarStyles } from '@operato/styles'\nimport { InfoFormStyles } from './info-dialog-styles.js'\nimport '@material/web/iconbutton/icon-button.js'\nimport { i18next } from '@operato/i18n'\n\nimport { hasPrivilege, privileged } from '@things-factory/auth-base/dist-client/index.js'\n\n@customElement('link-builder')\nexport class LinkBuilder extends LitElement {\n static styles = [\n ScrollbarStyles,\n CommonPopupStyles,\n InfoFormStyles,\n css`\n :host {\n --popup-min-width: 0;\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n color: var(--md-sys-color-on-surface);\n }\n\n .section {\n margin-top: 0;\n }\n\n .fields label[inline] {\n flex-direction: row;\n align-items: center;\n gap: var(--spacing-small, 4px);\n }\n\n .fields label[inline] input[type='checkbox'] {\n width: auto;\n margin: 0;\n accent-color: var(--md-sys-color-primary);\n }\n\n ox-input-key-values {\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-small, 6px);\n padding: 4px;\n background-color: var(--md-sys-color-surface);\n }\n\n [warn] {\n color: var(--md-sys-color-on-surface-variant);\n font-size: var(--popup-font-size-small);\n }\n `\n ]\n\n @property({ type: String, attribute: true }) command?: string\n @property({ type: String, attribute: 'target-id' }) targetId?: string\n @property({ type: String, attribute: 'target-name' }) targetName?: string\n\n @state() link?: string\n @state() keyvalues: any\n @state() appliances: any[] = []\n @state() targetAppliance: any\n @state() key?: string\n @state() byname: boolean = false\n\n private clipboard?: Clipboard\n\n render() {\n return html`\n <div class=\"section\">\n <ox-title-with-help topic=\"operato-board/headless-link\" msgid=\"label.headless-link\">headless link</ox-title-with-help>\n </div>\n <div class=\"note\">\n ${i18next.t('text.headless-link hint', {\n defaultValue: 'Opens this board full screen with no application chrome - for a wall display or a kiosk.'\n })}\n </div>\n\n <div class=\"fields\">\n <div class=\"copyable\">\n <input id=\"link\" type=\"text\" .value=${this.link || ''} readonly @focus=${e => e.target.select()} />\n <md-icon-button clipboard-copy title=${i18next.t('button.copy-url', { defaultValue: 'Copy link' })}>\n <md-icon>content_copy</md-icon>\n </md-icon-button>\n </div>\n\n <div class=\"row\">\n <label>\n ${i18next.t('label.headless-link query-key')}\n <input id=\"key\" type=\"text\" .value=${this.key || ''} @change=${e => (this.key = e.target.value)} />\n </label>\n <label>\n ${i18next.t('label.headless-link target-appliance')}\n ${privileged(\n { privilege: 'query', category: 'security', superUserGranted: true, domainOwnerGranted: true },\n html`\n <select\n @change=${e => (this.targetAppliance = this.appliances.find(appliance => appliance.id == e.target.value))}\n .value=${this.targetAppliance?.id}\n >\n <option value=\"\"></option>\n ${this.appliances.map(\n appliance => html`\n <option .value=${appliance.id} ?selected=${appliance.id == this.targetAppliance?.id}>\n ${appliance.name}\n </option>\n `\n )}\n </select>\n `,\n html`<span warn>\n ${i18next.t('text.appliance token needs security privilege', {\n defaultValue: 'Adding an access token needs domain-owner or security query permission.'\n })}\n </span>`\n )}\n </label>\n </div>\n\n <label>\n ${i18next.t('label.headless-link query-parameters')}\n <ox-input-key-values @change=${e => (this.keyvalues = e.detail)}></ox-input-key-values>\n </label>\n\n <label inline>\n <input\n type=\"checkbox\"\n id=\"by-name\"\n @change=${(e: Event) => (this.byname = !!(e.target as HTMLInputElement)?.checked)}\n />\n ${i18next.t('label.headless-link by-name')}\n </label>\n </div>\n `\n }\n\n firstUpdated() {\n const copybuttons = this.renderRoot.querySelectorAll('[clipboard-copy]')\n\n this.clipboard = new Clipboard(copybuttons, {\n target: (trigger => trigger.parentElement.querySelector('input')) as any\n })\n\n this.getAppliances()\n }\n\n updated(changes: PropertyValues<this>) {\n const command = (this.command || '') + (this.byname ? '-by-name' : '')\n const target = encodeURIComponent((this.byname ? this.targetName : this.targetId) || '')\n\n const baseUrl = document.querySelector('base')?.href\n const fullUrl = new URL(command + '/' + target, baseUrl).href\n\n const query = {\n ...(this.key ? { [this.key]: JSON.stringify(this.keyvalues) } : this.keyvalues),\n ...(this.targetAppliance ? { access_token: this.targetAppliance?.accessToken } : {})\n }\n\n const queryString = this.objectToQueryString(query)\n\n this.link = fullUrl + (queryString ? '?' + queryString : '')\n }\n\n /** Copies the current link; the tab's footer button calls this. */\n async copyLink() {\n try {\n await navigator.clipboard.writeText(this.link || '')\n this.dispatchEvent(new CustomEvent('copied', { bubbles: true, composed: true }))\n } catch (e) {\n const input = this.renderRoot.querySelector('input#link') as HTMLInputElement\n input?.select()\n document.execCommand('copy')\n }\n }\n\n objectToQueryString(obj) {\n return Object.keys(obj)\n .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`)\n .join('&')\n }\n\n async getAppliances() {\n this.appliances = []\n this.targetAppliance = null\n\n if (\n await hasPrivilege({ privilege: 'query', category: 'security', superUserGranted: true, domainOwnerGranted: true })\n ) {\n const response = (\n await client.query({\n query: gql`\n query {\n appliances {\n items {\n id\n name\n description\n accessToken\n }\n }\n }\n `\n })\n ).data\n\n this.appliances = response.appliances.items\n }\n }\n}\n"]}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import '@material/web/icon/icon.js';
|
|
2
|
-
import '@material/web/
|
|
3
|
-
import '@material/web/button/
|
|
2
|
+
import '@material/web/button/filled-button.js';
|
|
3
|
+
import '@material/web/button/text-button.js';
|
|
4
4
|
import { LitElement } from 'lit';
|
|
5
|
+
/** The play-group form - the same shape as group-info-basic.ts, on purpose. */
|
|
5
6
|
export declare class PlayGroupInfoBasic extends LitElement {
|
|
6
7
|
static styles: import("lit").CSSResult[];
|
|
7
8
|
playGroupId?: string;
|
|
8
9
|
playGroup: any;
|
|
9
|
-
private link?;
|
|
10
10
|
render(): import("lit-html").TemplateResult<1>;
|
|
11
|
+
private renderProvenance;
|
|
11
12
|
updated(changes: any): void;
|
|
12
13
|
firstUpdated(): void;
|
|
13
14
|
refresh(): Promise<void>;
|