diff --git a/README.md b/README.md index 100e89e..d3c1e0a 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,7 @@ Inspired from : https://github.com/mo22/textstl - [ ] finalize font-picker tests - [x] Add somes options (~~multiple kerning~~, multiple suport spacing, round corner, hole in support) - [x] multi-line text ? (#33) -- [ ] save/load text (via URL encoded / local storage ?) -- [ ] (or) QP to save current settings ? +- [x] save/load text via QP to save current settings - [x] handle custom font ## Prerequisites diff --git a/app/components/save-modal.hbs b/app/components/save-modal.hbs new file mode 100644 index 0000000..2a14d61 --- /dev/null +++ b/app/components/save-modal.hbs @@ -0,0 +1,14 @@ + +

+ {{t "save_modal.title"}} +

+

+ {{t "save_modal.info" currentUrl=@currentUrl htmlSafe=true}} +

+ + + + +
\ No newline at end of file diff --git a/app/components/save-modal.ts b/app/components/save-modal.ts new file mode 100644 index 0000000..4cb66ad --- /dev/null +++ b/app/components/save-modal.ts @@ -0,0 +1,14 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +interface SaveModalArgs { + currentUrl: string; + onHide?: () => void; +} + +export default class SaveModal extends Component { + @action + selectAll({ target }: { target: HTMLTextAreaElement }) { + target.select(); + } +} diff --git a/app/components/ui/modal.hbs b/app/components/ui/modal.hbs new file mode 100644 index 0000000..cc5aea4 --- /dev/null +++ b/app/components/ui/modal.hbs @@ -0,0 +1,5 @@ +
+
+ {{yield}} +
+
\ No newline at end of file diff --git a/app/components/ui/modal.ts b/app/components/ui/modal.ts new file mode 100644 index 0000000..8d28c07 --- /dev/null +++ b/app/components/ui/modal.ts @@ -0,0 +1,19 @@ +import Component from '@glimmer/component'; +import { modifier } from 'ember-modifier'; +import UIkit from 'uikit'; +import { action } from '@ember/object'; + +interface ModalArgs { + onHide?: () => void; +} + +export default class UiModal extends Component { + openModal = modifier((element: HTMLDivElement) => { + UIkit.modal(element).show(); + }); + + @action + onHide() { + this.args.onHide?.(); + } +} diff --git a/app/controllers/app/generator.ts b/app/controllers/app/generator.ts index 0c824ad..9910e02 100644 --- a/app/controllers/app/generator.ts +++ b/app/controllers/app/generator.ts @@ -5,12 +5,27 @@ import FontManagerService from 'text2stl/services/font-manager'; import TextMakerService from 'text2stl/services/text-maker'; import STLExporterService from 'text2stl/services/stl-exporter'; import CounterService from 'text2stl/services/counter'; -import type ApplicationRoute from 'text2stl/routes/app/generator'; -import type IntlService from 'ember-intl/services/intl'; import { tracked } from '@glimmer/tracking'; import { trackedFunction } from 'ember-resources/util/function'; +import { Registry as Services } from '@ember/service'; + +import type ApplicationRoute from 'text2stl/routes/app/generator'; +import type IntlService from 'ember-intl/services/intl'; export default class GeneratorController extends Controller { + queryParams = ['modelSettings']; + + // Serialize model settings as QP + get modelSettings() { + return this.model?.serialize() ?? ''; + } + + set modelSettings(_value: string) { + // Nothing needed here, model is update : + // By route according to QP when route is loaded + // By components for later update + } + @service declare textMaker: TextMakerService; @service declare fontManager: FontManagerService; @@ -21,6 +36,8 @@ export default class GeneratorController extends Controller { @service('counter') declare counterService: CounterService; + @service declare router: Services['router']; + declare model: RouteModel; _gtag = gtag; @@ -74,6 +91,22 @@ export default class GeneratorController extends Controller { this.counterService.updateCounter(); this.stlExporter.downloadMeshAsSTL(mesh); } + + @tracked saveModalVisible = false; + + get currentUrl() { + return window.location.href; + } + + @action + showSaveModal() { + this.saveModalVisible = true; + } + + @action + hideSaveModal() { + this.saveModalVisible = false; + } } declare module '@ember/controller' { diff --git a/app/models/text-maker-settings.ts b/app/models/text-maker-settings.ts index 4ebb6c2..4fccbb3 100644 --- a/app/models/text-maker-settings.ts +++ b/app/models/text-maker-settings.ts @@ -19,7 +19,12 @@ interface TextMakerAdditionnalSettings { variantName: Variant; } -export class SupportPaddingSettings implements SupportPadding { +interface QPSerializable { + serialize(): string; + deserialize(json: string): void; +} + +export class SupportPaddingSettings implements SupportPadding, QPSerializable { @tracked top: number; @tracked bottom: number; @tracked left: number; @@ -31,9 +36,26 @@ export class SupportPaddingSettings implements SupportPadding { this.left = args.left; this.right = args.right; } + + serialize(): string { + return JSON.stringify({ + top: this.top, + bottom: this.bottom, + left: this.left, + right: this.right, + }); + } + + deserialize(json: string) { + const v = JSON.parse(json) as SupportPaddingSettings; + this.top = v.top; + this.bottom = v.bottom; + this.left = v.left; + this.right = v.right; + } } -export class HandleSettings implements Handle { +export class HandleSettings implements Handle, QPSerializable { @tracked type: 'hole' | 'handle' | 'none'; @tracked position: 'left' | 'top' | 'right' | 'bottom'; @tracked size: number; @@ -49,9 +71,30 @@ export class HandleSettings implements Handle { this.offsetX = args.offsetX; this.offsetY = args.offsetY; } + + serialize(): string { + return JSON.stringify({ + type: this.type, + position: this.position, + size: this.size, + size2: this.size2, + offsetX: this.offsetX, + offsetY: this.offsetY, + }); + } + + deserialize(json: string) { + const v = JSON.parse(json) as HandleSettings; + this.type = v.type; + this.position = v.position; + this.size = v.size; + this.size2 = v.size2; + this.offsetX = v.offsetX; + this.offsetY = v.offsetY; + } } -export default class TextMakerSettings implements TextMakerParameters { +export default class TextMakerSettings implements TextMakerParameters, QPSerializable { @tracked fontName: string; @tracked variantName?: Variant; @tracked text: string; @@ -86,4 +129,45 @@ export default class TextMakerSettings implements TextMakerParameters { args.handleSettings ?? textMakerDefault.handleSettings, ); } + + serialize(): string { + return JSON.stringify({ + fontName: this.fontName, + variantName: this.variantName, + text: this.text, + size: this.size, + customFont: this.customFont, + height: this.height, + spacing: this.spacing, + vSpacing: this.vSpacing, + alignment: this.alignment, + type: this.type, + supportHeight: this.supportHeight, + supportBorderRadius: this.supportBorderRadius, + supportPadding: this.supportPadding.serialize(), + handleSettings: this.handleSettings.serialize(), + }); + } + + deserialize(json: string) { + const v = JSON.parse(json) as Omit & { + supportPadding: string; + handleSettings: string; + }; + + this.fontName = v.fontName; + this.variantName = v.variantName; + this.text = v.text; + this.size = v.size; + this.customFont = v.customFont; + this.height = v.height; + this.spacing = v.spacing; + this.vSpacing = v.vSpacing; + this.alignment = v.alignment; + this.type = v.type; + this.supportHeight = v.supportHeight; + this.supportBorderRadius = v.supportBorderRadius; + this.supportPadding.deserialize(v.supportPadding); + this.handleSettings.deserialize(v.handleSettings); + } } diff --git a/app/routes/app/generator.ts b/app/routes/app/generator.ts index c17ffc0..702087e 100644 --- a/app/routes/app/generator.ts +++ b/app/routes/app/generator.ts @@ -7,13 +7,26 @@ const { } = config; export default class ApplicationRoute extends Route { - async model() { + queryParams = { + modelSettings: { + replace: true, // No history for model changes + }, + }; + + async model(params: { modelSettings: string }) { // Create a default settings for first rendering - return new TextMakerSettings({ + const model = new TextMakerSettings({ ...textMakerDefault, supportPadding: { ...textMakerDefault.supportPadding }, handleSettings: { ...textMakerDefault.handleSettings }, }); + + // Load model settings from QP if any + if (params.modelSettings) { + model.deserialize(params.modelSettings); + } + + return model; } afterModel() { diff --git a/app/templates/app/generator.hbs b/app/templates/app/generator.hbs index e2b292e..1bff11e 100644 --- a/app/templates/app/generator.hbs +++ b/app/templates/app/generator.hbs @@ -3,19 +3,29 @@
- {{! TODO: move this into basic button component ? }} - + + + + + + {{#if this.counter}}
@@ -36,4 +46,8 @@
- \ No newline at end of file + + +{{#if this.saveModalVisible}} + +{{/if}} \ No newline at end of file diff --git a/package.json b/package.json index f0f9053..9aeaf2c 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@types/qunit": "^2.11.1", "@types/rsvp": "^4.0.3", "@types/three": "~0.137.0", + "@types/uikit": "^3.14.1", "@typescript-eslint/eslint-plugin": "^6.3.0", "@typescript-eslint/parser": "^6.3.0", "broccoli-asset-rev": "^3.0.0", diff --git a/translations/en-us.yaml b/translations/en-us.yaml index 1e3b34c..f17a227 100644 --- a/translations/en-us.yaml +++ b/translations/en-us.yaml @@ -101,3 +101,8 @@ errors: unknown_font: The font is not in font list seo: description: Generate 3D printable text in one click ! Choose a font from more than 1000 fonts or use your own, different shape and settings & download an STL file to print. +save: save settings +save_modal: + title: Save your current settings + info: Bookmark the current URL or save the URL bellow to easily retrieve the current settings. + close: Close diff --git a/translations/fr-fr.yaml b/translations/fr-fr.yaml index 885aec5..132821d 100644 --- a/translations/fr-fr.yaml +++ b/translations/fr-fr.yaml @@ -98,3 +98,8 @@ errors: unknown_font: Police non trouvée dans la liste seo: description: Générer du texte 3D en 1 click ! Choisissez une police parmi plus de 1000 polices Google ou utilisez la vôtre, différentes formes et réglagles & téléchargez un fichier STL à imprimer. +save: enregister les réglages +save_modal: + title: Enregistrer vos réglages actuels + info: Ajouter l'URL actuelle de la page à vos favoris ou enregistrer l'URL ci dessous pour facilement retrouvé les réglages actuels. + close: Fermer diff --git a/yarn.lock b/yarn.lock index 1c1619a..2771305 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2100,6 +2100,11 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40" integrity sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg== +"@types/uikit@^3.14.1": + version "3.14.1" + resolved "https://registry.npmjs.org/@types/uikit/-/uikit-3.14.1.tgz#2dc17decee28c4fed2f96d9f67fc5ac5235794ca" + integrity sha512-DvyFy9PJjedeycR26zC71ri7OgpFkYa63xw6Cr9dOU2dlQWn8TmS8moHDFw3qsB0TDSJ3VaMQNYyE/lOH89e+w== + "@typescript-eslint/eslint-plugin@^6.3.0": version "6.3.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.3.0.tgz#e751e148aab7ccaf8a7bfd370f7ce9e6bdd1f3f4"