@things-factory/inquiry 10.1.251 → 10.1.253
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/client/components/inquiry-writer.ts +108 -104
- package/dist-client/components/inquiry-writer.d.ts +2 -0
- package/dist-client/components/inquiry-writer.js +98 -95
- package/dist-client/components/inquiry-writer.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/test/writer-popup-standard.test.ts +40 -0
- package/translations/en.json +2 -2
- package/translations/es.json +2 -2
- package/translations/ja.json +2 -2
- package/translations/ko.json +2 -2
- package/translations/ms.json +2 -2
- package/translations/zh.json +2 -2
|
@@ -7,92 +7,55 @@ import '@material/web/icon/icon.js'
|
|
|
7
7
|
import '@material/web/button/filled-button.js'
|
|
8
8
|
import '@material/web/button/text-button.js'
|
|
9
9
|
import '@operato/input/ox-input-file.js'
|
|
10
|
+
import '@operato/input/ox-input-select-buttons.js'
|
|
11
|
+
import '@operato/popup/ox-popup-actions.js'
|
|
10
12
|
|
|
11
13
|
import { css, html, LitElement, nothing } from 'lit'
|
|
12
14
|
import { customElement, property, state } from 'lit/decorators.js'
|
|
13
15
|
|
|
14
16
|
import { i18next, localize } from '@operato/i18n'
|
|
15
17
|
import { notify, openPopup } from '@operato/layout'
|
|
16
|
-
import {
|
|
18
|
+
import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'
|
|
17
19
|
|
|
18
20
|
import { inquiriesChanged, inquiryApi } from '../utils/inquiry-api.js'
|
|
19
|
-
import {
|
|
21
|
+
import { KINDS, kindWord, reasonOf, t } from '../utils/inquiry-words.js'
|
|
20
22
|
|
|
21
23
|
const MAX_TITLE = 200
|
|
22
24
|
|
|
23
25
|
/** Opens the window, carrying the screen it is opened on. */
|
|
24
26
|
export function openInquiryWriter(kind = 'QUESTION') {
|
|
25
27
|
const pageUrl = location.href
|
|
26
|
-
const handle: any = openPopup(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const handle: any = openPopup(
|
|
29
|
+
html`<inquiry-writer .kind=${kind} .pageUrl=${pageUrl} @close=${() => handle.close()}></inquiry-writer>`,
|
|
30
|
+
{
|
|
31
|
+
backdrop: true,
|
|
32
|
+
size: 'large',
|
|
33
|
+
title: t('inquiry.title.write')
|
|
34
|
+
}
|
|
35
|
+
)
|
|
31
36
|
return handle
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
@customElement('inquiry-writer')
|
|
35
40
|
export class InquiryWriter extends localize(i18next)(LitElement) {
|
|
41
|
+
/* The house shape of a popup (page-standard sections 7, 8): only the body scrolls, fields are labels above native
|
|
42
|
+
inputs, the one button row is `ox-popup-actions`. */
|
|
36
43
|
static styles = [
|
|
44
|
+
CommonPopupStyles,
|
|
45
|
+
PopupBodyStyles,
|
|
37
46
|
FormFieldStyles,
|
|
38
|
-
|
|
47
|
+
PopupFieldStyles,
|
|
39
48
|
css`
|
|
40
|
-
|
|
41
|
-
display: flex;
|
|
42
|
-
flex-direction: column;
|
|
43
|
-
gap: var(--spacing-medium, 8px);
|
|
44
|
-
padding: var(--spacing-large, 12px);
|
|
45
|
-
color: var(--md-sys-color-on-surface);
|
|
46
|
-
overflow: auto;
|
|
47
|
-
}
|
|
48
|
-
label {
|
|
49
|
-
display: flex;
|
|
50
|
-
flex-direction: column;
|
|
51
|
-
gap: 2px;
|
|
52
|
-
}
|
|
53
|
-
label.check {
|
|
54
|
-
flex-direction: row;
|
|
55
|
-
align-items: center;
|
|
56
|
-
gap: var(--spacing-small, 4px);
|
|
57
|
-
}
|
|
58
|
-
.kinds {
|
|
59
|
-
display: inline-flex;
|
|
60
|
-
gap: 4px;
|
|
61
|
-
padding: 3px;
|
|
62
|
-
border-radius: 10px;
|
|
63
|
-
background: var(--ui-panel-2, var(--md-sys-color-surface-container));
|
|
49
|
+
ox-input-select-buttons {
|
|
64
50
|
align-self: flex-start;
|
|
65
51
|
}
|
|
66
|
-
.kinds button {
|
|
67
|
-
display: inline-flex;
|
|
68
|
-
align-items: center;
|
|
69
|
-
gap: 4px;
|
|
70
|
-
padding: 4px 14px;
|
|
71
|
-
border: 0;
|
|
72
|
-
border-radius: 8px;
|
|
73
|
-
background: transparent;
|
|
74
|
-
box-shadow: none;
|
|
75
|
-
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
76
|
-
font: inherit;
|
|
77
|
-
font-weight: 600;
|
|
78
|
-
cursor: pointer;
|
|
79
|
-
}
|
|
80
|
-
.kinds button[aria-pressed='true'] {
|
|
81
|
-
background: var(--ui-panel, var(--md-sys-color-surface-container-lowest));
|
|
82
|
-
color: var(--ui-ink, var(--md-sys-color-on-surface));
|
|
83
|
-
}
|
|
84
|
-
.kinds md-icon {
|
|
85
|
-
--md-icon-size: 16px;
|
|
86
|
-
}
|
|
87
52
|
textarea {
|
|
88
53
|
min-height: 160px;
|
|
89
|
-
|
|
90
|
-
box-sizing: border-box;
|
|
91
|
-
width: 100%;
|
|
54
|
+
resize: vertical;
|
|
92
55
|
}
|
|
93
|
-
.
|
|
94
|
-
|
|
95
|
-
|
|
56
|
+
.note {
|
|
57
|
+
color: var(--ui-muted);
|
|
58
|
+
font-size: var(--ui-t-small, 11.5px);
|
|
96
59
|
word-break: break-all;
|
|
97
60
|
}
|
|
98
61
|
.files {
|
|
@@ -100,15 +63,15 @@ export class InquiryWriter extends localize(i18next)(LitElement) {
|
|
|
100
63
|
flex-wrap: wrap;
|
|
101
64
|
gap: var(--spacing-small, 4px);
|
|
102
65
|
align-items: center;
|
|
66
|
+
color: var(--ui-ink);
|
|
103
67
|
}
|
|
104
68
|
.file {
|
|
105
69
|
display: inline-flex;
|
|
106
70
|
align-items: center;
|
|
107
71
|
gap: 2px;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
font-size: var(--ui-t-small, 12px);
|
|
72
|
+
}
|
|
73
|
+
.file md-icon {
|
|
74
|
+
--md-icon-size: 14px;
|
|
112
75
|
}
|
|
113
76
|
.file button {
|
|
114
77
|
border: 0;
|
|
@@ -116,15 +79,18 @@ export class InquiryWriter extends localize(i18next)(LitElement) {
|
|
|
116
79
|
box-shadow: none;
|
|
117
80
|
padding: 0;
|
|
118
81
|
cursor: pointer;
|
|
119
|
-
color:
|
|
82
|
+
color: var(--ui-muted);
|
|
120
83
|
}
|
|
121
|
-
.
|
|
122
|
-
--md-icon-size: 14px;
|
|
123
|
-
}
|
|
124
|
-
.buttons {
|
|
84
|
+
.check {
|
|
125
85
|
display: flex;
|
|
126
|
-
|
|
127
|
-
gap: var(--spacing-
|
|
86
|
+
align-items: center;
|
|
87
|
+
gap: var(--spacing-small, 4px);
|
|
88
|
+
color: var(--ui-ink);
|
|
89
|
+
}
|
|
90
|
+
/* The field rule stretches its inputs to the row; a checkbox keeps its own size. */
|
|
91
|
+
.field .check input {
|
|
92
|
+
width: auto;
|
|
93
|
+
margin: 0;
|
|
128
94
|
}
|
|
129
95
|
`
|
|
130
96
|
]
|
|
@@ -142,39 +108,71 @@ export class InquiryWriter extends localize(i18next)(LitElement) {
|
|
|
142
108
|
render() {
|
|
143
109
|
const title = this.title.trim()
|
|
144
110
|
return html`
|
|
145
|
-
<div class="
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
111
|
+
<div class="body">
|
|
112
|
+
<div class="field">
|
|
113
|
+
<ox-input-select-buttons
|
|
114
|
+
.value=${this.kind}
|
|
115
|
+
.options=${KINDS.map(code => ({ display: kindWord(code), value: code }))}
|
|
116
|
+
@change=${(e: CustomEvent) => e.detail && (this.kind = e.detail)}
|
|
117
|
+
></ox-input-select-buttons>
|
|
118
|
+
<span>${t(`inquiry.text.kind-hint-${this.kind.toLowerCase()}`)}</span>
|
|
119
|
+
</div>
|
|
120
|
+
<label class="field">
|
|
121
|
+
<span
|
|
122
|
+
>${t('inquiry.field.title')} ·
|
|
123
|
+
${t('inquiry.text.title-limit', { count: this.title.length, max: MAX_TITLE })}</span
|
|
124
|
+
>
|
|
125
|
+
<input .value=${this.title} maxlength=${MAX_TITLE} @input=${(e: any) => (this.title = e.target.value)} />
|
|
126
|
+
</label>
|
|
127
|
+
<label class="field">
|
|
128
|
+
<span>${t('inquiry.field.body')}</span>
|
|
129
|
+
<textarea
|
|
130
|
+
.value=${this.body}
|
|
131
|
+
placeholder=${t(`inquiry.text.body-placeholder-${this.kind.toLowerCase()}`)}
|
|
132
|
+
@input=${(e: any) => (this.body = e.target.value)}
|
|
133
|
+
></textarea>
|
|
134
|
+
</label>
|
|
135
|
+
<div class="files">
|
|
136
|
+
${this.files.map(
|
|
137
|
+
(file, index) =>
|
|
138
|
+
html`<span class="file"
|
|
139
|
+
><md-icon>attach_file</md-icon>${file.name}<button
|
|
140
|
+
aria-label=${t('inquiry.button.remove')}
|
|
141
|
+
@click=${() => (this.files = this.files.filter((_, i) => i !== index))}
|
|
142
|
+
>
|
|
143
|
+
<md-icon>close</md-icon>
|
|
144
|
+
</button></span
|
|
145
|
+
>`
|
|
146
|
+
)}
|
|
147
|
+
<ox-input-file
|
|
148
|
+
quiet
|
|
149
|
+
hide-filelist
|
|
150
|
+
multiple
|
|
151
|
+
icon="attach_file"
|
|
152
|
+
.progress=${this.progress}
|
|
153
|
+
@change=${(e: CustomEvent) => this.pick(e)}
|
|
154
|
+
></ox-input-file>
|
|
155
|
+
</div>
|
|
156
|
+
<div class="field">
|
|
157
|
+
<label class="check"
|
|
158
|
+
><input
|
|
159
|
+
type="checkbox"
|
|
160
|
+
.checked=${this.privately}
|
|
161
|
+
@change=${(e: any) => (this.privately = e.target.checked)}
|
|
162
|
+
/>${t('inquiry.text.private-label')}</label
|
|
163
|
+
>
|
|
164
|
+
<span>${this.privately ? t('inquiry.text.private-hint') : t('inquiry.text.domain-hint')}</span>
|
|
165
|
+
</div>
|
|
166
|
+
${this.pageUrl ? html`<div class="note">${t('inquiry.text.page-hint', { page: this.pageUrl })}</div>` : nothing}
|
|
177
167
|
</div>
|
|
168
|
+
<ox-popup-actions>
|
|
169
|
+
<md-text-button slot="cancel" @click=${() => this.dispatchEvent(new CustomEvent('close'))}
|
|
170
|
+
>${t('inquiry.button.cancel')}</md-text-button
|
|
171
|
+
>
|
|
172
|
+
<md-filled-button slot="confirm" ?disabled=${!title || this.sending} @click=${() => this.send()}
|
|
173
|
+
>${t('inquiry.button.submit')}</md-filled-button
|
|
174
|
+
>
|
|
175
|
+
</ox-popup-actions>
|
|
178
176
|
`
|
|
179
177
|
}
|
|
180
178
|
|
|
@@ -190,14 +188,20 @@ export class InquiryWriter extends localize(i18next)(LitElement) {
|
|
|
190
188
|
this.sending = true
|
|
191
189
|
try {
|
|
192
190
|
await inquiryApi.create(
|
|
193
|
-
{
|
|
191
|
+
{
|
|
192
|
+
kind: this.kind,
|
|
193
|
+
title: this.title.trim(),
|
|
194
|
+
body: this.body,
|
|
195
|
+
visibility: this.privately ? 'PRIVATE' : 'DOMAIN',
|
|
196
|
+
pageUrl: this.pageUrl
|
|
197
|
+
},
|
|
194
198
|
this.files,
|
|
195
199
|
fraction => (this.progress = fraction)
|
|
196
200
|
)
|
|
197
201
|
inquiriesChanged()
|
|
198
202
|
notify({ message: t('inquiry.text.sent') })
|
|
199
203
|
/* The writer stays on the screen they were on - the answer comes as a notice and on the menu's badge. */
|
|
200
|
-
this.dispatchEvent(new CustomEvent('
|
|
204
|
+
this.dispatchEvent(new CustomEvent('close'))
|
|
201
205
|
} catch (error) {
|
|
202
206
|
notify({ level: 'error', message: reasonOf(error) })
|
|
203
207
|
} finally {
|
|
@@ -2,6 +2,8 @@ import '@material/web/icon/icon.js';
|
|
|
2
2
|
import '@material/web/button/filled-button.js';
|
|
3
3
|
import '@material/web/button/text-button.js';
|
|
4
4
|
import '@operato/input/ox-input-file.js';
|
|
5
|
+
import '@operato/input/ox-input-select-buttons.js';
|
|
6
|
+
import '@operato/popup/ox-popup-actions.js';
|
|
5
7
|
import { LitElement } from 'lit';
|
|
6
8
|
/** Opens the window, carrying the screen it is opened on. */
|
|
7
9
|
export declare function openInquiryWriter(kind?: string): any;
|
|
@@ -8,18 +8,20 @@ import '@material/web/icon/icon.js';
|
|
|
8
8
|
import '@material/web/button/filled-button.js';
|
|
9
9
|
import '@material/web/button/text-button.js';
|
|
10
10
|
import '@operato/input/ox-input-file.js';
|
|
11
|
+
import '@operato/input/ox-input-select-buttons.js';
|
|
12
|
+
import '@operato/popup/ox-popup-actions.js';
|
|
11
13
|
import { css, html, LitElement, nothing } from 'lit';
|
|
12
14
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
13
15
|
import { i18next, localize } from '@operato/i18n';
|
|
14
16
|
import { notify, openPopup } from '@operato/layout';
|
|
15
|
-
import {
|
|
17
|
+
import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles';
|
|
16
18
|
import { inquiriesChanged, inquiryApi } from '../utils/inquiry-api.js';
|
|
17
|
-
import {
|
|
19
|
+
import { KINDS, kindWord, reasonOf, t } from '../utils/inquiry-words.js';
|
|
18
20
|
const MAX_TITLE = 200;
|
|
19
21
|
/** Opens the window, carrying the screen it is opened on. */
|
|
20
22
|
export function openInquiryWriter(kind = 'QUESTION') {
|
|
21
23
|
const pageUrl = location.href;
|
|
22
|
-
const handle = openPopup(html `<inquiry-writer .kind=${kind} .pageUrl=${pageUrl} @
|
|
24
|
+
const handle = openPopup(html `<inquiry-writer .kind=${kind} .pageUrl=${pageUrl} @close=${() => handle.close()}></inquiry-writer>`, {
|
|
23
25
|
backdrop: true,
|
|
24
26
|
size: 'large',
|
|
25
27
|
title: t('inquiry.title.write')
|
|
@@ -37,66 +39,24 @@ let InquiryWriter = class InquiryWriter extends localize(i18next)(LitElement) {
|
|
|
37
39
|
this.files = [];
|
|
38
40
|
this.sending = false;
|
|
39
41
|
}
|
|
42
|
+
/* The house shape of a popup (page-standard sections 7, 8): only the body scrolls, fields are labels above native
|
|
43
|
+
inputs, the one button row is `ox-popup-actions`. */
|
|
40
44
|
static { this.styles = [
|
|
45
|
+
CommonPopupStyles,
|
|
46
|
+
PopupBodyStyles,
|
|
41
47
|
FormFieldStyles,
|
|
42
|
-
|
|
48
|
+
PopupFieldStyles,
|
|
43
49
|
css `
|
|
44
|
-
|
|
45
|
-
display: flex;
|
|
46
|
-
flex-direction: column;
|
|
47
|
-
gap: var(--spacing-medium, 8px);
|
|
48
|
-
padding: var(--spacing-large, 12px);
|
|
49
|
-
color: var(--md-sys-color-on-surface);
|
|
50
|
-
overflow: auto;
|
|
51
|
-
}
|
|
52
|
-
label {
|
|
53
|
-
display: flex;
|
|
54
|
-
flex-direction: column;
|
|
55
|
-
gap: 2px;
|
|
56
|
-
}
|
|
57
|
-
label.check {
|
|
58
|
-
flex-direction: row;
|
|
59
|
-
align-items: center;
|
|
60
|
-
gap: var(--spacing-small, 4px);
|
|
61
|
-
}
|
|
62
|
-
.kinds {
|
|
63
|
-
display: inline-flex;
|
|
64
|
-
gap: 4px;
|
|
65
|
-
padding: 3px;
|
|
66
|
-
border-radius: 10px;
|
|
67
|
-
background: var(--ui-panel-2, var(--md-sys-color-surface-container));
|
|
50
|
+
ox-input-select-buttons {
|
|
68
51
|
align-self: flex-start;
|
|
69
52
|
}
|
|
70
|
-
.kinds button {
|
|
71
|
-
display: inline-flex;
|
|
72
|
-
align-items: center;
|
|
73
|
-
gap: 4px;
|
|
74
|
-
padding: 4px 14px;
|
|
75
|
-
border: 0;
|
|
76
|
-
border-radius: 8px;
|
|
77
|
-
background: transparent;
|
|
78
|
-
box-shadow: none;
|
|
79
|
-
color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
|
|
80
|
-
font: inherit;
|
|
81
|
-
font-weight: 600;
|
|
82
|
-
cursor: pointer;
|
|
83
|
-
}
|
|
84
|
-
.kinds button[aria-pressed='true'] {
|
|
85
|
-
background: var(--ui-panel, var(--md-sys-color-surface-container-lowest));
|
|
86
|
-
color: var(--ui-ink, var(--md-sys-color-on-surface));
|
|
87
|
-
}
|
|
88
|
-
.kinds md-icon {
|
|
89
|
-
--md-icon-size: 16px;
|
|
90
|
-
}
|
|
91
53
|
textarea {
|
|
92
54
|
min-height: 160px;
|
|
93
|
-
|
|
94
|
-
box-sizing: border-box;
|
|
95
|
-
width: 100%;
|
|
55
|
+
resize: vertical;
|
|
96
56
|
}
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
|
|
57
|
+
.note {
|
|
58
|
+
color: var(--ui-muted);
|
|
59
|
+
font-size: var(--ui-t-small, 11.5px);
|
|
100
60
|
word-break: break-all;
|
|
101
61
|
}
|
|
102
62
|
.files {
|
|
@@ -104,15 +64,15 @@ let InquiryWriter = class InquiryWriter extends localize(i18next)(LitElement) {
|
|
|
104
64
|
flex-wrap: wrap;
|
|
105
65
|
gap: var(--spacing-small, 4px);
|
|
106
66
|
align-items: center;
|
|
67
|
+
color: var(--ui-ink);
|
|
107
68
|
}
|
|
108
69
|
.file {
|
|
109
70
|
display: inline-flex;
|
|
110
71
|
align-items: center;
|
|
111
72
|
gap: 2px;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
font-size: var(--ui-t-small, 12px);
|
|
73
|
+
}
|
|
74
|
+
.file md-icon {
|
|
75
|
+
--md-icon-size: 14px;
|
|
116
76
|
}
|
|
117
77
|
.file button {
|
|
118
78
|
border: 0;
|
|
@@ -120,49 +80,86 @@ let InquiryWriter = class InquiryWriter extends localize(i18next)(LitElement) {
|
|
|
120
80
|
box-shadow: none;
|
|
121
81
|
padding: 0;
|
|
122
82
|
cursor: pointer;
|
|
123
|
-
color:
|
|
124
|
-
}
|
|
125
|
-
.file md-icon {
|
|
126
|
-
--md-icon-size: 14px;
|
|
83
|
+
color: var(--ui-muted);
|
|
127
84
|
}
|
|
128
|
-
.
|
|
85
|
+
.check {
|
|
129
86
|
display: flex;
|
|
130
|
-
|
|
131
|
-
gap: var(--spacing-
|
|
87
|
+
align-items: center;
|
|
88
|
+
gap: var(--spacing-small, 4px);
|
|
89
|
+
color: var(--ui-ink);
|
|
90
|
+
}
|
|
91
|
+
/* The field rule stretches its inputs to the row; a checkbox keeps its own size. */
|
|
92
|
+
.field .check input {
|
|
93
|
+
width: auto;
|
|
94
|
+
margin: 0;
|
|
132
95
|
}
|
|
133
96
|
`
|
|
134
97
|
]; }
|
|
135
98
|
render() {
|
|
136
99
|
const title = this.title.trim();
|
|
137
100
|
return html `
|
|
138
|
-
<div class="
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
101
|
+
<div class="body">
|
|
102
|
+
<div class="field">
|
|
103
|
+
<ox-input-select-buttons
|
|
104
|
+
.value=${this.kind}
|
|
105
|
+
.options=${KINDS.map(code => ({ display: kindWord(code), value: code }))}
|
|
106
|
+
@change=${(e) => e.detail && (this.kind = e.detail)}
|
|
107
|
+
></ox-input-select-buttons>
|
|
108
|
+
<span>${t(`inquiry.text.kind-hint-${this.kind.toLowerCase()}`)}</span>
|
|
109
|
+
</div>
|
|
110
|
+
<label class="field">
|
|
111
|
+
<span
|
|
112
|
+
>${t('inquiry.field.title')} ·
|
|
113
|
+
${t('inquiry.text.title-limit', { count: this.title.length, max: MAX_TITLE })}</span
|
|
114
|
+
>
|
|
115
|
+
<input .value=${this.title} maxlength=${MAX_TITLE} @input=${(e) => (this.title = e.target.value)} />
|
|
116
|
+
</label>
|
|
117
|
+
<label class="field">
|
|
118
|
+
<span>${t('inquiry.field.body')}</span>
|
|
119
|
+
<textarea
|
|
120
|
+
.value=${this.body}
|
|
121
|
+
placeholder=${t(`inquiry.text.body-placeholder-${this.kind.toLowerCase()}`)}
|
|
122
|
+
@input=${(e) => (this.body = e.target.value)}
|
|
123
|
+
></textarea>
|
|
124
|
+
</label>
|
|
125
|
+
<div class="files">
|
|
126
|
+
${this.files.map((file, index) => html `<span class="file"
|
|
127
|
+
><md-icon>attach_file</md-icon>${file.name}<button
|
|
128
|
+
aria-label=${t('inquiry.button.remove')}
|
|
129
|
+
@click=${() => (this.files = this.files.filter((_, i) => i !== index))}
|
|
130
|
+
>
|
|
131
|
+
<md-icon>close</md-icon>
|
|
132
|
+
</button></span
|
|
133
|
+
>`)}
|
|
134
|
+
<ox-input-file
|
|
135
|
+
quiet
|
|
136
|
+
hide-filelist
|
|
137
|
+
multiple
|
|
138
|
+
icon="attach_file"
|
|
139
|
+
.progress=${this.progress}
|
|
140
|
+
@change=${(e) => this.pick(e)}
|
|
141
|
+
></ox-input-file>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="field">
|
|
144
|
+
<label class="check"
|
|
145
|
+
><input
|
|
146
|
+
type="checkbox"
|
|
147
|
+
.checked=${this.privately}
|
|
148
|
+
@change=${(e) => (this.privately = e.target.checked)}
|
|
149
|
+
/>${t('inquiry.text.private-label')}</label
|
|
150
|
+
>
|
|
151
|
+
<span>${this.privately ? t('inquiry.text.private-hint') : t('inquiry.text.domain-hint')}</span>
|
|
152
|
+
</div>
|
|
153
|
+
${this.pageUrl ? html `<div class="note">${t('inquiry.text.page-hint', { page: this.pageUrl })}</div>` : nothing}
|
|
165
154
|
</div>
|
|
155
|
+
<ox-popup-actions>
|
|
156
|
+
<md-text-button slot="cancel" @click=${() => this.dispatchEvent(new CustomEvent('close'))}
|
|
157
|
+
>${t('inquiry.button.cancel')}</md-text-button
|
|
158
|
+
>
|
|
159
|
+
<md-filled-button slot="confirm" ?disabled=${!title || this.sending} @click=${() => this.send()}
|
|
160
|
+
>${t('inquiry.button.submit')}</md-filled-button
|
|
161
|
+
>
|
|
162
|
+
</ox-popup-actions>
|
|
166
163
|
`;
|
|
167
164
|
}
|
|
168
165
|
pick(e) {
|
|
@@ -176,11 +173,17 @@ let InquiryWriter = class InquiryWriter extends localize(i18next)(LitElement) {
|
|
|
176
173
|
async send() {
|
|
177
174
|
this.sending = true;
|
|
178
175
|
try {
|
|
179
|
-
await inquiryApi.create({
|
|
176
|
+
await inquiryApi.create({
|
|
177
|
+
kind: this.kind,
|
|
178
|
+
title: this.title.trim(),
|
|
179
|
+
body: this.body,
|
|
180
|
+
visibility: this.privately ? 'PRIVATE' : 'DOMAIN',
|
|
181
|
+
pageUrl: this.pageUrl
|
|
182
|
+
}, this.files, fraction => (this.progress = fraction));
|
|
180
183
|
inquiriesChanged();
|
|
181
184
|
notify({ message: t('inquiry.text.sent') });
|
|
182
185
|
/* The writer stays on the screen they were on - the answer comes as a notice and on the menu's badge. */
|
|
183
|
-
this.dispatchEvent(new CustomEvent('
|
|
186
|
+
this.dispatchEvent(new CustomEvent('close'));
|
|
184
187
|
}
|
|
185
188
|
catch (error) {
|
|
186
189
|
notify({ level: 'error', message: reasonOf(error) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inquiry-writer.js","sourceRoot":"","sources":["../../client/components/inquiry-writer.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 문의 · 제안 보내기 (ADR-0184 decisions 1, 4, 5, 9) - the window the avatar menu opens on any screen: the kind, the title,\n * the words, files, and who reads it. The screen it was opened on goes with it; the application, its version, the\n * writer and the domain are the server's to take.\n */\nimport '@material/web/icon/icon.js'\nimport '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@operato/input/ox-input-file.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { notify, openPopup } from '@operato/layout'\nimport { ButtonStyles, FormFieldStyles } from '@operato/styles'\n\nimport { inquiriesChanged, inquiryApi } from '../utils/inquiry-api.js'\nimport { KIND_ICONS, KINDS, kindWord, reasonOf, t } from '../utils/inquiry-words.js'\n\nconst MAX_TITLE = 200\n\n/** Opens the window, carrying the screen it is opened on. */\nexport function openInquiryWriter(kind = 'QUESTION') {\n const pageUrl = location.href\n const handle: any = openPopup(html`<inquiry-writer .kind=${kind} .pageUrl=${pageUrl} @done=${() => handle.close()}></inquiry-writer>`, {\n backdrop: true,\n size: 'large',\n title: t('inquiry.title.write')\n })\n return handle\n}\n\n@customElement('inquiry-writer')\nexport class InquiryWriter extends localize(i18next)(LitElement) {\n static styles = [\n FormFieldStyles,\n ButtonStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-large, 12px);\n color: var(--md-sys-color-on-surface);\n overflow: auto;\n }\n label {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n label.check {\n flex-direction: row;\n align-items: center;\n gap: var(--spacing-small, 4px);\n }\n .kinds {\n display: inline-flex;\n gap: 4px;\n padding: 3px;\n border-radius: 10px;\n background: var(--ui-panel-2, var(--md-sys-color-surface-container));\n align-self: flex-start;\n }\n .kinds button {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 14px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n box-shadow: none;\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n }\n .kinds button[aria-pressed='true'] {\n background: var(--ui-panel, var(--md-sys-color-surface-container-lowest));\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n }\n .kinds md-icon {\n --md-icon-size: 16px;\n }\n textarea {\n min-height: 160px;\n font-family: inherit;\n box-sizing: border-box;\n width: 100%;\n }\n .hint {\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n word-break: break-all;\n }\n .files {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-small, 4px);\n align-items: center;\n }\n .file {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n padding: 0 var(--spacing-small, 4px);\n border: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n border-radius: var(--border-radius, 4px);\n font-size: var(--ui-t-small, 12px);\n }\n .file button {\n border: 0;\n background: transparent;\n box-shadow: none;\n padding: 0;\n cursor: pointer;\n color: inherit;\n }\n .file md-icon {\n --md-icon-size: 14px;\n }\n .buttons {\n display: flex;\n justify-content: flex-end;\n gap: var(--spacing-medium, 8px);\n }\n `\n ]\n\n @property() kind = 'QUESTION'\n @property() pageUrl = ''\n @state() title = ''\n @state() body = ''\n @state() privately = false\n @state() files: File[] = []\n @state() sending = false\n /* Set only while files go up - the picker shows its bar then. */\n @state() progress?: number\n\n render() {\n const title = this.title.trim()\n return html`\n <div class=\"kinds\" role=\"group\" aria-label=${t('inquiry.field.kind')}>\n ${KINDS.map(\n code => html`<button aria-pressed=${String(this.kind === code)} @click=${() => (this.kind = code)}><md-icon>${KIND_ICONS[code]}</md-icon>${kindWord(code)}</button>`\n )}\n </div>\n <span class=\"hint\">${t(`inquiry.text.kind-hint-${this.kind.toLowerCase()}`)}</span>\n <label\n >${t('inquiry.field.title')}\n <input .value=${this.title} maxlength=${MAX_TITLE} @input=${(e: any) => (this.title = e.target.value)} />\n <span class=\"hint\">${t('inquiry.text.title-limit', { count: this.title.length, max: MAX_TITLE })}</span>\n </label>\n <label\n >${t('inquiry.field.body')}\n <textarea .value=${this.body} placeholder=${t(`inquiry.text.body-placeholder-${this.kind.toLowerCase()}`)} @input=${(e: any) => (this.body = e.target.value)}></textarea>\n </label>\n <div class=\"files\">\n ${this.files.map(\n (file, index) =>\n html`<span class=\"file\"\n ><md-icon>attach_file</md-icon>${file.name}<button aria-label=${t('inquiry.button.remove')} @click=${() => (this.files = this.files.filter((_, i) => i !== index))}>\n <md-icon>close</md-icon>\n </button></span\n >`\n )}\n <ox-input-file quiet hide-filelist multiple icon=\"attach_file\" .progress=${this.progress} @change=${(e: CustomEvent) => this.pick(e)}></ox-input-file>\n </div>\n <label class=\"check\"><input type=\"checkbox\" .checked=${this.privately} @change=${(e: any) => (this.privately = e.target.checked)} />${t('inquiry.text.private-label')}</label>\n <span class=\"hint\">${this.privately ? t('inquiry.text.private-hint') : t('inquiry.text.domain-hint')}</span>\n ${this.pageUrl ? html`<span class=\"hint\">${t('inquiry.text.page-hint', { page: this.pageUrl })}</span>` : nothing}\n <div class=\"buttons\">\n <md-text-button @click=${() => this.dispatchEvent(new CustomEvent('done'))}>${t('inquiry.button.cancel')}</md-text-button>\n <md-filled-button ?disabled=${!title || this.sending} @click=${() => this.send()}>${t('inquiry.button.submit')}</md-filled-button>\n </div>\n `\n }\n\n private pick(e: CustomEvent) {\n const picked = [...((e.detail as File[]) ?? [])]\n /* The picker's own reset says `change` again, with nothing - that one is let go. */\n if (!picked.length) return\n ;(e.target as any).reset?.()\n this.files = [...this.files, ...picked]\n }\n\n private async send() {\n this.sending = true\n try {\n await inquiryApi.create(\n { kind: this.kind, title: this.title.trim(), body: this.body, visibility: this.privately ? 'PRIVATE' : 'DOMAIN', pageUrl: this.pageUrl },\n this.files,\n fraction => (this.progress = fraction)\n )\n inquiriesChanged()\n notify({ message: t('inquiry.text.sent') })\n /* The writer stays on the screen they were on - the answer comes as a notice and on the menu's badge. */\n this.dispatchEvent(new CustomEvent('done'))\n } catch (error) {\n notify({ level: 'error', message: reasonOf(error) })\n } finally {\n this.sending = false\n this.progress = undefined\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"inquiry-writer.js","sourceRoot":"","sources":["../../client/components/inquiry-writer.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 문의 · 제안 보내기 (ADR-0184 decisions 1, 4, 5, 9) - the window the avatar menu opens on any screen: the kind, the title,\n * the words, files, and who reads it. The screen it was opened on goes with it; the application, its version, the\n * writer and the domain are the server's to take.\n */\nimport '@material/web/icon/icon.js'\nimport '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@operato/input/ox-input-file.js'\nimport '@operato/input/ox-input-select-buttons.js'\nimport '@operato/popup/ox-popup-actions.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { notify, openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\n\nimport { inquiriesChanged, inquiryApi } from '../utils/inquiry-api.js'\nimport { KINDS, kindWord, reasonOf, t } from '../utils/inquiry-words.js'\n\nconst MAX_TITLE = 200\n\n/** Opens the window, carrying the screen it is opened on. */\nexport function openInquiryWriter(kind = 'QUESTION') {\n const pageUrl = location.href\n const handle: any = openPopup(\n html`<inquiry-writer .kind=${kind} .pageUrl=${pageUrl} @close=${() => handle.close()}></inquiry-writer>`,\n {\n backdrop: true,\n size: 'large',\n title: t('inquiry.title.write')\n }\n )\n return handle\n}\n\n@customElement('inquiry-writer')\nexport class InquiryWriter extends localize(i18next)(LitElement) {\n /* The house shape of a popup (page-standard sections 7, 8): only the body scrolls, fields are labels above native\n inputs, the one button row is `ox-popup-actions`. */\n static styles = [\n CommonPopupStyles,\n PopupBodyStyles,\n FormFieldStyles,\n PopupFieldStyles,\n css`\n ox-input-select-buttons {\n align-self: flex-start;\n }\n textarea {\n min-height: 160px;\n resize: vertical;\n }\n .note {\n color: var(--ui-muted);\n font-size: var(--ui-t-small, 11.5px);\n word-break: break-all;\n }\n .files {\n display: flex;\n flex-wrap: wrap;\n gap: var(--spacing-small, 4px);\n align-items: center;\n color: var(--ui-ink);\n }\n .file {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n }\n .file md-icon {\n --md-icon-size: 14px;\n }\n .file button {\n border: 0;\n background: transparent;\n box-shadow: none;\n padding: 0;\n cursor: pointer;\n color: var(--ui-muted);\n }\n .check {\n display: flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n color: var(--ui-ink);\n }\n /* The field rule stretches its inputs to the row; a checkbox keeps its own size. */\n .field .check input {\n width: auto;\n margin: 0;\n }\n `\n ]\n\n @property() kind = 'QUESTION'\n @property() pageUrl = ''\n @state() title = ''\n @state() body = ''\n @state() privately = false\n @state() files: File[] = []\n @state() sending = false\n /* Set only while files go up - the picker shows its bar then. */\n @state() progress?: number\n\n render() {\n const title = this.title.trim()\n return html`\n <div class=\"body\">\n <div class=\"field\">\n <ox-input-select-buttons\n .value=${this.kind}\n .options=${KINDS.map(code => ({ display: kindWord(code), value: code }))}\n @change=${(e: CustomEvent) => e.detail && (this.kind = e.detail)}\n ></ox-input-select-buttons>\n <span>${t(`inquiry.text.kind-hint-${this.kind.toLowerCase()}`)}</span>\n </div>\n <label class=\"field\">\n <span\n >${t('inquiry.field.title')} ·\n ${t('inquiry.text.title-limit', { count: this.title.length, max: MAX_TITLE })}</span\n >\n <input .value=${this.title} maxlength=${MAX_TITLE} @input=${(e: any) => (this.title = e.target.value)} />\n </label>\n <label class=\"field\">\n <span>${t('inquiry.field.body')}</span>\n <textarea\n .value=${this.body}\n placeholder=${t(`inquiry.text.body-placeholder-${this.kind.toLowerCase()}`)}\n @input=${(e: any) => (this.body = e.target.value)}\n ></textarea>\n </label>\n <div class=\"files\">\n ${this.files.map(\n (file, index) =>\n html`<span class=\"file\"\n ><md-icon>attach_file</md-icon>${file.name}<button\n aria-label=${t('inquiry.button.remove')}\n @click=${() => (this.files = this.files.filter((_, i) => i !== index))}\n >\n <md-icon>close</md-icon>\n </button></span\n >`\n )}\n <ox-input-file\n quiet\n hide-filelist\n multiple\n icon=\"attach_file\"\n .progress=${this.progress}\n @change=${(e: CustomEvent) => this.pick(e)}\n ></ox-input-file>\n </div>\n <div class=\"field\">\n <label class=\"check\"\n ><input\n type=\"checkbox\"\n .checked=${this.privately}\n @change=${(e: any) => (this.privately = e.target.checked)}\n />${t('inquiry.text.private-label')}</label\n >\n <span>${this.privately ? t('inquiry.text.private-hint') : t('inquiry.text.domain-hint')}</span>\n </div>\n ${this.pageUrl ? html`<div class=\"note\">${t('inquiry.text.page-hint', { page: this.pageUrl })}</div>` : nothing}\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.dispatchEvent(new CustomEvent('close'))}\n >${t('inquiry.button.cancel')}</md-text-button\n >\n <md-filled-button slot=\"confirm\" ?disabled=${!title || this.sending} @click=${() => this.send()}\n >${t('inquiry.button.submit')}</md-filled-button\n >\n </ox-popup-actions>\n `\n }\n\n private pick(e: CustomEvent) {\n const picked = [...((e.detail as File[]) ?? [])]\n /* The picker's own reset says `change` again, with nothing - that one is let go. */\n if (!picked.length) return\n ;(e.target as any).reset?.()\n this.files = [...this.files, ...picked]\n }\n\n private async send() {\n this.sending = true\n try {\n await inquiryApi.create(\n {\n kind: this.kind,\n title: this.title.trim(),\n body: this.body,\n visibility: this.privately ? 'PRIVATE' : 'DOMAIN',\n pageUrl: this.pageUrl\n },\n this.files,\n fraction => (this.progress = fraction)\n )\n inquiriesChanged()\n notify({ message: t('inquiry.text.sent') })\n /* The writer stays on the screen they were on - the answer comes as a notice and on the menu's badge. */\n this.dispatchEvent(new CustomEvent('close'))\n } catch (error) {\n notify({ level: 'error', message: reasonOf(error) })\n } finally {\n this.sending = false\n this.progress = undefined\n }\n }\n}\n"]}
|