surveyjs / survey-creator

Scalable open-source survey software to generate dynamic JSON-driven forms within your JavaScript application. The form builder features a drag-and-drop UI, CSS Theme Editor, and GUI for conditional logic and form branching.
https://surveyjs.io/open-source
Other
915 stars 373 forks source link

Add Reusable Custom Themes - TypeScript compilation errors #5278

Closed JaneSjs closed 8 months ago

JaneSjs commented 8 months ago

T17030 - Add Reusable Custom Themes https://surveyjs.answerdesk.io/internal/ticket/details/T17030


The code of the Add Reusable Custom Themes Demo (Angular) contains TypeScript compilation errors.

creator-angular-demo.zip

tsv2013 commented 8 months ago

We've fixed some issues in the example code and in the library code. Closing for now.

JaneSjs commented 8 months ago

It appears that some code changes were required. In particular, creatorSettings was unnecessary. The updated demo is available at reusable-custom-themes.zip.

survey-creator.component.ts

import { Component, OnInit } from "@angular/core";
import { SurveyCreatorModel } from "survey-creator-core";
import "survey-core/survey.i18n.js";
import "survey-creator-core/survey-creator-core.i18n.js";
import { formJSON } from "./survey_json";
import { customTheme } from "./theme_json";
import { lightTheme, darkTheme } from "./theme_variations";
import { SurveyModel, Action, ComputedUpdater, settings, surveyLocalization, SvgRegistry } from "survey-core";
import { editorLocalization, PredefinedThemes } from "survey-creator-core";
import "survey-core/defaultV2.css";
import "survey-creator-core/survey-creator-core.css";

const enLocale = editorLocalization.getLocale("en");

@Component({
    // tslint:disable-next-line:component-selector
    selector: "survey-creator-component",
    templateUrl: "./survey-creator.component.html",
    styleUrls: ["./survey-creator.component.css"]
})
export class SurveyCreatorComponent implements OnInit {
    model: SurveyCreatorModel;
    ngOnInit() {
        const options = {
            showThemeTab: true
        };
        const creator = new SurveyCreatorModel(options);
        // Register a custom SVG icon for the Save Theme action
        const saveAsIcon = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d = "M24 11H22V13H20V11H18V9H20V7H22V9H24V11ZM20 14H22V20C22 21.1 21.1 22 20 22H4C2.9 22 2 21.1 2 20V4L4 2H20C21.1 2 22 2.9 22 4V6H20V4H17V8H7V4H4.83L4 4.83V20H6V13H18V20H20V14ZM9 6H15V4H9V6ZM16 15H8V20H16V15Z" fill = "black" fill-opacity="0.45" /></svg>';
        SvgRegistry.registerIconFromSvg("icon-saveas", saveAsIcon);

        const themeTabPlugin = creator.themeEditor;

        function addCustomTheme(theme, userFriendlyThemeName) {
            // Add a localized user-friendly theme name
            enLocale.theme.names[theme.themeName] = userFriendlyThemeName;
            // Add the theme to the theme list
            themeTabPlugin.addTheme(theme);
        }

        // Add a custom theme to the Theme Editor
        addCustomTheme(customTheme, "Custom Theme");

        // Set the custom theme as an initial survey theme
        creator.theme = customTheme;

        // Register a custom theme with Dark and Light variations
        addCustomTheme(lightTheme, "Custom Theme with Dark/Light Variations");
        addCustomTheme(darkTheme, "Custom Theme with Dark/Light Variations");

        function askForThemeName(title, text, initialValue, callback) {
            const survey = new SurveyModel({
                showNavigationButtons: "none",
                showQuestionNumbers: "none",
                questionErrorLocation: "bottom",
                questions: [{
                    type: "text",
                    name: "title",
                    title: text,
                    defaultValue: initialValue.title,
                    isRequired: true,
                    requiredErrorText: "Theme title is required"
                }]
            });
            survey['isCompact'] = true;
            const popupContainer: HTMLElement = <HTMLElement>settings.environment.popupMountContainer;
            const popupViewModel = settings.showDialog({
                componentName: "survey",
                data: { model: survey},
                onApply: () => {
                    if (survey.completeLastPage()) {
                        callback(true, survey.data);
                        return true;
                    }
                    return false;
                },
                onCancel: () => {
                    callback(false);
                    return false;
                },
                title: title,
                displayMode: "popup",
                isFocusedContent: true,
                cssClass: "my-dialog"
            }, popupContainer);

            const toolbar = popupViewModel.footerToolbar;
            const applyBtn = toolbar.getActionById("apply");
            const cancelBtn = toolbar.getActionById("cancel");
            cancelBtn.title = surveyLocalization.getString("cancel");
            applyBtn.title = surveyLocalization.getString("ok");
        }

        let themeId = 1;
        function saveCustomTheme() {
            // Get the current theme
            const currentTheme = creator.theme;
            // Generate a unique theme name
            currentTheme.themeName += "_modified_" + themeId;
            // Generate a human-friendly theme name
            const themeTitle = "My Custom Theme " + themeId;
            askForThemeName("Do you want to save the current theme configuration?", "Enter a theme title", { title: themeTitle }, (confirm, data) => {
                if (confirm) {
                    addCustomTheme(currentTheme, data.title);
                    // Set the theme as a current theme; update the theme list and theme options
                    const themeBuilder = themeTabPlugin.model;
                    themeBuilder.setTheme(currentTheme);
                    themeId++;
                    updateCustomActions();
                    // ...
                    // (Optional) Save the theme to an external storage here
                    // ...
                }
            });
        };
        function deleteCurrentCustomTheme() {
            const currentTheme = creator.theme;
            const builtInThemeIndex = PredefinedThemes.indexOf(currentTheme.themeName!);
            if (builtInThemeIndex === -1) { // A custom theme
                const enLocale = editorLocalization.getLocale("en");
                settings.confirmActionAsync("Do you want to delete the following theme: \"" + enLocale.theme.names[currentTheme.themeName!] + "\"?", (confirm) => {
                    if (confirm) {
                        themeTabPlugin.removeTheme(currentTheme);
                        const themeBuilder = themeTabPlugin.model;
                        themeBuilder.setTheme({ themeName: "default" });
                        updateCustomActions();
                        // ...
                        // (Optional) Delete the theme from an external storage here
                        // ...
                    }
                });
            }
        };

        // Register custom actions to save and delete a current theme modification
        const saveThemeAction = new Action({
            id: "svd-save-custom-theme",
            title: "Add custom theme to the list",
            action: saveCustomTheme,
            iconName: "icon-saveas",
            showTitle: false
        });
        creator.toolbar.actions.push(saveThemeAction);

        const deleteThemeAction = new Action({
            id: "svd-delete-custom-theme",
            title: "Delete theme",
            action: deleteCurrentCustomTheme,
            iconName: "icon-delete",
            showTitle: false
        });
        creator.toolbar.actions.push(deleteThemeAction);

        function updateCustomActions() {
            const isThemeTab = creator.activeTab === "theme";
            saveThemeAction.visible = isThemeTab;
            const currentTheme = creator.theme;
            const isCustomTheme = PredefinedThemes.indexOf(currentTheme.themeName!) === -1;
            deleteThemeAction.visible = isThemeTab && isCustomTheme;
        }

        // Update the Save and Delete action visibility when a user selects a theme or activates another tab
        updateCustomActions();
        themeTabPlugin.onThemeSelected.add(updateCustomActions);
        creator.onActiveTabChanged.add(updateCustomActions);

        // Initialize a survey JSON
        creator.JSON = formJSON;

        // Activate the Themes Tab
        creator.activeTab = "theme";

        this.model = creator;
    }
}

Regarding the changes we made in our library: with the next release, the survey.isCompact option will be made public.