@sealcode/sealgen 0.8.48 → 0.8.49

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 (136) hide show
  1. package/.log/ti-107027.log +576 -0
  2. package/.log/ti-129414.log +1355 -0
  3. package/.log/ti-135707.log +1049 -0
  4. package/.log/ti-136226.log +1049 -0
  5. package/.log/ti-147652.log +533 -0
  6. package/.log/ti-157088.log +2938 -0
  7. package/.log/ti-250667.log +548 -0
  8. package/.log/ti-29438.log +165 -0
  9. package/.log/ti-29894.log +149 -0
  10. package/.log/ti-33785.log +149 -0
  11. package/.log/ti-34262.log +149 -0
  12. package/.log/ti-35719.log +149 -0
  13. package/.log/ti-41629.log +518 -0
  14. package/.log/ti-441248.log +517 -0
  15. package/.log/ti-5352.log +517 -0
  16. package/.log/ti-76335.log +639 -0
  17. package/.log/ti-811639.log +517 -0
  18. package/.log/ti-81805.log +564 -0
  19. package/.log/ti-86459.log +517 -0
  20. package/.log/tsserver.log +6881 -0
  21. package/.log/tsserver.sync-conflict-20221010-222251-JUX7UR2.log +9412 -0
  22. package/package.json +1 -1
  23. package/src/add-collection.ts +31 -0
  24. package/src/add-route.test.ts +58 -0
  25. package/src/add-route.ts +89 -0
  26. package/src/build.ts +94 -0
  27. package/src/cli.ts +36 -0
  28. package/src/components/components.ts +2 -0
  29. package/src/components/tabs.ts +95 -0
  30. package/src/components/toggle-modal.ts +52 -0
  31. package/src/example.test.ts +3 -0
  32. package/src/find-scss-includes.ts +32 -0
  33. package/src/forms/controls/array.ts +226 -0
  34. package/src/forms/controls/autocomplete.ts +33 -0
  35. package/src/forms/controls/big-radio-group.ts +48 -0
  36. package/src/forms/controls/checkbox-group.ts +71 -0
  37. package/src/forms/controls/checkbox.ts +34 -0
  38. package/src/forms/controls/checkboxed-list-input.ts +50 -0
  39. package/src/forms/controls/collapsible-checkbox-group.ts +28 -0
  40. package/src/forms/controls/collapsible-field-group.ts +94 -0
  41. package/src/forms/controls/controls.ts +30 -0
  42. package/src/forms/controls/derived-frame.ts +91 -0
  43. package/src/forms/controls/derived.ts +108 -0
  44. package/src/forms/controls/dropdown.ts +87 -0
  45. package/src/forms/controls/editable-collection-subset.ts +97 -0
  46. package/src/forms/controls/field-group.ts +69 -0
  47. package/src/forms/controls/file.ts +47 -0
  48. package/src/forms/controls/form-control.ts +24 -0
  49. package/src/forms/controls/form-field-control.ts +22 -0
  50. package/src/forms/controls/form-header.ts +19 -0
  51. package/src/forms/controls/form-paragraph.ts +11 -0
  52. package/src/forms/controls/frame.ts +26 -0
  53. package/src/forms/controls/hidden-input.ts +22 -0
  54. package/src/forms/controls/html.ts +38 -0
  55. package/src/forms/controls/multiple-files.ts +256 -0
  56. package/src/forms/controls/multiple-photos.ts +44 -0
  57. package/src/forms/controls/number.ts +38 -0
  58. package/src/forms/controls/photo.ts +74 -0
  59. package/src/forms/controls/radio.ts +43 -0
  60. package/src/forms/controls/reverse-single-reference.ts +50 -0
  61. package/src/forms/controls/simple-input.ts +148 -0
  62. package/src/forms/controls/textarea.ts +60 -0
  63. package/src/forms/controls/tickable.ts +127 -0
  64. package/src/forms/fields/boolean.test.ts +36 -0
  65. package/src/forms/fields/boolean.ts +63 -0
  66. package/src/forms/fields/checkboxed-list.ts +66 -0
  67. package/src/forms/fields/collection-field.ts +62 -0
  68. package/src/forms/fields/email.test.ts +24 -0
  69. package/src/forms/fields/email.ts +20 -0
  70. package/src/forms/fields/enum-multiple.test.ts +31 -0
  71. package/src/forms/fields/enum-multiple.ts +62 -0
  72. package/src/forms/fields/enum.test.ts +25 -0
  73. package/src/forms/fields/enum.ts +56 -0
  74. package/src/forms/fields/existing-sealious-value.ts +22 -0
  75. package/src/forms/fields/field.ts +172 -0
  76. package/src/forms/fields/file.ts +89 -0
  77. package/src/forms/fields/multiple-files.ts +20 -0
  78. package/src/forms/fields/number.test.ts +34 -0
  79. package/src/forms/fields/number.ts +70 -0
  80. package/src/forms/fields/pick-from-list.test.ts +19 -0
  81. package/src/forms/fields/pick-from-list.ts +52 -0
  82. package/src/forms/fields/proxy-field.ts +24 -0
  83. package/src/forms/fields/regexp.test.ts +25 -0
  84. package/src/forms/fields/regexp.ts +53 -0
  85. package/src/forms/fields/reverse-single-reference.ts +20 -0
  86. package/src/forms/fields/simple-form-field.ts +30 -0
  87. package/src/forms/fields/structured-array.ts +27 -0
  88. package/src/forms/form.test.ts +351 -0
  89. package/src/forms/form.ts +294 -0
  90. package/src/forms/forms.scss +85 -0
  91. package/src/forms/multiform.test.ts +540 -0
  92. package/src/forms/multiform.ts +236 -0
  93. package/src/generate-collections.ts +77 -0
  94. package/src/generate-routes.ts +121 -0
  95. package/src/index.ts +25 -0
  96. package/src/make-env.ts +15 -0
  97. package/src/make-hidden-inputs.ts +24 -0
  98. package/src/mount.ts +85 -0
  99. package/src/page/access-control.ts +18 -0
  100. package/src/page/list.ts +240 -0
  101. package/src/page/mountable-with-fields.ts +158 -0
  102. package/src/page/mountable.ts +13 -0
  103. package/src/page/page.ts +46 -0
  104. package/src/page/props-parser.ts +38 -0
  105. package/src/page/stateful-page.ts +202 -0
  106. package/src/run.ts +0 -0
  107. package/src/sanitize.ts +9 -0
  108. package/src/templates/collection.ts +11 -0
  109. package/src/templates/form.ts +60 -0
  110. package/src/templates/list.ts +75 -0
  111. package/src/templates/multiform.ts +58 -0
  112. package/src/templates/page.ts +25 -0
  113. package/src/templates/post.ts +21 -0
  114. package/src/templates/redirect.ts +22 -0
  115. package/src/templates/route-test.ts +27 -0
  116. package/src/templates/stateful-page.ts +65 -0
  117. package/src/test_utils/locator_is_visible.ts +17 -0
  118. package/src/utils/assert-throws.ts +18 -0
  119. package/src/utils/base64.ts +7 -0
  120. package/src/utils/browser-creator.js +10 -0
  121. package/src/utils/escape-url-params.ts +14 -0
  122. package/src/utils/exec.ts +30 -0
  123. package/src/utils/format-external-url.ts +13 -0
  124. package/src/utils/import-path.ts +25 -0
  125. package/src/utils/input-wrapper.ts +21 -0
  126. package/src/utils/list-collections.ts +16 -0
  127. package/src/utils/make-slug.ts +5 -0
  128. package/src/utils/natural-numbers.ts +5 -0
  129. package/src/utils/prettier.ts +13 -0
  130. package/src/utils/question.ts +23 -0
  131. package/src/utils/render-attributes.ts +17 -0
  132. package/src/utils/sleep.ts +3 -0
  133. package/src/utils/translate.ts +13 -0
  134. package/src/utils/url-with-new-params.ts +9 -0
  135. package/src/utils/utils.ts +23 -0
  136. package/src/utils/walk.ts +20 -0
@@ -0,0 +1,226 @@
1
+ import Router from "@koa/router";
2
+ import { hasShape, predicates } from "@sealcode/ts-predicates";
3
+ import { Context } from "koa";
4
+ import qs from "qs";
5
+ import { Field, FieldsetOutput } from "sealious";
6
+ import { tempstream } from "tempstream";
7
+ import { Controls, Fields } from "../..";
8
+ import { FormField, StructuredArrayField } from "../fields/field";
9
+ import { FormDataValue } from "../form";
10
+ import { FormControl } from "./controls";
11
+ import { FormControlContext } from "./form-control";
12
+ import { FormFieldControl } from "./form-field-control";
13
+
14
+ export type UnboundControlStorage<
15
+ F extends { new (...args: any): FormFieldControl } = {
16
+ new (...args: any): FormFieldControl;
17
+ }
18
+ > = [F, string, ConstructorParameters<F>];
19
+
20
+ export function UnboundControl<
21
+ F extends { new (...args: any): FormFieldControl }
22
+ >(
23
+ controlClass: F,
24
+ arbitrary_field_name: string,
25
+ params: ConstructorParameters<F>["1"]
26
+ ): UnboundControlStorage {
27
+ return [controlClass, arbitrary_field_name, params];
28
+ }
29
+
30
+ export class StructuredArray<
31
+ Subfields extends Record<string, Field>
32
+ > extends FormFieldControl {
33
+ constructor(
34
+ public field: StructuredArrayField<Subfields>,
35
+ public item_controls: Array<FormControl | UnboundControlStorage>
36
+ ) {
37
+ super([field]);
38
+ }
39
+
40
+ getFrameID() {
41
+ return `array-frame-${this.field.name}`;
42
+ }
43
+
44
+ getInnerFormID() {
45
+ return this.getFrameID() + "-form";
46
+ }
47
+
48
+ getActionURL(form_id?: string, action?: Record<string, unknown>) {
49
+ return `${this.getFrameID()}${
50
+ form_id || action ? "?" : ""
51
+ }${qs.stringify({ form_id, action })}`;
52
+ }
53
+
54
+ async getDefaultItemBody() {
55
+ return {};
56
+ }
57
+
58
+ makeSubfield(_field_name: string): FormField {
59
+ const fields = this;
60
+ return new Fields.SimpleFormField(false);
61
+ }
62
+
63
+ async renderItemControl(
64
+ ctx: Context,
65
+ form_id: string,
66
+ control: FormControl | UnboundControlStorage,
67
+ item: FieldsetOutput<Subfields>,
68
+ index: number
69
+ ) {
70
+ const fctx = {
71
+ ctx,
72
+ data: {},
73
+ messages: [],
74
+ field_name_prefix: "",
75
+ form_id,
76
+ validate: false,
77
+ };
78
+ if (Array.isArray(control)) {
79
+ const [constructor, field_name, params] = control;
80
+ const field = this.makeSubfield(field_name);
81
+ if (!field) {
82
+ console.warn(
83
+ `makeSubfield returned null for field ${field_name}`
84
+ );
85
+ return "";
86
+ }
87
+ await field.init(
88
+ `${this.field.name}[data][${index}][${field_name}]`
89
+ );
90
+ const hidden_shadow = new Controls.Derived(
91
+ [field],
92
+ async (
93
+ values,
94
+ { inner_form_id, field_name }
95
+ ) => /* HTML */ `<input
96
+ name="${field_name}"
97
+ type="hidden"
98
+ value="${values[field_name] || ""}"
99
+ form="${inner_form_id}"
100
+ />`,
101
+ async () => ({
102
+ inner_form_id: this.getInnerFormID(),
103
+ field_name: field.name,
104
+ })
105
+ );
106
+ return tempstream`${new constructor(field, params).render({
107
+ ...fctx,
108
+ data: { [field.name]: item[field_name] as FormDataValue },
109
+ })}${hidden_shadow.render({
110
+ ...fctx,
111
+ data: { [field.name]: item[field_name] as FormDataValue },
112
+ form_id: this.getInnerFormID(),
113
+ })}`;
114
+ } else {
115
+ return control.render(fctx);
116
+ }
117
+ }
118
+
119
+ async renderItem(
120
+ ctx: Context,
121
+ form_id: string,
122
+ item: FieldsetOutput<Subfields>,
123
+ index: number
124
+ ) {
125
+ return tempstream/* HTML */ `<div>
126
+ ${this.item_controls.map((control) =>
127
+ this.renderItemControl(ctx, form_id, control, item, index)
128
+ )}
129
+ <input
130
+ type="submit"
131
+ value="remove"
132
+ form="${this.getInnerFormID()}"
133
+ formaction="${this.getActionURL(form_id, {
134
+ remove: index,
135
+ })}"
136
+ />
137
+ </div>`;
138
+ }
139
+
140
+ async getItems(ctx: Context, raw_values: Record<string, FormDataValue>) {
141
+ return (await this.field.getValue(ctx, raw_values)).parsed as Promise<
142
+ FieldsetOutput<Subfields>[]
143
+ >;
144
+ }
145
+
146
+ async _render(
147
+ ctx: Context,
148
+ form_id: string,
149
+ raw_values: Record<string, FormDataValue>
150
+ ) {
151
+ const items = await this.getItems(ctx, raw_values);
152
+
153
+ return tempstream/* HTML */ `<turbo-frame id="${this.getFrameID()}">
154
+ Reverse Single Reference
155
+ ${items.map((item, index) =>
156
+ this.renderItem(ctx, form_id, item, index)
157
+ )}
158
+ <form id="${this.getInnerFormID()}" method="POST"></form>
159
+ <input
160
+ type="submit"
161
+ value="add"
162
+ form="${this.getInnerFormID()}"
163
+ formaction="${this.getActionURL(form_id, {
164
+ insert: { index: items.length, value: {} },
165
+ })}"
166
+ />
167
+ </turbo-frame>`;
168
+ }
169
+
170
+ render(fctx: FormControlContext) {
171
+ return this._render(fctx.ctx, fctx.form_id, fctx.data);
172
+ }
173
+
174
+ async getTargetItemID(ctx: Context): Promise<string> {
175
+ return ctx.params.id;
176
+ }
177
+
178
+ mount(router: Router) {
179
+ console.log("mounting!", this.getActionURL());
180
+ router.post(this.getActionURL(), async (ctx) => {
181
+ let form_id = ctx.query.form_id;
182
+ if (typeof form_id != "string") {
183
+ throw new Error("Missing form_id query param");
184
+ }
185
+
186
+ const item = await this.field.sealious_field.collection.getByID(
187
+ ctx.$context,
188
+ await this.getTargetItemID(ctx)
189
+ );
190
+
191
+ if (!hasShape({ action: predicates.object }, ctx.$body)) {
192
+ throw new Error("Missing action param");
193
+ }
194
+
195
+ const field_data = ctx.$body[this.field.name] || { data: [] };
196
+ console.log({ action: field_data });
197
+ if (
198
+ !hasShape(
199
+ {
200
+ data: predicates.array(
201
+ this.field.sealious_field.value_predicate
202
+ ),
203
+ },
204
+ field_data
205
+ )
206
+ ) {
207
+ throw new Error("missing data atribute in action description");
208
+ }
209
+ item.set(this.field.name, {
210
+ ...ctx.$body.action,
211
+ data: field_data.data || [],
212
+ });
213
+ console.log("ACTION", {
214
+ ...ctx.$body.action,
215
+ data: field_data.data || [],
216
+ });
217
+ await item.save(ctx.$context);
218
+ console.log(await item.getDecodedBody(ctx.$context, {}));
219
+ ctx.body = this._render(
220
+ ctx,
221
+ form_id,
222
+ await item.getDecodedBody(ctx.$context, {})
223
+ );
224
+ });
225
+ }
226
+ }
@@ -0,0 +1,33 @@
1
+ import { Context } from "koa";
2
+ import { PickFromListField } from "../fields/pick-from-list";
3
+ import { FormDataValue } from "../form";
4
+ import { FormControlContext } from "./form-control";
5
+ import { SimpleInput } from "./simple-input";
6
+
7
+ export class Autocomplete extends SimpleInput<string | null> {
8
+ constructor(
9
+ public field: PickFromListField<boolean>,
10
+ public options: { id?: string; label?: string; placeholder?: string }
11
+ ) {
12
+ super(field);
13
+ }
14
+
15
+ getListID(): string {
16
+ return this.getID() + "_list";
17
+ }
18
+
19
+ async getInputAttributes(fctx: FormControlContext) {
20
+ return {
21
+ ...(await super.getInputAttributes(fctx)),
22
+ list: this.getListID(),
23
+ };
24
+ }
25
+
26
+ async postInput(ctx: Context) {
27
+ return /* HTML */ `<datalist id="${this.getListID()}">
28
+ ${(await this.field.generateOptions(ctx)).map(
29
+ ({ value }) => /* HTML */ `<option>${value}</option>`
30
+ )}
31
+ </datalist>`;
32
+ }
33
+ }
@@ -0,0 +1,48 @@
1
+ import { FlatTemplatable, tempstream } from "tempstream";
2
+ import { FormField } from "../fields/field";
3
+ import { FormControlContext } from "./form-control";
4
+ import { FormFieldControl } from "./form-field-control";
5
+
6
+ export class BigRadioGroup extends FormFieldControl {
7
+ constructor(
8
+ public field: FormField,
9
+ public options: Record<
10
+ string,
11
+ {
12
+ description:
13
+ | FlatTemplatable
14
+ | ((fctx: FormControlContext) => FlatTemplatable);
15
+ }
16
+ >
17
+ ) {
18
+ super([field]);
19
+ }
20
+
21
+ async render(fctx: FormControlContext): Promise<FlatTemplatable> {
22
+ const { raw: current_value } = await this.field.getValue(
23
+ fctx.ctx,
24
+ fctx.data
25
+ );
26
+ return tempstream/* HTML */ `<div class="bigradios">
27
+ ${Object.entries(this.options).map(([key, { description }]) => {
28
+ const id = `${this.field.name}-radio-${key}`;
29
+ return tempstream/* HTML */ `<input
30
+ type="radio"
31
+ name="${this.field.name}"
32
+ value="${key}"
33
+ id="${id}"
34
+ style="display: none"
35
+ ${fctx.form_id ? `form=${fctx.form_id}` : ""}
36
+ ${this.field.required ? "required" : ""}
37
+ ${key == current_value ? "checked" : ""}
38
+ />
39
+ <div class="bigradio">
40
+ ${typeof description == "function"
41
+ ? description(fctx)
42
+ : description}
43
+ <label class="bigradio__cta" for="${id}">Wybierz</label>
44
+ </div>`;
45
+ })}
46
+ </div>`;
47
+ }
48
+ }
@@ -0,0 +1,71 @@
1
+ import { Context } from "koa";
2
+ import { FlatTemplatable } from "tempstream";
3
+ import { EnumMultipleField, FormField } from "../fields/field";
4
+ import { ProxyFormField } from "../fields/proxy-field";
5
+ import { FormDataValue, FormMessage } from "../form";
6
+ import { CheckboxWithValue } from "./checkbox";
7
+ import { FieldGroup, FormFieldControl } from "./controls";
8
+ import { FormControlContext } from "./form-control";
9
+
10
+ export type DefaultCheckboxOptions = {
11
+ id?: string;
12
+ label?: string;
13
+ hide_errors?: boolean;
14
+ readonly?: boolean;
15
+ name?: string;
16
+ checkboxNameToLabel?: (name: string) => string;
17
+ classes?: string[];
18
+ };
19
+
20
+ export class CheckboxGroup<
21
+ Options extends DefaultCheckboxOptions = DefaultCheckboxOptions
22
+ > extends FormFieldControl {
23
+ constructor(
24
+ public field: EnumMultipleField<readonly string[], boolean>,
25
+ public options: Options = {} as Options
26
+ ) {
27
+ super([field]);
28
+ }
29
+
30
+ wrapGroup(
31
+ fctx: FormControlContext,
32
+ checkboxes: CheckboxWithValue[]
33
+ ): Promise<FlatTemplatable> {
34
+ return new FieldGroup(checkboxes, {
35
+ label: this.options.label,
36
+ classes: [
37
+ ...(this.options.classes || []),
38
+ "field-group--checkboxes",
39
+ ],
40
+ }).render(fctx);
41
+ }
42
+
43
+ async render<Fields extends Record<string, FormField>>(
44
+ fctx: FormControlContext
45
+ ): Promise<FlatTemplatable> {
46
+ const { parsed: values } = await this.field.getValue(
47
+ fctx.ctx,
48
+ fctx.data
49
+ );
50
+ const checkboxes = this.field.options.map(
51
+ (option) =>
52
+ new CheckboxWithValue(
53
+ new ProxyFormField(
54
+ false,
55
+ this.field.name,
56
+ false,
57
+ values.includes(option)
58
+ ),
59
+ {
60
+ id: this.field.name + "__" + option,
61
+ label: (
62
+ this.options.checkboxNameToLabel ||
63
+ ((s: string) => s)
64
+ )(option),
65
+ },
66
+ option
67
+ )
68
+ );
69
+ return this.wrapGroup(fctx, checkboxes);
70
+ }
71
+ }
@@ -0,0 +1,34 @@
1
+ import { Context } from "koa";
2
+ import { FormDataValue } from "../form";
3
+ import { Tickable, TickableOptions } from "./tickable";
4
+ import { FormField } from "../fields/field";
5
+
6
+ export class Checkbox extends Tickable<boolean> {
7
+ type = <const>"checkbox";
8
+
9
+ isChecked(
10
+ _ctx: Context,
11
+ _data: Record<string, FormDataValue>,
12
+ value: boolean
13
+ ): boolean {
14
+ return value;
15
+ }
16
+
17
+ getValueAttribute(): string {
18
+ return "";
19
+ }
20
+ }
21
+
22
+ export class CheckboxWithValue extends Checkbox {
23
+ constructor(
24
+ public field: FormField<boolean, boolean>,
25
+ options: TickableOptions,
26
+ public value: string
27
+ ) {
28
+ super(field, options);
29
+ }
30
+
31
+ getValueAttribute(): string {
32
+ return this.value;
33
+ }
34
+ }
@@ -0,0 +1,50 @@
1
+ import { is, predicates } from "@sealcode/ts-predicates";
2
+ import { Context } from "koa";
3
+ import { tempstream } from "tempstream";
4
+ import { getRequiredClass, inputWrapper } from "../../utils/input-wrapper";
5
+ import { ChekboxedListField } from "../fields/field";
6
+ import { FormDataValue } from "../form";
7
+ import { FormFieldControl } from "./controls";
8
+ import { FormControlContext } from "./form-control";
9
+
10
+ export class CheckboxedListInput extends FormFieldControl {
11
+ constructor(
12
+ public field: ChekboxedListField<boolean>,
13
+ public options: { label: string } = { label: field.name }
14
+ ) {
15
+ super([field]);
16
+ }
17
+
18
+ async render(fctx: FormControlContext) {
19
+ const { parsed: pickedValues } = await this.field.getValue(
20
+ fctx.ctx,
21
+ fctx.data
22
+ );
23
+ if (!is(pickedValues, predicates.array(predicates.string))) {
24
+ throw new Error("picked values is not an array of strings");
25
+ }
26
+ const [options, isVisible] = await Promise.all([
27
+ this.field.generateOptions(fctx.ctx),
28
+ this.field.isVisible(fctx.ctx),
29
+ ]);
30
+
31
+ const { name, required } = this.field;
32
+
33
+ return tempstream/* HTML */ `${isVisible
34
+ ? Object.entries(options).map(([value, text]) =>
35
+ inputWrapper(
36
+ [name, getRequiredClass(required)],
37
+ /* HTML */ `
38
+ <input
39
+ type="checkbox"
40
+ id="${name}.${value}"
41
+ name="${name}.${value}"
42
+ ${pickedValues.includes(value) ? "checked" : ""}
43
+ />
44
+ <label for="${name}.${value}">${text}</label>
45
+ `
46
+ )
47
+ )
48
+ : ""}`;
49
+ }
50
+ }
@@ -0,0 +1,28 @@
1
+ import { Context } from "koa";
2
+ import { FlatTemplatable } from "tempstream";
3
+ import { FormDataValue, FormMessage } from "../form";
4
+ import { CheckboxWithValue } from "./checkbox";
5
+ import { FieldGroup, FormFieldControl } from "./controls";
6
+ import { CheckboxGroup, DefaultCheckboxOptions } from "./checkbox-group";
7
+ import { CollapsibleFieldGroup } from "./collapsible-field-group";
8
+ import { FormControlContext } from "./form-control";
9
+
10
+ export class CollapsibleCheckboxGroup extends CheckboxGroup<
11
+ DefaultCheckboxOptions & {
12
+ textWhenOpen?: string;
13
+ textWhenClosed?: string;
14
+ groupID?: string;
15
+ }
16
+ > {
17
+ wrapGroup(
18
+ fctx: FormControlContext,
19
+ checkboxes: CheckboxWithValue[]
20
+ ): Promise<FlatTemplatable> {
21
+ return new CollapsibleFieldGroup(checkboxes, {
22
+ label: this.options.label,
23
+ textWhenOpen: this.options.textWhenOpen,
24
+ textWhenClosed: this.options.textWhenClosed,
25
+ groupID: this.options.groupID,
26
+ }).render(fctx);
27
+ }
28
+ }
@@ -0,0 +1,94 @@
1
+ import { FlatTemplatable } from "tempstream";
2
+ import { FieldGroup } from "./field-group";
3
+ import { randomUUID } from "crypto";
4
+ import { FormControl } from "./form-control";
5
+
6
+ type CollapsibleFieldGroupOptions = {
7
+ label?: string;
8
+ classes?: string[];
9
+ openOnStart?: boolean;
10
+ textWhenOpen?: string;
11
+ textWhenClosed?: string;
12
+ groupID?: string;
13
+ };
14
+
15
+ export class CollapsibleFieldGroup extends FieldGroup {
16
+ public collapseID: string;
17
+ constructor(
18
+ public controls: FormControl[],
19
+ public options: CollapsibleFieldGroupOptions = {}
20
+ ) {
21
+ super([]);
22
+ if (this.options.groupID == undefined) {
23
+ this.options.groupID = "A" + randomUUID(); // "A" is needed as ids always have to start with a letter
24
+ }
25
+ this.collapseID = this.options.groupID + "collapse";
26
+ }
27
+
28
+ getGroupLabelProps() {
29
+ return super.getGroupLabelProps() + ` for="${this.collapseID}"`;
30
+ }
31
+
32
+ preLabelContainer(): FlatTemplatable {
33
+ return /* HTML */ `<style>
34
+ input#${this.collapseID} {
35
+ display: none;
36
+ }
37
+
38
+ input#${this.collapseID} ~ .field-group__fields {
39
+ display: none;
40
+ }
41
+
42
+ input#${this.collapseID}:checked ~ .field-group__fields {
43
+ display: block;
44
+ }
45
+
46
+ input#${this.collapseID}
47
+ ~ .field-group__label-container
48
+ .field-group__collapse-label-text--open {
49
+ display: none;
50
+ }
51
+
52
+ input#${this.collapseID}:checked
53
+ ~ .field-group__label-container
54
+ .field-group__collapse-label-text--open {
55
+ display: inline-block;
56
+ }
57
+
58
+ input#${this.collapseID}
59
+ ~ .field-group__label-container
60
+ .field-group__collapse-label-text--closed {
61
+ display: inline-block;
62
+ }
63
+
64
+ input#${this.collapseID}:checked
65
+ ~ .field-group__label-container
66
+ .field-group__collapse-label-text--closed {
67
+ display: none;
68
+ }
69
+ </style>
70
+ <input
71
+ type="checkbox"
72
+ id="${this.collapseID}"
73
+ ${this.options.openOnStart ? "checked" : ""}
74
+ />`;
75
+ }
76
+
77
+ postLabel(): FlatTemplatable {
78
+ return /* HTML */ `<label
79
+ class="field-group__collapse-label"
80
+ for="${this.collapseID}"
81
+ >
82
+ <span
83
+ class="field-group__collapse-label-text field-group__collapse-label-text--open"
84
+ >
85
+ ${this.options.textWhenOpen || "close"}
86
+ </span>
87
+ <span
88
+ class="field-group__collapse-label-text field-group__collapse-label-text--closed"
89
+ >
90
+ ${this.options.textWhenClosed || "open"}
91
+ </span>
92
+ </label>`;
93
+ }
94
+ }
@@ -0,0 +1,30 @@
1
+ export { FormControl } from "./form-control";
2
+ export { FormFieldControl } from "./form-field-control";
3
+ export { EditableCollectionSubset } from "./editable-collection-subset";
4
+ export { Frame } from "./frame";
5
+ export { CheckboxedListInput } from "./checkboxed-list-input";
6
+ export * from "./dropdown";
7
+ export { SimpleInput } from "./simple-input";
8
+ export { FormHeader } from "./form-header";
9
+ export { FormParagraph } from "./form-paragraph";
10
+ export { HTML } from "./html";
11
+ export { HiddenInput } from "./hidden-input";
12
+ export { Textarea } from "./textarea";
13
+ export { FieldGroup } from "./field-group";
14
+ export * from "./derived";
15
+ export * from "./derived-frame";
16
+ export * from "./tickable";
17
+ export * from "./checkbox";
18
+ export * from "./radio";
19
+ export * from "./big-radio-group";
20
+ export * from "./checkbox-group";
21
+ export * from "./collapsible-field-group";
22
+ export * from "./collapsible-checkbox-group";
23
+ export * from "./autocomplete";
24
+ export * from "./file";
25
+ export * from "./photo";
26
+ export * from "./multiple-files";
27
+ export * from "./number";
28
+ export * from "./multiple-photos";
29
+ // export * from "./reverse-single-reference";
30
+ export * from "./array";