@things-factory/learning 0.0.0-stage → 10.1.261

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 (167) hide show
  1. package/dist-client/bootstrap.d.ts +2 -0
  2. package/dist-client/bootstrap.js +4 -0
  3. package/dist-client/bootstrap.js.map +1 -0
  4. package/dist-client/components/learning-assign-form.d.ts +24 -0
  5. package/dist-client/components/learning-assign-form.js +77 -0
  6. package/dist-client/components/learning-assign-form.js.map +1 -0
  7. package/dist-client/components/learning-course-form.d.ts +29 -0
  8. package/dist-client/components/learning-course-form.js +104 -0
  9. package/dist-client/components/learning-course-form.js.map +1 -0
  10. package/dist-client/components/learning-material-form.d.ts +41 -0
  11. package/dist-client/components/learning-material-form.js +202 -0
  12. package/dist-client/components/learning-material-form.js.map +1 -0
  13. package/dist-client/components/learning-question-form.d.ts +33 -0
  14. package/dist-client/components/learning-question-form.js +142 -0
  15. package/dist-client/components/learning-question-form.js.map +1 -0
  16. package/dist-client/components/learning-todo.d.ts +12 -0
  17. package/dist-client/components/learning-todo.js +43 -0
  18. package/dist-client/components/learning-todo.js.map +1 -0
  19. package/dist-client/components/learning-unit-form.d.ts +44 -0
  20. package/dist-client/components/learning-unit-form.js +151 -0
  21. package/dist-client/components/learning-unit-form.js.map +1 -0
  22. package/dist-client/index.d.ts +2 -0
  23. package/dist-client/index.js +3 -0
  24. package/dist-client/index.js.map +1 -0
  25. package/dist-client/pages/learning-course-editor.d.ts +63 -0
  26. package/dist-client/pages/learning-course-editor.js +476 -0
  27. package/dist-client/pages/learning-course-editor.js.map +1 -0
  28. package/dist-client/pages/learning-course-list.d.ts +37 -0
  29. package/dist-client/pages/learning-course-list.js +92 -0
  30. package/dist-client/pages/learning-course-list.js.map +1 -0
  31. package/dist-client/pages/learning-enrollment-status.d.ts +65 -0
  32. package/dist-client/pages/learning-enrollment-status.js +211 -0
  33. package/dist-client/pages/learning-enrollment-status.js.map +1 -0
  34. package/dist-client/pages/learning-view.d.ts +63 -0
  35. package/dist-client/pages/learning-view.js +446 -0
  36. package/dist-client/pages/learning-view.js.map +1 -0
  37. package/dist-client/pages/my-learning.d.ts +22 -0
  38. package/dist-client/pages/my-learning.js +110 -0
  39. package/dist-client/pages/my-learning.js.map +1 -0
  40. package/dist-client/route.d.ts +1 -0
  41. package/dist-client/route.js +21 -0
  42. package/dist-client/route.js.map +1 -0
  43. package/dist-client/tsconfig.tsbuildinfo +1 -0
  44. package/dist-client/utils/learning-api.d.ts +61 -0
  45. package/dist-client/utils/learning-api.js +85 -0
  46. package/dist-client/utils/learning-api.js.map +1 -0
  47. package/dist-client/utils/learning-words.d.ts +20 -0
  48. package/dist-client/utils/learning-words.js +22 -0
  49. package/dist-client/utils/learning-words.js.map +1 -0
  50. package/dist-client/utils/material-source.d.ts +12 -0
  51. package/dist-client/utils/material-source.js +77 -0
  52. package/dist-client/utils/material-source.js.map +1 -0
  53. package/dist-server/activities.d.ts +38 -0
  54. package/dist-server/activities.js +42 -0
  55. package/dist-server/activities.js.map +1 -0
  56. package/dist-server/ai-reads.d.ts +9 -0
  57. package/dist-server/ai-reads.js +70 -0
  58. package/dist-server/ai-reads.js.map +1 -0
  59. package/dist-server/attachment-reader.d.ts +1 -0
  60. package/dist-server/attachment-reader.js +23 -0
  61. package/dist-server/attachment-reader.js.map +1 -0
  62. package/dist-server/index.d.ts +4 -0
  63. package/dist-server/index.js +33 -0
  64. package/dist-server/index.js.map +1 -0
  65. package/dist-server/learning-subjects.d.ts +10 -0
  66. package/dist-server/learning-subjects.js +31 -0
  67. package/dist-server/learning-subjects.js.map +1 -0
  68. package/dist-server/role-tasks.d.ts +2 -0
  69. package/dist-server/role-tasks.js +17 -0
  70. package/dist-server/role-tasks.js.map +1 -0
  71. package/dist-server/service/course/course-mutation.d.ts +19 -0
  72. package/dist-server/service/course/course-mutation.js +177 -0
  73. package/dist-server/service/course/course-mutation.js.map +1 -0
  74. package/dist-server/service/course/course-query.d.ts +27 -0
  75. package/dist-server/service/course/course-query.js +183 -0
  76. package/dist-server/service/course/course-query.js.map +1 -0
  77. package/dist-server/service/course/course-question.d.ts +41 -0
  78. package/dist-server/service/course/course-question.js +116 -0
  79. package/dist-server/service/course/course-question.js.map +1 -0
  80. package/dist-server/service/course/course-rules.d.ts +51 -0
  81. package/dist-server/service/course/course-rules.js +93 -0
  82. package/dist-server/service/course/course-rules.js.map +1 -0
  83. package/dist-server/service/course/course-service.d.ts +72 -0
  84. package/dist-server/service/course/course-service.js +398 -0
  85. package/dist-server/service/course/course-service.js.map +1 -0
  86. package/dist-server/service/course/course-type.d.ts +56 -0
  87. package/dist-server/service/course/course-type.js +202 -0
  88. package/dist-server/service/course/course-type.js.map +1 -0
  89. package/dist-server/service/course/course-unit.d.ts +37 -0
  90. package/dist-server/service/course/course-unit.js +128 -0
  91. package/dist-server/service/course/course-unit.js.map +1 -0
  92. package/dist-server/service/course/course-version.d.ts +45 -0
  93. package/dist-server/service/course/course-version.js +151 -0
  94. package/dist-server/service/course/course-version.js.map +1 -0
  95. package/dist-server/service/course/course.d.ts +23 -0
  96. package/dist-server/service/course/course.js +92 -0
  97. package/dist-server/service/course/course.js.map +1 -0
  98. package/dist-server/service/course/index.d.ts +15 -0
  99. package/dist-server/service/course/index.js +25 -0
  100. package/dist-server/service/course/index.js.map +1 -0
  101. package/dist-server/service/course/material-audio.d.ts +1 -0
  102. package/dist-server/service/course/material-audio.js +43 -0
  103. package/dist-server/service/course/material-audio.js.map +1 -0
  104. package/dist-server/service/course/material-draft-mutation.d.ts +13 -0
  105. package/dist-server/service/course/material-draft-mutation.js +55 -0
  106. package/dist-server/service/course/material-draft-mutation.js.map +1 -0
  107. package/dist-server/service/course/material-draft-rules.d.ts +38 -0
  108. package/dist-server/service/course/material-draft-rules.js +83 -0
  109. package/dist-server/service/course/material-draft-rules.js.map +1 -0
  110. package/dist-server/service/course/material-draft-service.d.ts +15 -0
  111. package/dist-server/service/course/material-draft-service.js +162 -0
  112. package/dist-server/service/course/material-draft-service.js.map +1 -0
  113. package/dist-server/service/enrollment/enrollment-mutation.d.ts +11 -0
  114. package/dist-server/service/enrollment/enrollment-mutation.js +124 -0
  115. package/dist-server/service/enrollment/enrollment-mutation.js.map +1 -0
  116. package/dist-server/service/enrollment/enrollment-query.d.ts +24 -0
  117. package/dist-server/service/enrollment/enrollment-query.js +236 -0
  118. package/dist-server/service/enrollment/enrollment-query.js.map +1 -0
  119. package/dist-server/service/enrollment/enrollment-remind.d.ts +1 -0
  120. package/dist-server/service/enrollment/enrollment-remind.js +45 -0
  121. package/dist-server/service/enrollment/enrollment-remind.js.map +1 -0
  122. package/dist-server/service/enrollment/enrollment-service.d.ts +110 -0
  123. package/dist-server/service/enrollment/enrollment-service.js +427 -0
  124. package/dist-server/service/enrollment/enrollment-service.js.map +1 -0
  125. package/dist-server/service/enrollment/enrollment-status.d.ts +53 -0
  126. package/dist-server/service/enrollment/enrollment-status.js +194 -0
  127. package/dist-server/service/enrollment/enrollment-status.js.map +1 -0
  128. package/dist-server/service/enrollment/enrollment-type.d.ts +82 -0
  129. package/dist-server/service/enrollment/enrollment-type.js +293 -0
  130. package/dist-server/service/enrollment/enrollment-type.js.map +1 -0
  131. package/dist-server/service/enrollment/enrollment.d.ts +53 -0
  132. package/dist-server/service/enrollment/enrollment.js +191 -0
  133. package/dist-server/service/enrollment/enrollment.js.map +1 -0
  134. package/dist-server/service/enrollment/index.d.ts +15 -0
  135. package/dist-server/service/enrollment/index.js +26 -0
  136. package/dist-server/service/enrollment/index.js.map +1 -0
  137. package/dist-server/service/enrollment/learning-ai.d.ts +45 -0
  138. package/dist-server/service/enrollment/learning-ai.js +93 -0
  139. package/dist-server/service/enrollment/learning-ai.js.map +1 -0
  140. package/dist-server/service/enrollment/learning-documents.d.ts +7 -0
  141. package/dist-server/service/enrollment/learning-documents.js +159 -0
  142. package/dist-server/service/enrollment/learning-documents.js.map +1 -0
  143. package/dist-server/service/enrollment/learning-outcome.d.ts +10 -0
  144. package/dist-server/service/enrollment/learning-outcome.js +16 -0
  145. package/dist-server/service/enrollment/learning-outcome.js.map +1 -0
  146. package/dist-server/service/enrollment/progress-rules.d.ts +119 -0
  147. package/dist-server/service/enrollment/progress-rules.js +132 -0
  148. package/dist-server/service/enrollment/progress-rules.js.map +1 -0
  149. package/dist-server/service/enrollment/quiz-attempt.d.ts +34 -0
  150. package/dist-server/service/enrollment/quiz-attempt.js +105 -0
  151. package/dist-server/service/enrollment/quiz-attempt.js.map +1 -0
  152. package/dist-server/service/enrollment/unit-progress.d.ts +34 -0
  153. package/dist-server/service/enrollment/unit-progress.js +134 -0
  154. package/dist-server/service/enrollment/unit-progress.js.map +1 -0
  155. package/dist-server/service/index.d.ts +14 -0
  156. package/dist-server/service/index.js +43 -0
  157. package/dist-server/service/index.js.map +1 -0
  158. package/dist-server/tsconfig.tsbuildinfo +1 -0
  159. package/package.json +57 -4
  160. package/things-factory.config.js +14 -0
  161. package/translations/en.json +278 -0
  162. package/translations/es.json +278 -0
  163. package/translations/ja.json +278 -0
  164. package/translations/ko.json +278 -0
  165. package/translations/ms.json +278 -0
  166. package/translations/zh.json +278 -0
  167. package/README.md +0 -3
@@ -0,0 +1,142 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/button/filled-button.js';
3
+ import '@material/web/button/text-button.js';
4
+ import '@material/web/icon/icon.js';
5
+ import '@operato/input/ox-input-select-buttons.js';
6
+ import '@operato/popup/ox-popup-actions.js';
7
+ import { css, html, LitElement } from 'lit';
8
+ import { customElement, property, state } from 'lit/decorators.js';
9
+ import { i18next, localize } from '@operato/i18n';
10
+ import { openPopup } from '@operato/layout';
11
+ import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles';
12
+ import { QUESTION_KINDS, questionKindWord, t } from '../utils/learning-words.js';
13
+ /** Opens the question form. Resolves with the question as given, or null. */
14
+ export function openQuestionForm(question) {
15
+ const popup = openPopup(html `<learning-question-form .question=${question} @done=${(e) => popup.close(e.detail)} @close=${() => popup.close()}></learning-question-form>`, { backdrop: true, size: 'large', title: question.id ? t('title.learning-question-edit') : t('title.learning-question-new') });
16
+ return popup.result.then(value => (value ?? null));
17
+ }
18
+ /* 문항 (ADR-0194 decision 4) - how it is answered, what it asks, its choices with which are right, and why. */
19
+ let LearningQuestionForm = class LearningQuestionForm extends localize(i18next)(LitElement) {
20
+ constructor() {
21
+ super(...arguments);
22
+ this.question = { kind: 'SINGLE', text: '', choices: [] };
23
+ this.draft = { kind: 'SINGLE', text: '', choices: [] };
24
+ }
25
+ static { this.styles = [
26
+ CommonPopupStyles,
27
+ PopupBodyStyles,
28
+ FormFieldStyles,
29
+ PopupFieldStyles,
30
+ css `
31
+ ox-input-select-buttons {
32
+ align-self: flex-start;
33
+ }
34
+ textarea {
35
+ min-height: 80px;
36
+ resize: vertical;
37
+ }
38
+ .choice {
39
+ display: flex;
40
+ align-items: center;
41
+ gap: var(--spacing-small, 4px);
42
+ }
43
+ .choice input[type='text'] {
44
+ flex: 1;
45
+ }
46
+ .field .choice input[type='checkbox'],
47
+ .field .choice input[type='radio'] {
48
+ width: auto;
49
+ margin: 0;
50
+ }
51
+ .choice button {
52
+ border: 0;
53
+ background: transparent;
54
+ cursor: pointer;
55
+ color: var(--ui-muted);
56
+ padding: 4px;
57
+ }
58
+ `
59
+ ]; }
60
+ willUpdate(changed) {
61
+ if (changed.has('question')) {
62
+ const choices = this.question.choices?.length ? this.question.choices.map(one => ({ ...one })) : [{ text: '', correct: true }, { text: '', correct: false }];
63
+ this.draft = { ...this.question, choices };
64
+ }
65
+ }
66
+ render() {
67
+ const multiple = this.draft.kind === 'MULTIPLE';
68
+ const right = this.draft.choices.filter(one => one.correct).length;
69
+ const ready = this.draft.text.trim() && this.draft.choices.length >= 2 && this.draft.choices.every(one => one.text.trim()) && right >= 1 && (multiple || right === 1);
70
+ return html `
71
+ <div class="body">
72
+ <div class="field">
73
+ <span>${t('field.learning-question-kind')}</span>
74
+ <ox-input-select-buttons .value=${this.draft.kind} .options=${QUESTION_KINDS.map(code => ({ display: questionKindWord(code), value: code }))} @change=${(e) => e.detail && this.kind(e.detail)}></ox-input-select-buttons>
75
+ </div>
76
+ <label class="field">
77
+ <span>${t('field.learning-question-text')}</span>
78
+ <textarea .value=${this.draft.text} @input=${(e) => (this.draft = { ...this.draft, text: e.target.value })}></textarea>
79
+ </label>
80
+ <fieldset class="field" style="border:0;padding:0;margin:0">
81
+ <legend><span>${t('field.learning-question-choices')}</span></legend>
82
+ ${this.draft.choices.map((choice, index) => html `<div class="choice">
83
+ <input
84
+ type=${multiple ? 'checkbox' : 'radio'}
85
+ name="correct"
86
+ aria-label=${t('label.learning-choice-correct', { number: index + 1 })}
87
+ .checked=${choice.correct}
88
+ @change=${(e) => this.mark(index, e.target.checked)}
89
+ />
90
+ <input type="text" aria-label=${t('label.learning-choice-text', { number: index + 1 })} .value=${choice.text} ?readonly=${this.draft.kind === 'TRUE_FALSE'} @input=${(e) => this.write(index, e.target.value)} />
91
+ ${this.draft.kind !== 'TRUE_FALSE' && this.draft.choices.length > 2
92
+ ? html `<button aria-label=${t('button.learning-choice-remove')} @click=${() => this.removeChoice(index)}><md-icon>close</md-icon></button>`
93
+ : ''}
94
+ </div>`)}
95
+ ${this.draft.kind !== 'TRUE_FALSE'
96
+ ? html `<md-text-button style="align-self:flex-start" @click=${() => (this.draft = { ...this.draft, choices: [...this.draft.choices, { text: '', correct: false }] })}>${t('button.learning-choice-add')}</md-text-button>`
97
+ : ''}
98
+ <span>${multiple ? t('text.learning-question-multiple-hint') : t('text.learning-question-single-hint')}</span>
99
+ </fieldset>
100
+ <label class="field">
101
+ <span>${t('field.learning-question-explanation')}</span>
102
+ <textarea .value=${this.draft.explanation ?? ''} @input=${(e) => (this.draft = { ...this.draft, explanation: e.target.value })}></textarea>
103
+ </label>
104
+ </div>
105
+ <ox-popup-actions>
106
+ <md-text-button slot="cancel" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>
107
+ <md-filled-button slot="confirm" ?disabled=${!ready} @click=${() => this.dispatchEvent(new CustomEvent('done', { detail: this.draft }))}>${t('button.confirm')}</md-filled-button>
108
+ </ox-popup-actions>
109
+ `;
110
+ }
111
+ kind(kind) {
112
+ if (kind === 'TRUE_FALSE')
113
+ this.draft = { ...this.draft, kind, choices: [{ text: t('text.quiz-true'), correct: true }, { text: t('text.quiz-false'), correct: false }] };
114
+ else if (kind === 'SINGLE' && this.draft.choices.filter(one => one.correct).length > 1)
115
+ this.draft = { ...this.draft, kind, choices: this.draft.choices.map((one, i) => ({ ...one, correct: i === this.draft.choices.findIndex(c => c.correct) })) };
116
+ else
117
+ this.draft = { ...this.draft, kind };
118
+ }
119
+ mark(index, checked) {
120
+ const multiple = this.draft.kind === 'MULTIPLE';
121
+ this.draft = { ...this.draft, choices: this.draft.choices.map((one, i) => ({ ...one, correct: i === index ? checked : multiple ? one.correct : false })) };
122
+ }
123
+ write(index, text) {
124
+ this.draft = { ...this.draft, choices: this.draft.choices.map((one, i) => (i === index ? { ...one, text } : one)) };
125
+ }
126
+ removeChoice(index) {
127
+ this.draft = { ...this.draft, choices: this.draft.choices.filter((_, i) => i !== index) };
128
+ }
129
+ };
130
+ __decorate([
131
+ property({ attribute: false }),
132
+ __metadata("design:type", Object)
133
+ ], LearningQuestionForm.prototype, "question", void 0);
134
+ __decorate([
135
+ state(),
136
+ __metadata("design:type", Object)
137
+ ], LearningQuestionForm.prototype, "draft", void 0);
138
+ LearningQuestionForm = __decorate([
139
+ customElement('learning-question-form')
140
+ ], LearningQuestionForm);
141
+ export { LearningQuestionForm };
142
+ //# sourceMappingURL=learning-question-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learning-question-form.js","sourceRoot":"","sources":["../../client/components/learning-question-form.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/button/text-button.js'\nimport '@material/web/icon/icon.js'\nimport '@operato/input/ox-input-select-buttons.js'\nimport '@operato/popup/ox-popup-actions.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\n\nimport { QUESTION_KINDS, questionKindWord, t } from '../utils/learning-words.js'\n\nexport type QuestionDraft = { id?: string; kind: string; text: string; explanation?: string | null; drafted?: boolean; choices: { id?: string; text: string; correct: boolean }[] }\n\n/** Opens the question form. Resolves with the question as given, or null. */\nexport function openQuestionForm(question: QuestionDraft): Promise<QuestionDraft | null> {\n const popup = openPopup(html`<learning-question-form .question=${question} @done=${(e: CustomEvent) => popup.close(e.detail)} @close=${() => popup.close()}></learning-question-form>`, { backdrop: true, size: 'large', title: question.id ? t('title.learning-question-edit') : t('title.learning-question-new') })\n return popup.result.then(value => (value ?? null) as any)\n}\n\n/* 문항 (ADR-0194 decision 4) - how it is answered, what it asks, its choices with which are right, and why. */\n@customElement('learning-question-form')\nexport class LearningQuestionForm extends localize(i18next)(LitElement) {\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: 80px;\n resize: vertical;\n }\n .choice {\n display: flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n }\n .choice input[type='text'] {\n flex: 1;\n }\n .field .choice input[type='checkbox'],\n .field .choice input[type='radio'] {\n width: auto;\n margin: 0;\n }\n .choice button {\n border: 0;\n background: transparent;\n cursor: pointer;\n color: var(--ui-muted);\n padding: 4px;\n }\n `\n ]\n\n @property({ attribute: false }) question: QuestionDraft = { kind: 'SINGLE', text: '', choices: [] }\n @state() private draft: QuestionDraft = { kind: 'SINGLE', text: '', choices: [] }\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('question')) {\n const choices = this.question.choices?.length ? this.question.choices.map(one => ({ ...one })) : [{ text: '', correct: true }, { text: '', correct: false }]\n this.draft = { ...this.question, choices }\n }\n }\n\n render() {\n const multiple = this.draft.kind === 'MULTIPLE'\n const right = this.draft.choices.filter(one => one.correct).length\n const ready = this.draft.text.trim() && this.draft.choices.length >= 2 && this.draft.choices.every(one => one.text.trim()) && right >= 1 && (multiple || right === 1)\n return html`\n <div class=\"body\">\n <div class=\"field\">\n <span>${t('field.learning-question-kind')}</span>\n <ox-input-select-buttons .value=${this.draft.kind} .options=${QUESTION_KINDS.map(code => ({ display: questionKindWord(code), value: code }))} @change=${(e: CustomEvent) => e.detail && this.kind(e.detail)}></ox-input-select-buttons>\n </div>\n <label class=\"field\">\n <span>${t('field.learning-question-text')}</span>\n <textarea .value=${this.draft.text} @input=${(e: any) => (this.draft = { ...this.draft, text: e.target.value })}></textarea>\n </label>\n <fieldset class=\"field\" style=\"border:0;padding:0;margin:0\">\n <legend><span>${t('field.learning-question-choices')}</span></legend>\n ${this.draft.choices.map(\n (choice, index) => html`<div class=\"choice\">\n <input\n type=${multiple ? 'checkbox' : 'radio'}\n name=\"correct\"\n aria-label=${t('label.learning-choice-correct', { number: index + 1 })}\n .checked=${choice.correct}\n @change=${(e: any) => this.mark(index, e.target.checked)}\n />\n <input type=\"text\" aria-label=${t('label.learning-choice-text', { number: index + 1 })} .value=${choice.text} ?readonly=${this.draft.kind === 'TRUE_FALSE'} @input=${(e: any) => this.write(index, e.target.value)} />\n ${this.draft.kind !== 'TRUE_FALSE' && this.draft.choices.length > 2\n ? html`<button aria-label=${t('button.learning-choice-remove')} @click=${() => this.removeChoice(index)}><md-icon>close</md-icon></button>`\n : ''}\n </div>`\n )}\n ${this.draft.kind !== 'TRUE_FALSE'\n ? html`<md-text-button style=\"align-self:flex-start\" @click=${() => (this.draft = { ...this.draft, choices: [...this.draft.choices, { text: '', correct: false }] })}>${t('button.learning-choice-add')}</md-text-button>`\n : ''}\n <span>${multiple ? t('text.learning-question-multiple-hint') : t('text.learning-question-single-hint')}</span>\n </fieldset>\n <label class=\"field\">\n <span>${t('field.learning-question-explanation')}</span>\n <textarea .value=${this.draft.explanation ?? ''} @input=${(e: any) => (this.draft = { ...this.draft, explanation: e.target.value })}></textarea>\n </label>\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${!ready} @click=${() => this.dispatchEvent(new CustomEvent('done', { detail: this.draft }))}>${t('button.confirm')}</md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private kind(kind: string) {\n if (kind === 'TRUE_FALSE')\n this.draft = { ...this.draft, kind, choices: [{ text: t('text.quiz-true'), correct: true }, { text: t('text.quiz-false'), correct: false }] }\n else if (kind === 'SINGLE' && this.draft.choices.filter(one => one.correct).length > 1)\n this.draft = { ...this.draft, kind, choices: this.draft.choices.map((one, i) => ({ ...one, correct: i === this.draft.choices.findIndex(c => c.correct) })) }\n else this.draft = { ...this.draft, kind }\n }\n\n private mark(index: number, checked: boolean) {\n const multiple = this.draft.kind === 'MULTIPLE'\n this.draft = { ...this.draft, choices: this.draft.choices.map((one, i) => ({ ...one, correct: i === index ? checked : multiple ? one.correct : false })) }\n }\n\n private write(index: number, text: string) {\n this.draft = { ...this.draft, choices: this.draft.choices.map((one, i) => (i === index ? { ...one, text } : one)) }\n }\n\n private removeChoice(index: number) {\n this.draft = { ...this.draft, choices: this.draft.choices.filter((_, i) => i !== index) }\n }\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import '@material/web/button/filled-button.js';
2
+ import { LitElement } from 'lit';
3
+ declare const LearningTodo_base: (new (...args: any[]) => LitElement) & typeof LitElement;
4
+ export declare class LearningTodo extends LearningTodo_base {
5
+ static styles: import("lit").CSSResult;
6
+ input: {
7
+ headline?: string;
8
+ address?: string;
9
+ };
10
+ render(): import("lit-html").TemplateResult<1>;
11
+ }
12
+ export {};
@@ -0,0 +1,43 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/button/filled-button.js';
3
+ import { css, html, LitElement } from 'lit';
4
+ import { customElement, property } from 'lit/decorators.js';
5
+ import { i18next, localize } from '@operato/i18n';
6
+ import { navigate } from '@operato/shell';
7
+ /*
8
+ * 수강 (ADR-0194 decision 5) - what the worklist to-do shows: the course, by when, and the way to it. Completing the
9
+ * course there ends the to-do; nothing is answered here.
10
+ */
11
+ let LearningTodo = class LearningTodo extends localize(i18next)(LitElement) {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.input = {};
15
+ }
16
+ static { this.styles = css `
17
+ :host {
18
+ display: block;
19
+ padding: var(--spacing-large, 12px);
20
+ font-size: var(--ui-t-body, 13px);
21
+ line-height: 1.6;
22
+ color: var(--ui-ink);
23
+ }
24
+
25
+ p {
26
+ margin: 0 0 var(--spacing-medium, 8px);
27
+ }
28
+ `; }
29
+ render() {
30
+ const { headline = '', address } = this.input ?? {};
31
+ return html `<p>${headline}</p>
32
+ ${address ? html `<md-filled-button @click=${() => navigate(address)}>${i18next.t('button.learning-open-course')}</md-filled-button>` : ''}`;
33
+ }
34
+ };
35
+ __decorate([
36
+ property({ attribute: false }),
37
+ __metadata("design:type", Object)
38
+ ], LearningTodo.prototype, "input", void 0);
39
+ LearningTodo = __decorate([
40
+ customElement('learning-todo')
41
+ ], LearningTodo);
42
+ export { LearningTodo };
43
+ //# sourceMappingURL=learning-todo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learning-todo.js","sourceRoot":"","sources":["../../client/components/learning-todo.ts"],"names":[],"mappings":";AAAA,OAAO,uCAAuC,CAAA;AAE9C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC;;;GAGG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;IAAxD;;QAe2B,UAAK,GAA4C,EAAE,CAAA;IAOrF,CAAC;aArBQ,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;GAYlB,AAZY,CAYZ;IAID,MAAM;QACJ,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAA;QACnD,OAAO,IAAI,CAAA,MAAM,QAAQ;QACrB,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,4BAA4B,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;IAC/I,CAAC;;AAN+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;2CAAoD;AAfxE,YAAY;IADxB,aAAa,CAAC,eAAe,CAAC;GAClB,YAAY,CAsBxB","sourcesContent":["import '@material/web/button/filled-button.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate } from '@operato/shell'\n\n/*\n * 수강 (ADR-0194 decision 5) - what the worklist to-do shows: the course, by when, and the way to it. Completing the\n * course there ends the to-do; nothing is answered here.\n */\n@customElement('learning-todo')\nexport class LearningTodo extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n display: block;\n padding: var(--spacing-large, 12px);\n font-size: var(--ui-t-body, 13px);\n line-height: 1.6;\n color: var(--ui-ink);\n }\n\n p {\n margin: 0 0 var(--spacing-medium, 8px);\n }\n `\n\n @property({ attribute: false }) input: { headline?: string; address?: string } = {}\n\n render() {\n const { headline = '', address } = this.input ?? {}\n return html`<p>${headline}</p>\n ${address ? html`<md-filled-button @click=${() => navigate(address)}>${i18next.t('button.learning-open-course')}</md-filled-button>` : ''}`\n }\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import '@material/web/button/filled-button.js';
2
+ import '@material/web/button/text-button.js';
3
+ import '@operato/input/ox-input-file.js';
4
+ import '@operato/input/ox-input-select-buttons.js';
5
+ import '@operato/popup/ox-popup-actions.js';
6
+ import { LitElement } from 'lit';
7
+ export type UnitDraft = {
8
+ id?: string;
9
+ kind: string;
10
+ name: string;
11
+ body?: string | null;
12
+ url?: string | null;
13
+ minSeconds?: number | null;
14
+ durationSeconds?: number | null;
15
+ pageCount?: number | null;
16
+ file?: {
17
+ name?: string;
18
+ } | null;
19
+ };
20
+ export type UnitEdit = {
21
+ unit: UnitDraft;
22
+ file?: File;
23
+ durationSeconds?: number;
24
+ pageCount?: number;
25
+ };
26
+ /** Opens the unit form. Resolves with what was given - the unit and a file picked for it - or null. */
27
+ export declare function openUnitForm(unit: UnitDraft): Promise<UnitEdit | null>;
28
+ /** A video's length in seconds, read from the file in the browser - the server needs it to know when it was watched to the end. */
29
+ export declare function videoDurationOf(file: File): Promise<number | undefined>;
30
+ declare const LearningUnitForm_base: (new (...args: any[]) => LitElement) & typeof LitElement;
31
+ export declare class LearningUnitForm extends LearningUnitForm_base {
32
+ static styles: import("lit").CSSResult[];
33
+ unit: UnitDraft;
34
+ private draft;
35
+ private file?;
36
+ private duration?;
37
+ private pageCount?;
38
+ willUpdate(changed: Map<string, unknown>): void;
39
+ render(): import("lit-html").TemplateResult<1>;
40
+ private patch;
41
+ private pick;
42
+ private done;
43
+ }
44
+ export {};
@@ -0,0 +1,151 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/button/filled-button.js';
3
+ import '@material/web/button/text-button.js';
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';
7
+ import { css, html, LitElement, nothing } from 'lit';
8
+ import { customElement, property, state } from 'lit/decorators.js';
9
+ import { i18next, localize } from '@operato/i18n';
10
+ import { pdfPageCount } from '@operato/learning/pdf.js';
11
+ import { openPopup } from '@operato/layout';
12
+ import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles';
13
+ import { t, UNIT_KINDS, unitKindWord } from '../utils/learning-words.js';
14
+ /** Opens the unit form. Resolves with what was given - the unit and a file picked for it - or null. */
15
+ export function openUnitForm(unit) {
16
+ const popup = openPopup(html `<learning-unit-form .unit=${unit} @done=${(e) => popup.close(e.detail)} @close=${() => popup.close()}></learning-unit-form>`, { backdrop: true, size: 'large', title: unit.id ? t('title.learning-unit-edit') : t('title.learning-unit-new') });
17
+ return popup.result.then(value => (value ?? null));
18
+ }
19
+ /** A video's length in seconds, read from the file in the browser - the server needs it to know when it was watched to the end. */
20
+ export function videoDurationOf(file) {
21
+ return new Promise(resolve => {
22
+ const video = document.createElement('video');
23
+ const url = URL.createObjectURL(file);
24
+ const done = (value) => {
25
+ URL.revokeObjectURL(url);
26
+ resolve(value);
27
+ };
28
+ video.preload = 'metadata';
29
+ video.onloadedmetadata = () => done(Number.isFinite(video.duration) ? Math.round(video.duration) : undefined);
30
+ video.onerror = () => done(undefined);
31
+ video.src = url;
32
+ });
33
+ }
34
+ /* 단원 (ADR-0194 decision 3) - its kind, name and what it holds: a video or a PDF file, a text, or a link. */
35
+ let LearningUnitForm = class LearningUnitForm extends localize(i18next)(LitElement) {
36
+ constructor() {
37
+ super(...arguments);
38
+ this.unit = { kind: 'VIDEO', name: '' };
39
+ this.draft = { kind: 'VIDEO', name: '' };
40
+ }
41
+ static { this.styles = [
42
+ CommonPopupStyles,
43
+ PopupBodyStyles,
44
+ FormFieldStyles,
45
+ PopupFieldStyles,
46
+ css `
47
+ ox-input-select-buttons {
48
+ align-self: flex-start;
49
+ }
50
+ textarea {
51
+ min-height: 240px;
52
+ resize: vertical;
53
+ font-family: var(--ui-font-mono, monospace);
54
+ }
55
+ `
56
+ ]; }
57
+ willUpdate(changed) {
58
+ if (changed.has('unit'))
59
+ this.draft = { ...this.unit };
60
+ }
61
+ render() {
62
+ const kind = this.draft.kind;
63
+ const fileKind = kind === 'VIDEO' || kind === 'PDF';
64
+ return html `
65
+ <div class="body">
66
+ <div class="field">
67
+ <span>${t('field.learning-unit-kind')}</span>
68
+ <ox-input-select-buttons .value=${kind} .options=${UNIT_KINDS.map(code => ({ display: unitKindWord(code), value: code }))} @change=${(e) => e.detail && this.patch({ kind: e.detail })}></ox-input-select-buttons>
69
+ <span>${t(`text.learning-unit-${kind.toLowerCase()}-hint`)}</span>
70
+ </div>
71
+ <label class="field">
72
+ <span>${t('field.learning-unit-name')}</span>
73
+ <input .value=${this.draft.name} maxlength="200" @input=${(e) => this.patch({ name: e.target.value })} />
74
+ </label>
75
+ ${fileKind
76
+ ? html `<div class="field">
77
+ <span>${kind === 'VIDEO' ? t('field.learning-unit-video') : t('field.learning-unit-pdf')}</span>
78
+ <ox-input-file accept=${kind === 'VIDEO' ? 'video/*' : 'application/pdf'} @change=${(e) => this.pick(e)}></ox-input-file>
79
+ <span>${this.file ? this.file.name : this.draft.file?.name ? t('text.learning-unit-file-kept', { name: this.draft.file.name }) : t('text.learning-unit-file-none')}
80
+ ${kind === 'VIDEO' && (this.duration ?? this.draft.durationSeconds) ? ` · ${t('text.learning-unit-duration', { minutes: Math.floor((this.duration ?? this.draft.durationSeconds ?? 0) / 60), seconds: (this.duration ?? this.draft.durationSeconds ?? 0) % 60 })}` : ''}
81
+ ${kind === 'PDF' && (this.pageCount ?? this.draft.pageCount) ? ` · ${t('text.learning-unit-pages', { pages: this.pageCount ?? this.draft.pageCount })}` : ''}</span
82
+ >
83
+ </div>`
84
+ : nothing}
85
+ ${kind === 'MARKDOWN'
86
+ ? html `<label class="field">
87
+ <span>${t('field.learning-unit-body')}</span>
88
+ <textarea .value=${this.draft.body ?? ''} @input=${(e) => this.patch({ body: e.target.value })}></textarea>
89
+ </label>`
90
+ : nothing}
91
+ ${kind === 'LINK'
92
+ ? html `<label class="field">
93
+ <span>${t('field.learning-unit-url')}</span>
94
+ <input type="url" .value=${this.draft.url ?? ''} placeholder="https://" @input=${(e) => this.patch({ url: e.target.value })} />
95
+ </label>`
96
+ : nothing}
97
+ ${kind === 'PDF' || kind === 'MARKDOWN'
98
+ ? html `<label class="field">
99
+ <span>${t('field.learning-unit-min-seconds')}</span>
100
+ <input type="number" min="1" step="1" .value=${String(this.draft.minSeconds ?? 60)} @input=${(e) => this.patch({ minSeconds: e.target.value ? Number(e.target.value) : null })} />
101
+ <span>${t('text.learning-unit-min-seconds-hint')}</span>
102
+ </label>`
103
+ : nothing}
104
+ </div>
105
+ <ox-popup-actions>
106
+ <md-text-button slot="cancel" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>
107
+ <md-filled-button slot="confirm" ?disabled=${!this.draft.name?.trim()} @click=${() => this.done()}>${t('button.confirm')}</md-filled-button>
108
+ </ox-popup-actions>
109
+ `;
110
+ }
111
+ patch(change) {
112
+ this.draft = { ...this.draft, ...change };
113
+ }
114
+ async pick(e) {
115
+ const [file] = e.detail ?? [];
116
+ if (!file)
117
+ return;
118
+ this.file = file;
119
+ this.duration = this.draft.kind === 'VIDEO' ? await videoDurationOf(file) : undefined;
120
+ /* Every page of a PDF is to be read - the count is read from the file, as a video's length is. */
121
+ this.pageCount = this.draft.kind === 'PDF' ? await pdfPageCount(file) : undefined;
122
+ }
123
+ done() {
124
+ this.dispatchEvent(new CustomEvent('done', { detail: { unit: { ...this.draft, name: this.draft.name.trim() }, file: this.file, durationSeconds: this.duration, pageCount: this.pageCount } }));
125
+ }
126
+ };
127
+ __decorate([
128
+ property({ attribute: false }),
129
+ __metadata("design:type", Object)
130
+ ], LearningUnitForm.prototype, "unit", void 0);
131
+ __decorate([
132
+ state(),
133
+ __metadata("design:type", Object)
134
+ ], LearningUnitForm.prototype, "draft", void 0);
135
+ __decorate([
136
+ state(),
137
+ __metadata("design:type", File)
138
+ ], LearningUnitForm.prototype, "file", void 0);
139
+ __decorate([
140
+ state(),
141
+ __metadata("design:type", Number)
142
+ ], LearningUnitForm.prototype, "duration", void 0);
143
+ __decorate([
144
+ state(),
145
+ __metadata("design:type", Number)
146
+ ], LearningUnitForm.prototype, "pageCount", void 0);
147
+ LearningUnitForm = __decorate([
148
+ customElement('learning-unit-form')
149
+ ], LearningUnitForm);
150
+ export { LearningUnitForm };
151
+ //# sourceMappingURL=learning-unit-form.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learning-unit-form.js","sourceRoot":"","sources":["../../client/components/learning-unit-form.ts"],"names":[],"mappings":"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","sourcesContent":["import '@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 { pdfPageCount } from '@operato/learning/pdf.js'\nimport { openPopup } from '@operato/layout'\nimport { CommonPopupStyles, FormFieldStyles, PopupBodyStyles, PopupFieldStyles } from '@operato/styles'\n\nimport { t, UNIT_KINDS, unitKindWord } from '../utils/learning-words.js'\n\nexport type UnitDraft = { id?: string; kind: string; name: string; body?: string | null; url?: string | null; minSeconds?: number | null; durationSeconds?: number | null; pageCount?: number | null; file?: { name?: string } | null }\nexport type UnitEdit = { unit: UnitDraft; file?: File; durationSeconds?: number; pageCount?: number }\n\n/** Opens the unit form. Resolves with what was given - the unit and a file picked for it - or null. */\nexport function openUnitForm(unit: UnitDraft): Promise<UnitEdit | null> {\n const popup = openPopup(html`<learning-unit-form .unit=${unit} @done=${(e: CustomEvent) => popup.close(e.detail)} @close=${() => popup.close()}></learning-unit-form>`, { backdrop: true, size: 'large', title: unit.id ? t('title.learning-unit-edit') : t('title.learning-unit-new') })\n return popup.result.then(value => (value ?? null) as any)\n}\n\n/** A video's length in seconds, read from the file in the browser - the server needs it to know when it was watched to the end. */\nexport function videoDurationOf(file: File): Promise<number | undefined> {\n return new Promise(resolve => {\n const video = document.createElement('video')\n const url = URL.createObjectURL(file)\n const done = (value?: number) => {\n URL.revokeObjectURL(url)\n resolve(value)\n }\n video.preload = 'metadata'\n video.onloadedmetadata = () => done(Number.isFinite(video.duration) ? Math.round(video.duration) : undefined)\n video.onerror = () => done(undefined)\n video.src = url\n })\n}\n\n/* 단원 (ADR-0194 decision 3) - its kind, name and what it holds: a video or a PDF file, a text, or a link. */\n@customElement('learning-unit-form')\nexport class LearningUnitForm extends localize(i18next)(LitElement) {\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: 240px;\n resize: vertical;\n font-family: var(--ui-font-mono, monospace);\n }\n `\n ]\n\n @property({ attribute: false }) unit: UnitDraft = { kind: 'VIDEO', name: '' }\n @state() private draft: UnitDraft = { kind: 'VIDEO', name: '' }\n @state() private file?: File\n @state() private duration?: number\n @state() private pageCount?: number\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('unit')) this.draft = { ...this.unit }\n }\n\n render() {\n const kind = this.draft.kind\n const fileKind = kind === 'VIDEO' || kind === 'PDF'\n return html`\n <div class=\"body\">\n <div class=\"field\">\n <span>${t('field.learning-unit-kind')}</span>\n <ox-input-select-buttons .value=${kind} .options=${UNIT_KINDS.map(code => ({ display: unitKindWord(code), value: code }))} @change=${(e: CustomEvent) => e.detail && this.patch({ kind: e.detail })}></ox-input-select-buttons>\n <span>${t(`text.learning-unit-${kind.toLowerCase()}-hint`)}</span>\n </div>\n <label class=\"field\">\n <span>${t('field.learning-unit-name')}</span>\n <input .value=${this.draft.name} maxlength=\"200\" @input=${(e: any) => this.patch({ name: e.target.value })} />\n </label>\n ${fileKind\n ? html`<div class=\"field\">\n <span>${kind === 'VIDEO' ? t('field.learning-unit-video') : t('field.learning-unit-pdf')}</span>\n <ox-input-file accept=${kind === 'VIDEO' ? 'video/*' : 'application/pdf'} @change=${(e: CustomEvent) => this.pick(e)}></ox-input-file>\n <span>${this.file ? this.file.name : this.draft.file?.name ? t('text.learning-unit-file-kept', { name: this.draft.file.name }) : t('text.learning-unit-file-none')}\n ${kind === 'VIDEO' && (this.duration ?? this.draft.durationSeconds) ? ` · ${t('text.learning-unit-duration', { minutes: Math.floor((this.duration ?? this.draft.durationSeconds ?? 0) / 60), seconds: (this.duration ?? this.draft.durationSeconds ?? 0) % 60 })}` : ''}\n ${kind === 'PDF' && (this.pageCount ?? this.draft.pageCount) ? ` · ${t('text.learning-unit-pages', { pages: this.pageCount ?? this.draft.pageCount })}` : ''}</span\n >\n </div>`\n : nothing}\n ${kind === 'MARKDOWN'\n ? html`<label class=\"field\">\n <span>${t('field.learning-unit-body')}</span>\n <textarea .value=${this.draft.body ?? ''} @input=${(e: any) => this.patch({ body: e.target.value })}></textarea>\n </label>`\n : nothing}\n ${kind === 'LINK'\n ? html`<label class=\"field\">\n <span>${t('field.learning-unit-url')}</span>\n <input type=\"url\" .value=${this.draft.url ?? ''} placeholder=\"https://\" @input=${(e: any) => this.patch({ url: e.target.value })} />\n </label>`\n : nothing}\n ${kind === 'PDF' || kind === 'MARKDOWN'\n ? html`<label class=\"field\">\n <span>${t('field.learning-unit-min-seconds')}</span>\n <input type=\"number\" min=\"1\" step=\"1\" .value=${String(this.draft.minSeconds ?? 60)} @input=${(e: any) => this.patch({ minSeconds: e.target.value ? Number(e.target.value) : null })} />\n <span>${t('text.learning-unit-min-seconds-hint')}</span>\n </label>`\n : nothing}\n </div>\n <ox-popup-actions>\n <md-text-button slot=\"cancel\" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>${t('button.cancel')}</md-text-button>\n <md-filled-button slot=\"confirm\" ?disabled=${!this.draft.name?.trim()} @click=${() => this.done()}>${t('button.confirm')}</md-filled-button>\n </ox-popup-actions>\n `\n }\n\n private patch(change: Partial<UnitDraft>) {\n this.draft = { ...this.draft, ...change }\n }\n\n private async pick(e: CustomEvent) {\n const [file] = (e.detail as File[]) ?? []\n if (!file) return\n this.file = file\n this.duration = this.draft.kind === 'VIDEO' ? await videoDurationOf(file) : undefined\n /* Every page of a PDF is to be read - the count is read from the file, as a video's length is. */\n this.pageCount = this.draft.kind === 'PDF' ? await pdfPageCount(file) : undefined\n }\n\n private done() {\n this.dispatchEvent(new CustomEvent('done', { detail: { unit: { ...this.draft, name: this.draft.name.trim() }, file: this.file, durationSeconds: this.duration, pageCount: this.pageCount } }))\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export * from './utils/learning-words.js';
2
+ export { unitsDoneOf } from './utils/learning-api.js';
@@ -0,0 +1,3 @@
1
+ export * from './utils/learning-words.js';
2
+ export { unitsDoneOf } from './utils/learning-api.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA","sourcesContent":["export * from './utils/learning-words.js'\nexport { unitsDoneOf } from './utils/learning-api.js'\n"]}
@@ -0,0 +1,63 @@
1
+ import '@material/web/button/filled-button.js';
2
+ import '@material/web/button/outlined-button.js';
3
+ import '@material/web/button/text-button.js';
4
+ import '@material/web/icon/icon.js';
5
+ import '@material/web/iconbutton/icon-button.js';
6
+ import '@operato/input/ox-input-select-buttons.js';
7
+ import '@operato/list-state/ox-empty-note.js';
8
+ import '@operato/list-state/ox-page-state.js';
9
+ import { PageView } from '@operato/shell';
10
+ declare const LearningCourseEditor_base: (new (...args: any[]) => import("lit").LitElement) & typeof PageView;
11
+ /**
12
+ * 과정 편집 (ADR-0194 decisions 2-4, 7) - a course and its versions. A draft version takes its settings, units and
13
+ * questions; publishing it closes the version before. A published or closed version is shown as it is - it never
14
+ * changes; a change starts a new version copied from it.
15
+ */
16
+ export declare class LearningCourseEditor extends LearningCourseEditor_base {
17
+ static styles: import("lit").CSSResult[];
18
+ private courseId;
19
+ private versionId;
20
+ private settings;
21
+ private course;
22
+ private version;
23
+ private saves;
24
+ get one(): any;
25
+ get current(): any;
26
+ get draft(): boolean;
27
+ get context(): {
28
+ subject?: {
29
+ type: string;
30
+ id: string;
31
+ } | undefined;
32
+ title: any;
33
+ frame: string;
34
+ menu: string;
35
+ actions: any[];
36
+ };
37
+ pageUpdated(changes: any, lifecycle: any): Promise<void>;
38
+ willUpdate(): void;
39
+ private materialDraft;
40
+ private settingsOf;
41
+ render(): import("lit-html").TemplateResult<1>;
42
+ private renderVersions;
43
+ private renderSettings;
44
+ private renderUnits;
45
+ private unitDetail;
46
+ private renderQuestions;
47
+ private editCourse;
48
+ private assign;
49
+ private newVersion;
50
+ private publish;
51
+ private closeVersion;
52
+ private removeVersion;
53
+ private saveSettings;
54
+ private saveUnits;
55
+ private editUnit;
56
+ private moveUnit;
57
+ private removeUnit;
58
+ private saveQuestions;
59
+ private editQuestion;
60
+ private moveQuestion;
61
+ private removeQuestion;
62
+ }
63
+ export {};