@media-quest/builder 0.0.1 → 0.0.3

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 (49) hide show
  1. package/dist/public-api.d.mts +652 -0
  2. package/dist/public-api.d.ts +652 -0
  3. package/dist/public-api.js +2187 -0
  4. package/dist/public-api.mjs +2136 -0
  5. package/package.json +11 -3
  6. package/src/Builder-option.ts +67 -0
  7. package/src/Builder-page.spec.ts +313 -0
  8. package/src/Builder-page.ts +249 -0
  9. package/src/Builder-question.spec.ts +68 -0
  10. package/src/Builder-question.ts +101 -0
  11. package/src/Builder-schema.spec.ts +302 -0
  12. package/src/Builder-schema.ts +250 -0
  13. package/src/Builder-text.spec.ts +24 -0
  14. package/src/Builder-text.ts +57 -0
  15. package/src/BuilderMainImageDto.ts +7 -0
  16. package/src/BuilderMainText.ts +81 -0
  17. package/src/BuilderMainVideoDto.ts +10 -0
  18. package/src/BuilderObject.ts +69 -0
  19. package/src/BuilderTag.ts +97 -0
  20. package/src/media-files.ts +28 -0
  21. package/src/public-api.ts +13 -6
  22. package/src/rulebuilder/Builder-rule.spec.ts +207 -0
  23. package/src/rulebuilder/Builder-rule.ts +134 -0
  24. package/src/rulebuilder/RuleAction.ts +20 -0
  25. package/src/rulebuilder/RuleBuilder-test-utils.ts +254 -0
  26. package/src/rulebuilder/RuleInput.ts +44 -0
  27. package/src/rulebuilder/RuleVariable.ts +39 -0
  28. package/src/rulebuilder/SingleSelectItem.ts +135 -0
  29. package/src/rulebuilder/condition/Builder-condition-group.spec.ts +47 -0
  30. package/src/rulebuilder/condition/Builder-condition-group.ts +91 -0
  31. package/src/rulebuilder/condition/Builder-condition.spec.ts +169 -0
  32. package/src/rulebuilder/condition/Builder-condition.ts +186 -0
  33. package/src/rulebuilder/condition/Builder-operator.spec.ts +9 -0
  34. package/src/rulebuilder/condition/Builder-operator.ts +31 -0
  35. package/src/rulebuilder/index.ts +22 -0
  36. package/src/rulebuilder/jump-to-action-manager.ts +33 -0
  37. package/src/rulebuilder/multi-select-item.ts +70 -0
  38. package/src/rulebuilder/page-action-manager.ts +20 -0
  39. package/src/rulebuilder/tag-action-manager.spec.ts +44 -0
  40. package/src/rulebuilder/tag-action-manager.ts +18 -0
  41. package/src/theme/AbstractThemeCompiler.ts +7 -0
  42. package/src/theme/IDefaultTheme.ts +178 -0
  43. package/src/theme/css-theme.ts +7 -0
  44. package/src/theme/default-theme-compiler.ts +395 -0
  45. package/src/theme/icon-urls.ts +29 -0
  46. package/src/theme/standard-props.ts +113 -0
  47. package/src/theme/theme-utils.ts +110 -0
  48. package/src/theme/theme1.spec.ts +52 -0
  49. package/tsconfig.json +0 -2
@@ -0,0 +1,33 @@
1
+ import type { RuleInput } from "./RuleInput";
2
+ import { JumpToPageSelectItem } from "./SingleSelectItem";
3
+
4
+ export class JumpToActionManager {
5
+ readonly options: ReadonlyArray<JumpToPageSelectItem>;
6
+ private _selected: JumpToPageSelectItem | false;
7
+ constructor(
8
+ private readonly validOptions: RuleInput["_jumpActions"],
9
+ private readonly initialSelection: string | false | undefined
10
+ ) {
11
+ this.options = validOptions.map(JumpToPageSelectItem.create);
12
+ this._selected = this.findSelected(initialSelection);
13
+ }
14
+ get selected(): JumpToPageSelectItem | false {
15
+ return this._selected;
16
+ }
17
+ set selected(selected: JumpToPageSelectItem | false) {
18
+ this._selected = this.findSelected(selected);
19
+ }
20
+ getSelectedPageId(): string | false {
21
+ return this._selected ? this._selected.data.pageId : false;
22
+ }
23
+ private findSelected(value: unknown): JumpToPageSelectItem | false {
24
+ if (!value) return false;
25
+ if (value instanceof JumpToPageSelectItem) {
26
+ return this.options.find((v) => v === value) || false;
27
+ }
28
+ if (typeof value === "string") {
29
+ return this.options.find((v) => v.data.pageId === value) || false;
30
+ }
31
+ return false;
32
+ }
33
+ }
@@ -0,0 +1,70 @@
1
+ import { ExcludeByPageAction, ExcludeByTagAction } from "./RuleAction";
2
+
3
+ export abstract class MultiSelectItem<T> {
4
+ private readonly _isSelectedInitially: boolean;
5
+ private readonly _selectLabel;
6
+ private readonly _toolTip;
7
+ private readonly _searchString;
8
+ public isSelected: boolean;
9
+ get selectLabel(): string {
10
+ return this._selectLabel;
11
+ }
12
+ get tooltip() {
13
+ return this._toolTip;
14
+ }
15
+
16
+ get searchString() {
17
+ return this._searchString;
18
+ }
19
+ protected constructor(readonly data: T, isSelected: boolean) {
20
+ this._isSelectedInitially = isSelected;
21
+ this.isSelected = isSelected;
22
+ this._searchString = this.getSearchString();
23
+ this._toolTip = this.getTooltip();
24
+ this._selectLabel = this.getSelectLabel();
25
+ }
26
+ protected abstract getSelectLabel(): string;
27
+ protected abstract getTooltip(): string;
28
+ protected abstract getSearchString(): string;
29
+ }
30
+
31
+ export class ExcludeByTagSelectItem extends MultiSelectItem<ExcludeByTagAction> {
32
+ public static readonly create = (tagData: ExcludeByTagAction, isSelected: boolean) => {
33
+ return new ExcludeByTagSelectItem(tagData, isSelected);
34
+ };
35
+ protected constructor(data: ExcludeByTagAction, isSelected: boolean) {
36
+ super(data, isSelected);
37
+ }
38
+ protected getSearchString(): string {
39
+ return this.data.tag;
40
+ }
41
+
42
+ protected getSelectLabel(): string {
43
+ return this.data.tag + " (" + this.data.pageCount + ")";
44
+ }
45
+
46
+ protected getTooltip(): string {
47
+ return this.data.tag + " (used in " + this.data.pageCount + " pages)";
48
+ }
49
+ }
50
+
51
+ export class ExcludeByPageIdSelectItem extends MultiSelectItem<ExcludeByPageAction> {
52
+ public static create = (ruleActionPage: ExcludeByPageAction, isSelected: boolean) => {
53
+ return new ExcludeByPageIdSelectItem(ruleActionPage, isSelected);
54
+ };
55
+
56
+ protected constructor(data: ExcludeByPageAction, isSelected: boolean) {
57
+ super(data, isSelected);
58
+ }
59
+ protected getSearchString(): string {
60
+ return this.data.pageId + this.data.mainText;
61
+ }
62
+
63
+ protected getSelectLabel(): string {
64
+ return this.data.pageId + " (" + this.data.pageNumber + ")";
65
+ }
66
+
67
+ protected getTooltip(): string {
68
+ return this.data.mainText;
69
+ }
70
+ }
@@ -0,0 +1,20 @@
1
+ import type { RuleInput } from "./RuleInput";
2
+ import { ExcludeByPageIdSelectItem } from "./multi-select-item";
3
+
4
+ export class PageActionManager {
5
+ private readonly _initialSelection: Set<string>;
6
+ readonly selectItems: ReadonlyArray<ExcludeByPageIdSelectItem>;
7
+
8
+ constructor(readonly validOptions: RuleInput["_pageIdActions"], readonly initialSelection: ReadonlyArray<string>) {
9
+ this._initialSelection = new Set([...initialSelection]);
10
+ this.selectItems = validOptions.map((opt) => {
11
+ const isSelected = this._initialSelection.has(opt.pageId);
12
+ return ExcludeByPageIdSelectItem.create(opt, isSelected);
13
+ });
14
+ }
15
+
16
+ getCurrentSelection(): ReadonlyArray<string> {
17
+ const selected = this.selectItems.filter((item) => item.isSelected).map((itm) => itm.data.pageId);
18
+ return selected;
19
+ }
20
+ }
@@ -0,0 +1,44 @@
1
+ import type { ExcludeByTagAction } from "./RuleAction";
2
+ import { TagActionManager } from "./tag-action-manager";
3
+
4
+ const opt = (tag: string): ExcludeByTagAction => {
5
+ return { kind: "exclude-by-tag", tag, pageCount: 2, description: "" };
6
+ };
7
+
8
+ const t1 = opt("tag1");
9
+ const t2 = opt("tag2");
10
+ const t3 = opt("tag3");
11
+ const t4 = opt("tag4");
12
+ const t5 = opt("tag5");
13
+ const t6 = opt("tag6");
14
+ const allOptions = [t1, t2, t3, t4, t5, t6];
15
+ describe("Builder Rule Tag ", () => {
16
+ test("Can initialize ExcludeByTagCollection, and filter invalid tags.", () => {
17
+ const collection = new TagActionManager(allOptions, [t1.tag, "__invalid__tag"]);
18
+
19
+ expect(collection).toBeInstanceOf(TagActionManager);
20
+ const initialTags = collection.getCurrentSelection();
21
+ const initialTag0 = initialTags[0];
22
+ expect(initialTag0).toBe(t1.tag);
23
+ expect(collection.getCurrentSelection().length).toBe(1);
24
+ });
25
+
26
+ test("Will remove duplicate selections.", () => {
27
+ const initialSelection = [t1.tag, t1.tag, t6.tag];
28
+ const collection = new TagActionManager(allOptions, initialSelection);
29
+
30
+ const initialTags = collection.getCurrentSelection();
31
+ expect(initialTags[0]).toBe(t1.tag);
32
+ expect(collection.getCurrentSelection().length).toBe(2);
33
+ });
34
+ test("Can update selection.", () => {
35
+ const initialSelection = [t1.tag, t6.tag];
36
+ const collection = new TagActionManager(allOptions, initialSelection);
37
+
38
+ const initialTags = collection.getCurrentSelection();
39
+ expect(initialTags[0]).toBe(t1.tag);
40
+ expect(collection.getCurrentSelection().length).toBe(2);
41
+ // collection.setTags([t1.tag, t2.tag, t3.tag]);
42
+ // expect(collection.getTags().length).toBe(3);
43
+ });
44
+ });
@@ -0,0 +1,18 @@
1
+ import type { RuleInput } from "./RuleInput";
2
+ import { ExcludeByTagSelectItem } from "./multi-select-item";
3
+
4
+ export class TagActionManager {
5
+ private readonly _initialSelection: Set<string>;
6
+ readonly selectItems: ReadonlyArray<ExcludeByTagSelectItem>;
7
+ constructor(readonly validOptions: RuleInput["_tagActions"], readonly initialSelection: ReadonlyArray<string>) {
8
+ this._initialSelection = new Set([...initialSelection]);
9
+ this.selectItems = validOptions.map((opt) => {
10
+ const isSelected = this._initialSelection.has(opt.tag);
11
+ return ExcludeByTagSelectItem.create(opt, isSelected);
12
+ });
13
+ }
14
+ getCurrentSelection(): ReadonlyArray<string> {
15
+ const selected = this.selectItems.filter((item) => item.isSelected).map((itm) => itm.data.tag);
16
+ return selected;
17
+ }
18
+ }
@@ -0,0 +1,7 @@
1
+ import type { BuilderSchemaDto } from "../Builder-schema";
2
+ import { SchemaDto } from "@media-quest/engine";
3
+
4
+ export abstract class AbstractThemeCompiler<ThemeSchema> {
5
+ protected constructor(protected readonly theme: ThemeSchema) {}
6
+ abstract compile(schema: BuilderSchemaDto): SchemaDto;
7
+ }
@@ -0,0 +1,178 @@
1
+ import type { CssTheme } from "./css-theme";
2
+ import { DStyle, DCss } from "@media-quest/engine";
3
+ import { IconUrls } from "./icon-urls";
4
+
5
+ type PStyle = Partial<DStyle>;
6
+ export interface IDefaultTheme {
7
+ name: string;
8
+ image: { style: PStyle };
9
+ videoPlayer: {
10
+ playButton: {
11
+ iconUrl: string;
12
+ css: PStyle;
13
+ cssDisabled: PStyle;
14
+ cssEnabled: PStyle;
15
+ };
16
+ pauseButton: {
17
+ iconUrl: string;
18
+ css: PStyle;
19
+ cssDisabled: PStyle;
20
+ cssEnabled: PStyle;
21
+ };
22
+ videoElement: {
23
+ css: PStyle;
24
+ };
25
+ };
26
+ mainText: {
27
+ noMedia: {
28
+ text: { css: PStyle; cssDisabled: PStyle; cssEnabled: PStyle };
29
+ audio: { css: PStyle; cssDisabled: PStyle; cssEnabled: PStyle };
30
+ };
31
+ withMedia: {
32
+ text: { css: PStyle; cssDisabled: PStyle; cssEnabled: PStyle };
33
+ audio: { css: PStyle; cssDisabled: PStyle; cssEnabled: PStyle };
34
+ };
35
+ };
36
+ // mainTextTheme: BuilderTextTheme;
37
+ responseButtons: BuilderOptionTheme;
38
+ nextButtonTheme: BuilderOptionTheme;
39
+ // mainQuestionText: BuilderTextTheme;
40
+ dontKnowButtonTheme?: BuilderOptionTheme;
41
+ buttonThemes?: Array<BuilderOptionTheme>;
42
+ }
43
+
44
+ export interface BuilderOptionTheme {
45
+ name: string;
46
+ div: CssTheme;
47
+ text1: PStyle;
48
+ text2?: CssTheme;
49
+ }
50
+
51
+ namespace BuilderOptionTheme {
52
+ const GREEN = "#70AD47";
53
+ const YELLOW = "#FFC000";
54
+ const ORANGE = "#F4902C";
55
+ const RED = "#FF0000";
56
+ const LIGHT_BLUE = "#42719C";
57
+ const WHITE = "#ffffff";
58
+ const BLUE = "#2F5597";
59
+ const BTN_WIDTH = 18.5;
60
+ const BTN_BORDER_WIDTH = 3;
61
+ const BTN_BORDER_RADIUS = 10;
62
+ const BTN_BORDER_STYLE: DStyle["borderStyle"] = "solid";
63
+ const BTN_HEIGHT = 9.2;
64
+ const BTN_SHORT_WIDTH = 13.7;
65
+ const FONT_WEIGHT: DStyle["fontWeight"] = 600;
66
+ const FONT_SIZE: DCss.Px["value"] = 35;
67
+
68
+ export const blueButton = (): BuilderOptionTheme => ({
69
+ name: "blue-button-theme",
70
+ div: {
71
+ css: {
72
+ backgroundColor: BLUE,
73
+ borderColor: BLUE,
74
+ textColor: WHITE,
75
+ fontSize: { _unit: "px", value: FONT_SIZE },
76
+ borderWidth: { _unit: "px", value: BTN_BORDER_WIDTH },
77
+ borderStyle: BTN_BORDER_STYLE,
78
+ borderRadius: { value: BTN_BORDER_RADIUS, _unit: "px" },
79
+ padding: { _unit: "px", value: 40 },
80
+ h: BTN_HEIGHT,
81
+ w: BTN_WIDTH,
82
+ x: 10,
83
+ y: 8,
84
+ textAlign: "center",
85
+ },
86
+ cssDisabled: { opacity: 0.3, cursor: "not-allowed" },
87
+ cssEnabled: { opacity: 1, cursor: "pointer" },
88
+ },
89
+ text1: {
90
+ y: 50,
91
+ transform: "translate(0%, 50%)",
92
+ textColor: WHITE,
93
+ fontSize: { _unit: "px", value: FONT_SIZE },
94
+ w: 84,
95
+ x: 8,
96
+ fontWeight: FONT_WEIGHT,
97
+ textAlign: "center",
98
+ },
99
+ });
100
+ }
101
+
102
+ export const DefaultTheme: IDefaultTheme = {
103
+ name: "default-theme",
104
+ videoPlayer: {
105
+ playButton: {
106
+ iconUrl: IconUrls.playCircleRegular,
107
+ css: { w: 5, h: 5, y: 48, x: 4 },
108
+ cssDisabled: { opacity: 0.3, cursor: "not-allowed" },
109
+ cssEnabled: { opacity: 0.8, cursor: "pointer" },
110
+ },
111
+ pauseButton: {
112
+ iconUrl: IconUrls.pauseSvg,
113
+ css: { w: 5, h: 5, y: 48, x: 4 },
114
+ cssDisabled: { opacity: 0.3, cursor: "not-allowed" },
115
+ cssEnabled: { opacity: 0.8, cursor: "pointer" },
116
+ },
117
+ videoElement: { css: { w: 100, h: 45, y: 55, x: 0 } },
118
+ },
119
+ image: { style: { h: 50, w: 100, x: 0 } },
120
+ mainText: {
121
+ noMedia: {
122
+ text: {
123
+ css: {
124
+ w: 80,
125
+ y: 27,
126
+ x: 10,
127
+ textAlign: "center",
128
+ textColor: "black",
129
+ fontSize: { _unit: "px", value: 30 },
130
+ },
131
+ cssDisabled: {},
132
+ cssEnabled: {},
133
+ },
134
+ audio: {
135
+ css: {
136
+ h: 6,
137
+ w: 6,
138
+ x: 4,
139
+ y: 32,
140
+ cursor: "pointer",
141
+ opacity: 0.8,
142
+ visibility: "visible",
143
+ },
144
+ cssDisabled: { opacity: 0.3, cursor: "not-allowed" },
145
+ cssEnabled: { opacity: 0.8, cursor: "pointer" },
146
+ },
147
+ },
148
+ withMedia: {
149
+ text: {
150
+ css: {
151
+ w: 80,
152
+ y: 27,
153
+ x: 10,
154
+ textAlign: "center",
155
+ textColor: "black",
156
+ fontSize: { _unit: "px", value: 30 },
157
+ },
158
+ cssDisabled: {},
159
+ cssEnabled: {},
160
+ },
161
+ audio: {
162
+ css: {
163
+ h: 6,
164
+ w: 6,
165
+ x: 4,
166
+ y: 32,
167
+ cursor: "pointer",
168
+ opacity: 0.8,
169
+ visibility: "visible",
170
+ },
171
+ cssDisabled: { opacity: 0.3, cursor: "not-allowed" },
172
+ cssEnabled: { opacity: 0.8, cursor: "pointer" },
173
+ },
174
+ },
175
+ },
176
+ nextButtonTheme: BuilderOptionTheme.blueButton(),
177
+ responseButtons: BuilderOptionTheme.blueButton(),
178
+ };
@@ -0,0 +1,7 @@
1
+ import { DStyle } from "@media-quest/engine";
2
+
3
+ export interface CssTheme<S extends Partial<DStyle> = Partial<DStyle>> {
4
+ css: S;
5
+ cssEnabled: Partial<DStyle>;
6
+ cssDisabled: Partial<DStyle>;
7
+ }