diff --git a/docs/guide/navbar.md b/docs/guide/navbar.md index 7646e1675..b93d14f3f 100644 --- a/docs/guide/navbar.md +++ b/docs/guide/navbar.md @@ -57,6 +57,8 @@ Unique identifier of the button, usefull when using the `navbar.getButton()` met Tooltip displayed when the mouse is over the button. +For translation purposes it can be a key in the main [`lang`](./config.md#lang) object. + #### `className` - type : `string` diff --git a/docs/plugins/settings.md b/docs/plugins/settings.md index a805c2385..3afa0ad8c 100644 --- a/docs/plugins/settings.md +++ b/docs/plugins/settings.md @@ -81,6 +81,8 @@ settings.addSetting({ }); ``` +For translation purposes, both `label` can be a key in the main [`lang`](../guide/config.md#lang) object. + ## Button badge A setting can also have a `badge` function, which return value will be used as a badge on the settings button itself. **Only one setting can declare a badge.** diff --git a/packages/core/src/buttons/AbstractButton.ts b/packages/core/src/buttons/AbstractButton.ts index 4f4285350..0a14b4546 100644 --- a/packages/core/src/buttons/AbstractButton.ts +++ b/packages/core/src/buttons/AbstractButton.ts @@ -101,7 +101,9 @@ export abstract class AbstractButton extends AbstractComponent { }); this.config = getConfig(config); - this.config.id = (this.constructor as typeof AbstractButton).id; + if (!config.id) { + this.config.id = (this.constructor as typeof AbstractButton).id; + } if (config.icon) { this.__setIcon(config.icon); @@ -110,7 +112,7 @@ export abstract class AbstractButton extends AbstractComponent { this.state.width = this.container.offsetWidth; if (this.config.title) { - this.container.title = this.config.title; + this.container.title = this.viewer.config.lang[this.config.title] ?? this.config.title; } else if (this.id && this.id in this.viewer.config.lang) { this.container.title = (this.viewer.config.lang as any)[this.id]; } diff --git a/packages/core/src/buttons/CustomButton.ts b/packages/core/src/buttons/CustomButton.ts index 7a779d3f8..32a0922eb 100644 --- a/packages/core/src/buttons/CustomButton.ts +++ b/packages/core/src/buttons/CustomButton.ts @@ -7,6 +7,7 @@ export class CustomButton extends AbstractButton { constructor(navbar: Navbar, config: NavbarCustomButton) { super(navbar, { + id: config.id ?? `psvButton-${Math.random().toString(36).substring(2)}`, className: `psv-custom-button ${config.className || ''}`, hoverScale: false, collapsable: config.collapsable !== false, @@ -16,12 +17,6 @@ export class CustomButton extends AbstractButton { this.customOnClick = config.onClick; - if (config.id) { - this.config.id = config.id; - } else { - this.config.id = 'psvButton-' + Math.random().toString(36).substring(2); - } - if (config.content) { if (typeof config.content === 'string') { this.container.innerHTML = config.content; diff --git a/packages/core/src/model.ts b/packages/core/src/model.ts index aeea0f7a9..cabfa723e 100644 --- a/packages/core/src/model.ts +++ b/packages/core/src/model.ts @@ -274,6 +274,7 @@ export type NavbarCustomButton = { id?: string; /** * Tooltip displayed when the mouse is over the button + * If can be a key in the global `lang` config */ title?: string; /** diff --git a/packages/map-plugin/src/components/MapCompassButton.ts b/packages/map-plugin/src/components/MapCompassButton.ts index aa5ebd0b6..a151316e5 100644 --- a/packages/map-plugin/src/components/MapCompassButton.ts +++ b/packages/map-plugin/src/components/MapCompassButton.ts @@ -6,7 +6,6 @@ export class MapCompassButton extends AbstractMapButton { constructor(map: MapComponent) { super(map, ButtonPosition.VERTICAL); - this.container.title = this.viewer.config.lang['mapNorth']; this.container.innerHTML = icon; this.container.querySelector('svg').style.width = '80%'; @@ -19,4 +18,8 @@ export class MapCompassButton extends AbstractMapButton { rotate(angle: number) { this.container.querySelector('svg').style.transform = `rotate3d(0, 0, 1, ${-angle}rad)`; } + + override update() { + this.container.title = this.viewer.config.lang['mapNorth']; + } } diff --git a/packages/map-plugin/src/components/MapComponent.ts b/packages/map-plugin/src/components/MapComponent.ts index cbe614a32..3d3418b1b 100644 --- a/packages/map-plugin/src/components/MapComponent.ts +++ b/packages/map-plugin/src/components/MapComponent.ts @@ -98,6 +98,7 @@ export class MapComponent extends AbstractComponent { window.addEventListener('touchend', this); canvasContainer.addEventListener('wheel', this); viewer.addEventListener(events.KeypressEvent.type, this); + viewer.addEventListener(events.ConfigChangedEvent.type, this); // map canvas this.canvas = document.createElement('canvas'); @@ -183,6 +184,14 @@ export class MapComponent extends AbstractComponent { e.preventDefault(); } break; + case events.ConfigChangedEvent.type: + if ((e as events.ConfigChangedEvent).containsOptions('lang')) { + this.resetButton?.update(); + this.closeButton?.update(); + this.compassButton?.update(); + this.maximizeButton?.update(); + } + break; case 'mousedown': { const event = e as MouseEvent; this.state.mouseX = event.clientX; diff --git a/packages/map-plugin/src/components/MapResetButton.ts b/packages/map-plugin/src/components/MapResetButton.ts index a75deca4e..22d687042 100644 --- a/packages/map-plugin/src/components/MapResetButton.ts +++ b/packages/map-plugin/src/components/MapResetButton.ts @@ -6,7 +6,6 @@ export class MapResetButton extends AbstractMapButton { constructor(map: MapComponent) { super(map, ButtonPosition.HORIZONTAL); - this.container.title = this.viewer.config.lang['mapReset']; this.container.innerHTML = reset; this.container.querySelector('svg').style.width = '80%'; @@ -15,4 +14,8 @@ export class MapResetButton extends AbstractMapButton { e.stopPropagation(); }); } + + override update() { + this.container.title = this.viewer.config.lang['mapReset']; + } } diff --git a/packages/plan-plugin/src/components/PlanComponent.ts b/packages/plan-plugin/src/components/PlanComponent.ts index 247cb88af..f0058be06 100644 --- a/packages/plan-plugin/src/components/PlanComponent.ts +++ b/packages/plan-plugin/src/components/PlanComponent.ts @@ -64,6 +64,7 @@ export class PlanComponent extends AbstractComponent { }); viewer.addEventListener(events.KeypressEvent.type, this); + viewer.addEventListener(events.ConfigChangedEvent.type, this); const mapContainer = document.createElement('div'); mapContainer.className = 'psv-plan__container'; @@ -132,6 +133,7 @@ export class PlanComponent extends AbstractComponent { cancelAnimationFrame(this.state.renderLoop); this.viewer.removeEventListener(events.KeypressEvent.type, this); + this.viewer.removeEventListener(events.ConfigChangedEvent.type, this); this.gallery?.removeEventListener('show-gallery', this); this.gallery?.removeEventListener('hide-gallery', this); @@ -150,6 +152,14 @@ export class PlanComponent extends AbstractComponent { e.preventDefault(); } break; + case events.ConfigChangedEvent.type: + if ((e as events.ConfigChangedEvent).containsOptions('lang')) { + this.resetButton?.update(); + this.closeButton?.update(); + this.layersButton?.update(); + this.maximizeButton?.update(); + } + break; case 'transitionstart': this.state.forceRender = true; break; diff --git a/packages/plan-plugin/src/components/PlanLayersButton.ts b/packages/plan-plugin/src/components/PlanLayersButton.ts index 5ad0902ec..34865b4e9 100644 --- a/packages/plan-plugin/src/components/PlanLayersButton.ts +++ b/packages/plan-plugin/src/components/PlanLayersButton.ts @@ -8,19 +8,14 @@ export class PlanLayersButton extends AbstractPlanButton { constructor(plan: PlanComponent) { super(plan, ButtonPosition.VERTICAL); - - const title = this.viewer.config.lang['mapLayers']; - - this.container.title = title; + this.container.innerHTML = layersIcon; this.select = document.createElement('select'); this.select.className = 'psv-plan__layers-select'; - this.select.setAttribute('aria-label', title) const placeholder = document.createElement('option'); placeholder.disabled = true; - placeholder.innerText = title; this.select.appendChild(placeholder); this.select.addEventListener('change', () => { @@ -33,6 +28,14 @@ export class PlanLayersButton extends AbstractPlanButton { this.hide(); } + override update() { + const title = this.viewer.config.lang['mapLayers']; + + this.container.title = title; + this.select.setAttribute('aria-label', title); + this.select.querySelector('option').innerText = title; + } + setLayers(layers: string[]) { this.show(); diff --git a/packages/plan-plugin/src/components/PlanResetButton.ts b/packages/plan-plugin/src/components/PlanResetButton.ts index 45ddf6265..76f80dcce 100644 --- a/packages/plan-plugin/src/components/PlanResetButton.ts +++ b/packages/plan-plugin/src/components/PlanResetButton.ts @@ -6,7 +6,6 @@ export class PlanResetButton extends AbstractPlanButton { constructor(plan: PlanComponent) { super(plan, ButtonPosition.HORIZONTAL); - this.container.title = this.viewer.config.lang['mapReset']; this.container.innerHTML = reset; this.container.querySelector('svg').style.width = '80%'; @@ -15,4 +14,8 @@ export class PlanResetButton extends AbstractPlanButton { e.stopPropagation(); }); } + + override update() { + this.container.title = this.viewer.config.lang['mapReset']; + } } diff --git a/packages/resolution-plugin/src/ResolutionPlugin.ts b/packages/resolution-plugin/src/ResolutionPlugin.ts index 1db78a019..382f0fc46 100644 --- a/packages/resolution-plugin/src/ResolutionPlugin.ts +++ b/packages/resolution-plugin/src/ResolutionPlugin.ts @@ -57,7 +57,7 @@ export class ResolutionPlugin extends AbstractPlugin { this.settings.addSetting({ id: ResolutionPlugin.id, type: 'options', - label: this.viewer.config.lang.resolution, + label: ResolutionPlugin.id, current: () => this.state.resolution, options: () => this.resolutions, apply: (resolution) => this.__setResolutionIfExists(resolution), diff --git a/packages/resolution-plugin/src/index.ts b/packages/resolution-plugin/src/index.ts index a2f4eba84..6d036581a 100644 --- a/packages/resolution-plugin/src/index.ts +++ b/packages/resolution-plugin/src/index.ts @@ -1,8 +1,9 @@ import { DEFAULTS } from '@photo-sphere-viewer/core'; import * as events from './events'; +import { ResolutionPlugin } from './ResolutionPlugin'; -DEFAULTS.lang.resolution = 'Quality'; +DEFAULTS.lang[ResolutionPlugin.id] = 'Quality'; -export { ResolutionPlugin } from './ResolutionPlugin'; +export { ResolutionPlugin }; export * from './model'; export { events }; diff --git a/packages/settings-plugin/src/SettingsComponent.ts b/packages/settings-plugin/src/SettingsComponent.ts index 1afbc9db1..9ce3a7413 100644 --- a/packages/settings-plugin/src/SettingsComponent.ts +++ b/packages/settings-plugin/src/SettingsComponent.ts @@ -137,11 +137,7 @@ export class SettingsComponent extends AbstractComponent { * Shows the list of options */ private __showSettings(focus: boolean) { - this.container.innerHTML = SETTINGS_TEMPLATE(this.plugin.settings, (setting) => { - const current = setting.current(); - const option = setting.options().find((opt) => opt.id === current); - return option?.label; - }); + this.container.innerHTML = SETTINGS_TEMPLATE(this.plugin.settings, this.viewer.config.lang); // must not focus during the initial transition if (focus) { @@ -153,11 +149,7 @@ export class SettingsComponent extends AbstractComponent { * Shows setting options panel */ private __showOptions(setting: OptionsSetting) { - const current = setting.current(); - - this.container.innerHTML = SETTING_OPTIONS_TEMPLATE(setting, (option) => { - return option.id === current; - }); + this.container.innerHTML = SETTING_OPTIONS_TEMPLATE(setting, this.viewer.config.lang); this.__focusFirstOption(); } diff --git a/packages/settings-plugin/src/constants.ts b/packages/settings-plugin/src/constants.ts index b50219a55..81427c0e7 100644 --- a/packages/settings-plugin/src/constants.ts +++ b/packages/settings-plugin/src/constants.ts @@ -1,9 +1,9 @@ -import { utils } from '@photo-sphere-viewer/core'; +import { utils, ViewerConfig } from '@photo-sphere-viewer/core'; import check from './icons/check.svg'; import chevron from './icons/chevron.svg'; import switchOff from './icons/switch-off.svg'; import switchOn from './icons/switch-on.svg'; -import { OptionsSetting, BaseSetting, SettingOption, ToggleSetting } from './model'; +import { OptionsSetting, Setting, ToggleSetting } from './model'; export const LOCAL_STORAGE_KEY = 'psvSettings'; export const ID_PANEL = 'settings'; @@ -17,14 +17,18 @@ export const OPTION_DATA_KEY = utils.dasherize(OPTION_DATA); /** * Setting item template, by type */ -export const SETTINGS_TEMPLATE_: Record = { - options: (setting: OptionsSetting, optionsCurrent: (s: OptionsSetting) => string) => ` -${setting.label} -${optionsCurrent(setting)} +export const SETTINGS_TEMPLATE_: Record = { + options: (setting: OptionsSetting, lang: ViewerConfig['lang']) => { + const current = setting.current(); + const option = setting.options().find((opt) => opt.id === current); + return ` +${lang[setting.label] ?? setting.label} +${option?.label ?? current} ${chevron} -`, - toggle: (setting: ToggleSetting) => ` -${setting.label} +`; + }, + toggle: (setting: ToggleSetting, lang: ViewerConfig['lang']) => ` +${lang[setting.label] ?? setting.label} ${setting.active() ? switchOn : switchOff} `, }; @@ -32,14 +36,14 @@ export const SETTINGS_TEMPLATE_: Record = { /** * Settings list template */ -export const SETTINGS_TEMPLATE = (settings: BaseSetting[], optionsCurrent: (s: OptionsSetting) => string) => ` +export const SETTINGS_TEMPLATE = (settings: Setting[], lang: ViewerConfig['lang']) => `
    ${settings .map( (setting) => `
  • - ${SETTINGS_TEMPLATE_[setting.type](setting as OptionsSetting, optionsCurrent)} + ${SETTINGS_TEMPLATE_[setting.type](setting, lang)}
  • ` ) @@ -50,12 +54,15 @@ export const SETTINGS_TEMPLATE = (settings: BaseSetting[], optionsCurrent: (s: O /** * Settings options template */ -export const SETTING_OPTIONS_TEMPLATE = (setting: OptionsSetting, optionActive: (o: SettingOption) => boolean) => ` +export const SETTING_OPTIONS_TEMPLATE = (setting: OptionsSetting, lang: ViewerConfig['lang']) => { + const current = setting.current(); + + return `
    • ${chevron} - ${setting.label} + ${lang[setting.label] ?? setting.label}
    • ${setting .options() @@ -63,11 +70,12 @@ export const SETTING_OPTIONS_TEMPLATE = (setting: OptionsSetting, optionActive: (option) => `
    • - ${optionActive(option) ? check : ''} - ${option.label} + ${option.id === current ? check : ''} + ${lang[option.label] ?? option.label}
    • ` ) .join('')}
    `; +}; diff --git a/packages/settings-plugin/src/model.ts b/packages/settings-plugin/src/model.ts index d448977cd..5b1148f5c 100644 --- a/packages/settings-plugin/src/model.ts +++ b/packages/settings-plugin/src/model.ts @@ -8,6 +8,7 @@ export type BaseSetting = { id: string; /** * label of the setting + * If can be a key in the global `lang` config */ label: string; /** @@ -64,6 +65,7 @@ export type SettingOption = { id: string; /** * label of the option + * If can be a key in the global `lang` config */ label: string; };