From 8898fea6b3fdc2a5f7244098a8212a08605ee9e9 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Fri, 1 Mar 2024 19:23:28 +0200 Subject: [PATCH 001/181] refactor search handler --- .../statsgrid/handler/SearchHandler.js | 783 +++++------------- 1 file changed, 228 insertions(+), 555 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index b502ec2eb9..4e6d35315b 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -1,19 +1,10 @@ import { StateHandler, controllerMixin, Messaging } from 'oskari-ui/util'; import { showMedataPopup } from '../components/description/MetadataPopup'; import { getHashForIndicator } from '../helper/StatisticsHelper'; -import { populateIndicatorOptions } from './SearchIndicatorOptionsHelper'; +import { populateIndicatorOptions, validateSelectionsForSearch } from './SearchIndicatorOptionsHelper'; import { getIndicatorMetadata } from './IndicatorHelper'; import { getDatasources, getUnsupportedDatasourceIds, getRegionsets } from '../helper/ConfigHelper'; - -const getValueAsArray = (selection) => { - if (selection === null || typeof selection === 'undefined') { - return []; - } - if (Array.isArray(selection)) { - return selection; - } - return [selection]; -}; +import { showSearchErrorPopup } from '../view/search/ErrorPopup'; class SearchController extends StateHandler { constructor (instance, service, stateHandler) { @@ -31,24 +22,28 @@ class SearchController extends StateHandler { return 'SearchHandler'; } - getInitialState () { + getInitialState (isReset) { const ds = getDatasources().length === 1 ? getDatasources()[0] : null; - // TODO: if ds => trigger populateIndicatorOptions => indicatorOptions + if (ds && !isReset) { + // prepopulate indicator options + setTimeout(() => this.fetchindicatorOptions(ds.id), 10000); + } return { searchTimeseries: false, selectedIndicators: [], regionsetFilter: [], selectedDatasource: ds ? ds.id : null, + selectedRegionset: null, disabledDatasources: [], - indicatorOptions: [], - indicatorParams: null, + indicatorOptions: ds && isReset ? this.getState().indicatorOptions : [], + indicatorParams: {}, // selectors, selected, selections isUserDatasource: ds ? ds.type === 'user' : false, loading: false }; } clearSearch () { - this.updateState(this.getInitialState()); + this.updateState(this.getInitialState(true)); } async fetchindicatorOptions () { @@ -65,18 +60,11 @@ class SearchController extends StateHandler { populateIndicatorOptions(selectedDatasource, response => { const { indicators = [], complete = false } = response; - const results = indicators.map(ind => { - return { - id: ind.id, - title: Oskari.getLocalized(ind.name), - regionsets: ind.regionsets - }; - }); this.updateState({ - indicatorOptions: this.validateIndicatorList(results), + indicatorOptions: this.validateIndicatorList(indicators), loading: !complete }); - if (complete && !isUserDatasource && !results.length) { + if (complete && !isUserDatasource && !indicators.length) { // show notification about empty indicator list for non-myindicators datasource Messaging.error(this.loc('errors.indicatorListIsEmpty')); } @@ -96,65 +84,33 @@ class SearchController extends StateHandler { validateIndicatorList (indicators = []) { const { regionsetFilter } = this.getState(); - const hasRegionSetRestriction = Array.isArray(regionsetFilter) && regionsetFilter.length > 0; - const supportsRegionset = (regionsets) => regionsetFilter.some(regionsetId => { - return regionsets.includes(regionsetId); - }); + const hasFilter = regionsetFilter.length > 0; + const isDisabled = (regionsets) => hasFilter && !regionsetFilter.some(regionsetId => regionsets.includes(regionsetId)); const results = indicators.map(ind => { - const value = { + return { ...ind, - disabled: false + disabled: isDisabled(ind.regionsets) }; - if (hasRegionSetRestriction) { - value.disabled = !supportsRegionset(ind.regionsets); - } - return value; }); results.sort((a, b) => a.disabled - b.disabled); return results; } setSearchTimeseries (searchTimeseries) { - this.updateState({ - searchTimeseries: !!searchTimeseries - }); - - const selectors = this.getState().indicatorParams?.selectors; - if (!selectors) { - return; - } - const keyWithTime = Object.keys(selectors).find((key) => selectors[key].time); - if (keyWithTime) { - let selected = selectors[keyWithTime].values[0].id; - if (searchTimeseries) { - if (selectors[keyWithTime].values?.length <= 1) { - Messaging.error(this.loc('errors.cannotDisplayAsSeries')); - this.updateState({ - searchTimeseries: false - }); - } else { - const selectValues = [ - selected, - selectors[keyWithTime].values[selectors[keyWithTime].values.length - 1].id - ]; - selected = [...selectValues].sort((a, b) => (a - b)); - } + const { selectors } = this.getState().indicatorParams; + if (selectors && searchTimeseries) { + const keyWithTime = Object.keys(selectors).find((key) => selectors[key].time); + if (!keyWithTime) { + Messaging.error(this.loc('errors.cannotDisplayAsSeries')); + this.updateState({ searchTimeseries: false }); + return; } - this.updateState({ - indicatorParams: { - ...this.getState().indicatorParams, - selected: { - ...this.getState().indicatorParams.selected, - [keyWithTime]: selected - } - } - }); - } else if (searchTimeseries) { - Messaging.error(this.loc('errors.cannotDisplayAsSeries')); - this.updateState({ - searchTimeseries: false - }); } + this.updateState({ searchTimeseries }); + this.initParamSelections(); + } + setSelectedRegionset (selectedRegionset) { + this.updateState({selectedRegionset}); } setRegionsetFilter (value) { @@ -168,7 +124,7 @@ class SearchController extends StateHandler { }); return; } - const disabledDatasources = getUnsupportedDatasourceIds(this.getState().regionsetFilter); + const disabledDatasources = getUnsupportedDatasourceIds(value); if (disabledDatasources.length) { if (disabledDatasources.includes(this.getState().selectedDatasource)) { this.clearSearch(); @@ -177,7 +133,8 @@ class SearchController extends StateHandler { this.updateState({ // reset any selected indicators because if they are disabled, user can't unselect them selectedIndicators: [], - indicatorParams: null, + indicatorParams: {}, + selectedRegionset: null, disabledDatasources, indicatorOptions: this.validateIndicatorList(this.getState().indicatorOptions) }); @@ -190,19 +147,26 @@ class SearchController extends StateHandler { selectedDatasource: ds.id || null, isUserDatasource: ds.type === 'user', selectedIndicators: [], - indicatorParams: null + indicatorParams: {}, + selectedRegionset: null }); this.fetchindicatorOptions(); } - setSelectedIndicators (value) { - this.updateState({ - selectedIndicators: value - }); + setSelectedIndicators (selectedIndicators) { + this.updateState({selectedIndicators}); if (this.metadataPopup) { this.openMetadataPopup(); } - this.fetchIndicatorParams(); + if (!selectedIndicators.length) { + this.updateState({ indicatorParams: {}, selectedRegionset: null }); + return; + } + if (selectedIndicators.length > 1) { + this.handleMultipleIndicatorParams(selectedIndicators); + } else { + this.handleSingleIndicatorParams(selectedIndicators[0]); + } } // indicator is state handler's selected indicator (on map) @@ -241,204 +205,139 @@ class SearchController extends StateHandler { this.metadataPopup = null; } - fetchIndicatorParams () { - if (!this.getState().selectedIndicators || this.getState().selectedIndicators.length === 0) { - this.updateState({ - indicatorParams: null - }); - return; - } + handleMultipleIndicatorParams (indicators) { + const combinedSelectors = {}; + const regionsets = new Set(); - if (this.getState().selectedIndicators.length > 1) { - this.handleMultipleIndicatorParams(); - } else { - this.handleSingleIndicatorParams(this.getState().selectedIndicators[0]); - } - } - - handleMultipleIndicatorParams () { - const indicators = this.getState().selectedIndicators.filter((n) => { return n !== ''; }); - const combinedValues = {}; - let regionsets = []; - - const addMissingElements = (list, newValues, propertyName) => { - if (!list) { - return [].concat(newValues); - } - - return list.concat(newValues.filter((value) => { - return !list.some((existingItem) => { - if (propertyName) { - return existingItem[propertyName] === value[propertyName]; - } - return existingItem === value; - }); - })); - }; const promise = new Promise((resolve, reject) => { indicators.forEach((indId, index) => { - this.handleSingleIndicatorParams(indId, (value) => { + this.handleSingleIndicatorParams(indId, (data) => { // include missing regionsets - regionsets = addMissingElements(regionsets, value.regionset); - Object.keys(value.selectors).forEach((selectorName) => { - if (!combinedValues[selectorName]) { - combinedValues[selectorName] = { - values: [], - time: !!value.selectors[selectorName].time - }; + data.regionsets.forEach(rs => regionsets.add(rs)); + Object.keys(data.selectors).forEach((name) => { + const { values, time } = data.selectors[name]; + const selector = combinedSelectors[name]; + if (!selector) { + combinedSelectors[name] = { values, time }; + } else { + const existingIds = selector.values.map(s => s.id); + const newValues = values.filter(v => !existingIds.includes(v.id)); + if (newValues.length) { + selector.values = [...selector.values, ...newValues].sort((a,b) => b.id - a.id); + } } - combinedValues[selectorName].values = addMissingElements(combinedValues[selectorName].values, value.selectors[selectorName].values, 'id'); }); }); if (index === indicators.length - 1) resolve(); }); }); promise.then(() => { - const data = { - datasrc: this.getState().selectedDatasource, - indicators: this.getState().selectedIndicators, - selectors: combinedValues, - regionset: regionsets, - selected: {} + const indicatorParams = { + selectors: combinedSelectors, + regionsets: [...regionsets] }; - data.selected = this.initParamSelections(data.selectors, data.regionset); - this.updateState({ - indicatorParams: data - }); + this.setIndicatorParams(indicatorParams); }); } - async handleSingleIndicatorParams (indId, cb) { - const panelLoc = this.loc('panels.newSearch'); + async handleSingleIndicatorParams (indicatorId, cb) { + const loc = this.loc('panels.newSearch.selectionValues'); + const { selectedDatasource } = this.getState(); try { - const result = await this.service.getIndicatorMetadata(this.getState().selectedDatasource, indId); - const combinedValues = {}; - result?.selectors.forEach((selector) => { - selector.allowedValues.forEach((val) => { - if (!combinedValues[selector.id]) { - combinedValues[selector.id] = { - values: [], - time: selector.time || false - }; - } - const name = val.name || val.id || val; - const optName = (panelLoc.selectionValues[selector.id] && panelLoc.selectionValues[selector.id][name]) ? panelLoc.selectionValues[selector.id][name] : name; - - const valObject = { - id: val.id || val, - title: optName - }; - combinedValues[selector.id].values.push(valObject); + // TODO: metadata selector allowedValue can be value or { name, id } => handle in one place and cache {id, title} or {value, label} + const meta = await this.service.getIndicatorMetadata(selectedDatasource, indicatorId); + const { selectors = [], regionsets = [] } = meta; + const combinedSelectors = {}; + selectors.forEach((selector) => { + const { id, allowedValues, time = false } = selector; + const values = allowedValues.map(val => { + // value or { name, id } + const valueId = val.id || val; + const name = val.name || valueId; + const title = loc[id]?.[name] || name; + return { id: valueId, title }; }); + combinedSelectors[id] = { values, time }; }); - if (result.regionsets.length === 0) { - Messaging.error('errors.regionsetsIsEmpty'); - } - - const data = { - datasrc: this.getState().selectedDatasource, - selectors: combinedValues, - indicators: this.getState().selectedIndicators, - regionset: result.regionsets, - selected: {} + const indicatorParams = { + selectors: combinedSelectors, + regionsets }; if (typeof cb === 'function') { - cb(data); + cb(indicatorParams); } else { - data.selected = this.initParamSelections(data.selectors, data.regionset); - this.updateState({ - indicatorParams: data - }); + this.setIndicatorParams(indicatorParams); } } catch (error) { Messaging.error(this.loc('errors.indicatorMetadataError')); } } - initParamSelections (selectors, regionsets) { - const { regionsetFilter, searchTimeseries } = this.getState(); + setIndicatorParams (params) { + const { indicatorParams } = this.getState(); + this.updateState({indicatorParams: {...indicatorParams, ...params}}); + this.initParamSelections(); + } + + initParamSelections () { + const { regionsetFilter, searchTimeseries, indicatorParams, selectedRegionset: rsId } = this.getState(); + const { selectors = {}, regionsets = [], selections: current = {}} = indicatorParams; const selections = {}; - Object.keys(selectors).forEach(key => { - let selected; - if (selectors[key].time) { - // time has multi-select => use array - selected = [selectors[key].values[0].id]; + const hasValidSelection = (key, allowed, time) => { + const cur = current[key]; + if (time) { if (searchTimeseries) { - if (selectors[key].values?.length <= 1) { - Messaging.error(this.loc('errors.cannotDisplayAsSeries')); - this.updateState({ - searchTimeseries: false - }); - } else { - const series = [selected, selectors[key].values[selectors[key].values.length - 1].id]; - selected = [...series].sort((a, b) => (a - b)); - } + return cur.length === 2 + } + return Array.isArray(cur) && cur.some(id => allowed.includes(id)); + } + return allowed.includes(cur); + }; + // use selectors to get rid of removed selectors' selections + Object.keys(selectors).forEach(key => { + const { time, values } = selectors[key]; + const allowed = values.map(val => val.id); + if (hasValidSelection(key, allowed, time)) { + // has valid selection already, use it + selections[key] = current[key]; + return; + } + const id = allowed[0]; + // time has multi-select => use array + const selected = time ? [id] : id; + if (time && searchTimeseries) { + if (allowed.length < 2) { + Messaging.error(this.loc('errors.cannotDisplayAsSeries')); + this.updateState({ searchTimeseries: false }); + } else { + const last = allowed[allowed.length - 1]; + selected.push(last); } - } else { - selected = selectors[key].values[0].id; } - selections[key] = selected; }); // metadata regionsets doesn't have same order than all regionsets // select first allowed value from all regionsets const allowedIds = regionsetFilter.length ? regionsets.filter(id => regionsetFilter.includes(id)) : regionsets; - selections.regionsets = getRegionsets().find(rs => allowedIds.includes(rs.id))?.id; - return selections; + const selectedRegionset = allowedIds.includes(rsId) ? rsId : getRegionsets().find(rs => allowedIds.includes(rs.id))?.id; + this.updateState({ selectedRegionset, indicatorParams: {...indicatorParams, selections }}); } - setParamSelection (param, value, index = null) { - let val; - if (index !== null) { - val = this.getState().indicatorParams.selected[param]; - val[index] = value; - } else { - val = value; - } + setParamSelection (param, value) { + const { indicatorParams } = this.getState(); + const { selections = {}} = indicatorParams; this.updateState({ indicatorParams: { - ...this.getState().indicatorParams, - selected: { - ...this.getState().indicatorParams.selected, - [param]: val - } + ...indicatorParams, + selections: {...selections, [param]: value } } }); } - getSearchValues () { - const data = { - datasource: this.getState().selectedDatasource, - indicator: this.getState().selectedIndicators, - regionset: this.getState().indicatorParams.selected.regionsets, - selections: { - ...this.getState().indicatorParams.selected - } - }; - - const keyWithTime = Object.keys(this.getState().indicatorParams.selected).find((key) => this.getState().indicatorParams.selectors[key].time); - - if (this.getState().searchTimeseries) { - data.selections[keyWithTime] = this.getState().indicatorParams.selected[keyWithTime][0]; - const values = this.getState().indicatorParams.selectors[keyWithTime].values.filter(val => val.id >= this.getState().indicatorParams.selected[keyWithTime][0] && val.id <= this.getState().indicatorParams.selected[keyWithTime][1]).reverse(); - data.series = { - id: keyWithTime, - values: values.map(val => val.id || val) - }; - } else if (keyWithTime) { - data.selections[keyWithTime] = this.getState().indicatorParams.selected[keyWithTime]; - } - - return data; - } - search () { - this.updateState({ - loading: true - }); - const searchData = this.getSearchValues(); - this.handleMultipleIndicatorsSearch(searchData); + this.updateState({ loading: true }); + this.handleMultipleIndicatorsSearch(); } /** @@ -449,95 +348,42 @@ class SearchController extends StateHandler { * This function rules out any unsupported selection parameters for each indicator and warns user of invalid values. * (f.ex.Selected year out of range) * + * + * @param {Object} commonSearchValues User's selected values from the search form + * * @param {Object} commonSearchValues User's selected values from the search form */ - async handleMultipleIndicatorsSearch (commonSearchValues) { - const indicators = getValueAsArray(commonSearchValues.indicator); - if (indicators.length === 0) { + async handleMultipleIndicatorsSearch () { + const state = this.getState(); + // Form should be disabled with invalid selections + if (!validateSelectionsForSearch(state)) { // nothing selected return; } - const refinedSearchValues = []; - const errorMap = new Map(); - const multiselectStatusMap = new Map(); - - // Overrides selection key and value from provided search values. - const getSearchWithModifiedParam = (values, paramKey, paramValue) => { - const modSelection = { ...values.selections, [paramKey]: paramValue }; - return { ...values, selections: modSelection }; - }; - - let metadataCounter = 0; - const checkDone = () => { - metadataCounter++; - if (metadataCounter === indicators.length) { - // All metadata requests have finished - this.addIndicatorsHavingData(refinedSearchValues, errorMap, multiselectStatusMap); - } - }; - - for (const indicator of indicators) { - if (indicator === '') { - checkDone(); - return; - } - // Overrides indicator array to make this search indicator specific. - const addSearchValues = values => { - refinedSearchValues.push({ ...values, indicator }); - }; + let refinedSearchValues = []; + const datasourceId = state.selectedDatasource; + for (const indicatorId of state.selectedIndicators) { // Get indicator metadata to check the search valididty try { - const metadata = await getIndicatorMetadata(commonSearchValues.datasource, indicator); - // Map possible errors by indicator name - const indicatorName = metadata && metadata.name ? Oskari.getLocalized(metadata.name) : indicator; + const metadata = await getIndicatorMetadata(datasourceId, indicatorId); if (!metadata) { - errorMap.set(indicatorName, { metadataNotFound: true }); - checkDone(); - return; + refinedSearchValues.push({ id: indicatorId, error: 'indicatorMetadataError'} ); + continue; } - const { error, multiselectStatus, ...searchValues } = this.getRefinedSearch(metadata, commonSearchValues); - - if (error) { - errorMap.set(indicatorName, error); - checkDone(); - return; - } - if (multiselectStatus) { - multiselectStatusMap.set(indicatorName, multiselectStatus); - } - // Save indicator name for possible error messaging. - searchValues.indicatorName = indicatorName; - - // Handle multiselect values - let multivalueParam; - let multivalueValues; - - if (searchValues.series) { - multivalueParam = searchValues.series.id; - multivalueValues = searchValues.series.values; - } else { - Object.keys(searchValues.selections).forEach(searchParamKey => { - const val = searchValues.selections[searchParamKey]; - if (!Array.isArray(val)) { - return; - } - multivalueParam = searchParamKey; - multivalueValues = val; - }); - } - // Add own search for each value of the serie / multiple select - if (multivalueParam && multivalueValues) { - multivalueValues.forEach(val => addSearchValues( - getSearchWithModifiedParam(searchValues, multivalueParam, val)) - ); - } else { - addSearchValues(searchValues); - } - checkDone(); + const values = this.getRefinedSearch(indicatorId, metadata); + refinedSearchValues = [...refinedSearchValues, ...values]; } catch (error) { Messaging.error(this.loc('errors.indicatorMetadataError')); } } + await this.addIndicators(refinedSearchValues.filter(i => !i.error), state.selectedRegionset); + // addIndicators sets error if failed to add => filter afterward + const errors = refinedSearchValues.filter(i => i.error || i.partialSeries); + if (errors.length) { + const isPartial = errors.length !== refinedSearchValues.length; + showSearchErrorPopup(errors, isPartial); + } + this.updateState({loading: false}); } /** @@ -545,227 +391,100 @@ class SearchController extends StateHandler { * Makes the actual selection validation based on the indicator metadata. * * @param {Object} metadata Indicator metadata - * @param {Object} commonSearchValues the search form values * @return {Object} search values suited for an indicator. * Adds "error" and "multiselectStatus" information to the search values. */ - getRefinedSearch (metadata, commonSearchValues) { - // Make a deep clone of search values - const indSearchValues = jQuery.extend(true, {}, commonSearchValues); - const { regionset, selections, series } = indSearchValues; + getRefinedSearch (id, metadata) { + const { indicatorParams, searchTimeseries, selectedRegionset, selectedDatasource } = this.getState(); + const { selections = {}, selectors = {}} = indicatorParams; + const keyWithTime = Object.keys(selections).find((key) => selectors[key].time); + const indSearchValues = { + id, + ds: selectedDatasource, + name: Oskari.getLocalized(metadata.name), // for showing error + selections: {} + }; - if (Array.isArray(metadata.regionsets) && !metadata.regionsets.includes(Number(regionset))) { - indSearchValues.error = { notAllowed: 'regionset' }; - return indSearchValues; - } - if (!selections) { - return indSearchValues; + if (Array.isArray(metadata.regionsets) && !metadata.regionsets.includes(selectedRegionset)) { + indSearchValues.error = 'notAllowedRegionset'; + return [indSearchValues]; } - - Object.keys(selections).forEach(selectionKey => { - const selector = metadata.selectors.find(selector => selector.id === selectionKey); - const checkNotAllowed = value => { - value = value.id || value; - return !selector.allowedValues.includes(value) && !selector.allowedValues.find(obj => obj.id === value); - }; - + const multiSelections = []; + Object.keys(selections).forEach(key => { + const selector = metadata.selectors.find(selector => selector.id === key); + // TODO: simplify after metadata response is unified + const checkAllowed = value => selector.allowedValues.includes(value) || selector.allowedValues.find(obj => obj.id === value); if (!selector) { - // Remove unsupported selectors silently - delete selections[selectionKey]; + indSearchValues.error = 'indicatorMetadataError'; return; } - const isSeriesSelection = series && series.id === selectionKey; - const value = isSeriesSelection ? series.values : selections[selectionKey]; - - if (!Array.isArray(value)) { - // Single option - if (checkNotAllowed(value)) { - indSearchValues.error = { notAllowed: selectionKey }; + const values = selections[key]; + // single + if (!Array.isArray(values)) { + indSearchValues.selections[key] = values; + if (!checkAllowed(values)) { + indSearchValues.error = 'invalidSelection'; } return; } - // Multiselect or series - // Filter out unsupported search param values - const notAllowed = value.filter(checkNotAllowed); - - // Set multiselect status for search - indSearchValues.multiselectStatus = { selector: selectionKey, invalid: notAllowed, requested: [...value] }; - - if (notAllowed.length === 0) { - // Selected values are valid - return; - } - if (notAllowed.length === value.length) { - // All selected values are out of range - delete selections[selectionKey]; - indSearchValues.error = { notAllowed: selectionKey }; + // series + if (key === keyWithTime && searchTimeseries) { + const [first, last] = values; + const range = selectors[key].values + .map(val => val.id) + .filter(id => id >= first && id <= last) + .reverse(); + const allowedValues = range.filter(checkAllowed); + indSearchValues.series = { + id: keyWithTime, + values: allowedValues + }; + if (range.length !== allowedValues.length) { + const invalids = range.filter(val => !checkAllowed(val)); + const all = range; + indSearchValues.partialSeries = { invalids, all }; + } + if (allowedValues < 2) { + indSearchValues.error = 'cannotDisplayAsSeries'; + } + indSearchValues.selections[key] = first; return; } - // Filter out unsupported search param values - if (isSeriesSelection) { - series.values = value.filter(cur => !notAllowed.includes(cur)); - } else { - selections[selectionKey] = value.filter(cur => !notAllowed.includes(cur)); - } - }); - return indSearchValues; - } - - /** - * @method addIndicatorsWithData - * Performs data check for each search. - * Adds indicators that have data. - * - * @param {Array} searchValues - * @param {Map} errors - * @param {Map} multiselectStatusMap - */ - addIndicatorsHavingData (searchValues, errors, multiselectStatusMap) { - const indicatorsHavingData = new Set(); - const successfullSearches = []; - const failedSearches = []; - let indicatorCounter = 0; - - const checkDone = () => { - indicatorCounter++; - if (indicatorCounter >= searchValues.length) { - // Handle indicators that failed the test - failedSearches.forEach(cur => this.updateSearchStatusWithFailure( - cur, - errors, - multiselectStatusMap, - successfullSearches, - indicatorsHavingData - )); - this.showSearchErrorMessages(successfullSearches, errors, multiselectStatusMap); - this.addIndicators(successfullSearches); - this.updateState({ - loading: false - }); - } - }; - const searchSuccessfull = search => { - if (!search.series || !indicatorsHavingData.has(search.indicator)) { - // Add series search only once - successfullSearches.push(search); - indicatorsHavingData.add(search.indicator); - } - checkDone(); - }; - const searchFailed = search => { - failedSearches.push(search); - checkDone(); - }; - - if (searchValues.length === 0) { - checkDone(); - return; - } - - // Run the searches to see if we get data from the service. - const batchSize = 1; - const batches = []; - let batch; - searchValues.forEach((search, index) => { - if (index % batchSize === 0) { - batch = []; - batches.push(batch); - } - batch.push(search); + multiSelections.push({ + key, + values, + invalid: values.filter(val => !checkAllowed(val)) + }); }); - const nextBatch = async () => { - const batch = batches.pop(); - if (batch) { - await consumeBatch(batch); - } - }; - const consumeBatch = async batch => { - for (const search of batch) { - // TODO: search values indicator => id, datasource => ds - const { datasource: ds, indicator: id, regionset, ...rest } = search; - try { - // TODO: addIndicator returns false if indicator couldn't be added - // these could be removed? - const data = await this.service.getIndicatorData({ id, ds, ...rest }, regionset); - if (!data) { - searchFailed(search); - return; - } - const enoughData = Object.values(data).some(val => !isNaN(val)); - if (!enoughData) { - searchFailed(search); - return; + // Add own search for each value of the multiple select + if (multiSelections.length) { + const indicators = []; + multiSelections.forEach(({ key, values, invalid }) => { + values.forEach(val => { + const selections = {...indSearchValues.selections, [key]: val}; + const indicator = {...indSearchValues, selections}; + if (invalid.includes(val)) { + indicator.error = 'invalidSelection'; } - searchSuccessfull(search); - } catch (error) { - searchFailed(search); - } - } - nextBatch(); - }; - nextBatch(); - } - - updateSearchStatusWithFailure (failedSearch, errors, multiselectStatusMap, successfullSearches, indicatorsHavingData) { - if (errors.has(failedSearch.indicatorName)) { - return; - } - if (!indicatorsHavingData.has(failedSearch.indicator)) { - errors.set(failedSearch.indicatorName, { datasetEmpty: true }); - return; - } - const multiselectStatus = multiselectStatusMap.get(failedSearch.indicatorName); - const invalidValue = failedSearch.selections[multiselectStatus.selector]; - multiselectStatus.invalid.push(invalidValue); - if (failedSearch.series) { - // Remove option from indicator's series - const seriesSearch = successfullSearches.find(cur => cur.indicator === failedSearch.indicator); - const index = seriesSearch.series.values.indexOf(invalidValue); - if (index !== -1) { - seriesSearch.series.values.splice(index, 1); - } - if (seriesSearch.series.values.length < 2) { - // Can't display as a serie. Downgrade to single indicator. - delete seriesSearch.series; - } - } - } - - showSearchErrorMessages (successfullSearches, errors, multiselectStatusMap) { - if (errors.size + multiselectStatusMap.size === 0) { - return; - } - - const indicatorMessages = []; - errors.forEach((value, indicatorName) => indicatorMessages.push(indicatorName)); - - multiselectStatusMap.forEach((status, indicatorName) => { - if (!errors.has(indicatorName) && status.invalid && status.invalid.length > 0) { - indicatorMessages.push(indicatorName + ' (' + this.getInvalidValuesStr(status.invalid, status.requested) + ')'); - } - }); - if (indicatorMessages.length > 0) { - const dialog = Oskari.clazz.create('Oskari.userinterface.component.Popup'); - const okBtn = dialog.createCloseButton('OK'); - let title; - if (successfullSearches.length > 0) { - title = this.loc('errors.onlyPartialDataForIndicators', { indicators: indicatorMessages.length }); - } else { - title = this.loc('errors.noDataForIndicators', { indicators: indicatorMessages.length }); - } - dialog.show(title, indicatorMessages.join('
'), [okBtn]); + indicators.push(indicator); + }); + }); + return indicators; } + return [indSearchValues]; } - async addIndicators (searchValues) { + async addIndicators (searchValues, regionset) { let latestHash = null; for (let i = 0; i < searchValues.length; i++) { - // TODO: search values indicator => id, datasource => ds - const { datasource: ds, indicator: id, regionset, ...rest } = searchValues[i]; - const indicator = { id, ds, ...rest }; + const indicator = searchValues[i]; indicator.hash = getHashForIndicator(indicator); - if (await this.stateHandler.addIndicator(indicator, regionset)) { + const success = await this.stateHandler.addIndicator(indicator, regionset); + // TODO: refactor to return error instead of boolean + if (success) { latestHash = indicator.hash; + } else { + indicator.error = 'failedToAdd'; } }; if (latestHash) { @@ -774,53 +493,6 @@ class SearchController extends StateHandler { } } - getInvalidValuesStr (invalids, all) { - if (!Array.isArray(invalids) || !Array.isArray(all)) { - return; - } - - let start; - let end; - let rangeCounter = 0; - - const reset = () => { - start = null; - end = null; - rangeCounter = 0; - }; - - const addRange = () => { - if (!rangeCounter) { - return 0; - } - if (rangeCounter >= 3) { - invalidRanges.push(start + ' - ' + end); - return; - } - invalidRanges.push(start); - if (start !== end) { - invalidRanges.push(end); - } - }; - - const invalidRanges = []; - all.sort(); - all.forEach(val => { - if (!invalids.includes(val)) { - addRange(); - reset(); - return; - } - start = start || val; - end = val; - rangeCounter++; - }); - if (rangeCounter !== 0) { - addRange(); - } - return invalidRanges.join(', '); - } - showIndicatorForm () { const { selectedDatasource, selectedIndicators } = this.getState(); const formHandler = this.instance.getViewHandler().formHandler; @@ -834,6 +506,7 @@ class SearchController extends StateHandler { const wrapped = controllerMixin(SearchController, [ 'setSearchTimeseries', + 'setSelectedRegionset', 'setRegionsetFilter', 'setSelectedDatasource', 'setSelectedIndicators', From eb90652efa486e1d9a2d2189499a81ed1000c8dc Mon Sep 17 00:00:00 2001 From: okauppinen Date: Fri, 1 Mar 2024 19:24:53 +0200 Subject: [PATCH 002/181] add error popup --- .../statsgrid/handler/SearchHandler.js | 3 - .../statsgrid/view/search/ErrorPopup.jsx | 87 +++++++++++++++++++ 2 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 bundles/statistics/statsgrid/view/search/ErrorPopup.jsx diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 4e6d35315b..7a6dfa1f10 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -348,9 +348,6 @@ class SearchController extends StateHandler { * This function rules out any unsupported selection parameters for each indicator and warns user of invalid values. * (f.ex.Selected year out of range) * - * - * @param {Object} commonSearchValues User's selected values from the search form - * * @param {Object} commonSearchValues User's selected values from the search form */ async handleMultipleIndicatorsSearch () { diff --git a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx new file mode 100644 index 0000000000..b9aa521605 --- /dev/null +++ b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx @@ -0,0 +1,87 @@ +import React from 'react'; +import styled from 'styled-components'; +import { showPopup } from 'oskari-ui/components/window'; +import { Message } from 'oskari-ui'; +import { LocaleProvider } from 'oskari-ui/util'; +import { BUNDLE_KEY } from '../../constants'; + +const POPUP_OPTIONS = { + id: BUNDLE_KEY + '-search-error' +}; + +const Content = styled.div` + padding: 20px; +`; + +const Popup = ({errors}) => { + // TODO: show reason for error if available => errorLoc[indicator.error] + return ( + + {errors.map((indicator,i) => { + const { name, partialSeries, selections } = indicator; + console.log(indicator.error); + const selection = partialSeries + ? getInvalidSerie(partialSeries) + : getSelection(selections); + return
{`${name} (${selection})`}
+ })} +
+ ); +}; + +export const showSearchErrorPopup = (errors, isPartial) => { + // no need to update + const titleKey = isPartial ? 'errors.onlyPartialDataForIndicators' : 'errors.noDataForIndicators'; + showPopup( + , + ( + + ), () => {}, POPUP_OPTIONS); +}; +const getSelection = selections => Object.values(selections).join(' / '); + +const getInvalidSerie = ({ invalids, all }) => { + if (!Array.isArray(invalids) || !Array.isArray(all)) { + return ''; + } + let start; + let end; + let rangeCounter = 0; + + const reset = () => { + start = null; + end = null; + rangeCounter = 0; + }; + + const addRange = () => { + if (!rangeCounter) { + return 0; + } + if (rangeCounter >= 3) { + invalidRanges.push(start + ' - ' + end); + return; + } + invalidRanges.push(start); + if (start !== end) { + invalidRanges.push(end); + } + }; + + const invalidRanges = []; + all.sort(); + all.forEach(val => { + if (!invalids.includes(val)) { + addRange(); + reset(); + return; + } + start = start || val; + end = val; + rangeCounter++; + }); + if (rangeCounter !== 0) { + addRange(); + } + return invalidRanges.join(', '); +}; From ac317b91d8c3367b74da5968adf69092aa8c5fe7 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Fri, 1 Mar 2024 19:25:22 +0200 Subject: [PATCH 003/181] add helper to validate selections --- .../handler/SearchIndicatorOptionsHelper.js | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js b/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js index a17c09b6ec..7865b3b6a5 100644 --- a/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js +++ b/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js @@ -113,6 +113,31 @@ export const populateIndicatorOptions = async (datasourceId, successCallback, er ------------------ /MAIN FUNCTIONALITY ------------------------ */ +/* +------------------ HELPERS ------------------------ +*/ +export const validateSelectionsForSearch = (state) => { + const { indicatorParams: { selections, selectors }, selectedRegionset, selectedIndicators } = state; + if (!selectedIndicators.length || !selectedRegionset || !selections) { + return false; + } + const keys = Object.keys(selections); + if (keys.length !== Object.keys(selectors).length) { + return false; + } + return keys.every(key => { + const selection = selections[key]; + if (Array.isArray(selection)) { + return selection.length > 0; + } + return selection === 0 || selection; + }); +}; + +/* +------------------ /HELPERS ------------------------ +*/ + /* ------------------ INTERNAL HELPERS ------------------------ */ From 63b5af1823f4fa07aeb1071df2efe896c0a06967 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Fri, 1 Mar 2024 19:28:23 +0200 Subject: [PATCH 004/181] apply changes to views --- .../statsgrid/view/search/IndicatorParams.jsx | 72 ++++++++++--------- .../statsgrid/view/search/SearchFlyout.jsx | 22 ++---- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx index 355ea42924..89283069f8 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx @@ -18,76 +18,82 @@ const StyledSelect = styled(Select)` width: 100%; `; -const TimeSeriesParams = ({fieldName, timeOptions, selectedValues, controller}) => ( +const TimeSeriesParams = ({name, options, selectedValues, controller}) => ( controller.setParamSelection(fieldName, value, 0)} + onChange={(value) => controller.setParamSelection(name, [value, selectedValues[1]])} /> controller.setParamSelection(fieldName, value, 1)} + onChange={(value) => controller.setParamSelection(name, [selectedValues[0], value])} /> ); -export const IndicatorParams = ({ params, allRegionsets = [], searchTimeseries, regionsetFilter, controller }) => { - const paramKeys = Object.keys(params.selectors); - const indicatorRegionsets = params.regionset || []; +export const IndicatorParams = ({ state, allRegionsets, controller }) => { + const { searchTimeseries, regionsetFilter, indicatorParams, selectedRegionset } = state; + const { selectors = {}, regionsets = [], selections = {} } = indicatorParams; + const paramKeys = Object.keys(selectors); + if (!paramKeys.length) { + return ( + + ); + } + const isDisabled = id => regionsetFilter.length && !regionsetFilter.includes(id); const regionsetOptions = allRegionsets - .filter(rs => indicatorRegionsets.includes(rs.id)) + .filter(rs => regionsets.includes(rs.id)) .map(rs => { - const opt = { value: rs.id, label: rs.name }; - if (regionsetFilter.length && !regionsetFilter.includes(rs.id)) { - opt.disabled = true; - } - return opt; + return { + value: rs.id, + label: rs.name, + disabled: isDisabled(rs.id) + }; }); return (
{paramKeys.map((param) => { - const selector = params.selectors[param]; - if (selector?.time && searchTimeseries) { - const timeOptions = selector?.values?.map(value => ({ value: value.id, label: value.title })); + const value = selections[param]; + const { values = [], time } = selectors[param] || {}; + const options = values.map(value => ({ value: value.id, label: value.title })); + if (time && searchTimeseries) { return ( + name={param} + options={options} + selectedValues={value} /> ); } return ( ({ value: value.id, label: value.title }))} - value={params.selected[param]} + options={options} + value={value} onChange={(value) => controller.setParamSelection(param, value)} - mode={selector?.time === true ? 'multiple' : ''} + mode={time ? 'multiple' : ''} /> ); })} - {regionsetOptions.length > 0 && ( - - - controller.setParamSelection('regionsets', value)} - /> - - )} + + + controller.setSelectedRegionset(value)} + /> +
); }; diff --git a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx index f277a6e6d1..a908f87a4e 100644 --- a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx +++ b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx @@ -7,6 +7,7 @@ import { LocaleProvider } from 'oskari-ui/util'; import { IndicatorParams } from './IndicatorParams'; import { IndicatorCollapse } from './IndicatorCollapse'; import { getDatasources, getRegionsets } from '../../helper/ConfigHelper'; +import { validateSelectionsForSearch } from '../../handler/SearchIndicatorOptionsHelper'; const BUNDLE_KEY = 'StatsGrid'; const Content = styled('div')` @@ -97,7 +98,7 @@ const SearchFlyout = ({ state, controller }) => { ({ value: i.id, label: i.title, disabled: !!i.disabled }))} + options={state?.indicatorOptions?.map(i => ({ value: i.id, label: i.name, disabled: !!i.disabled }))} placeholder={} disabled={!state?.indicatorOptions || state?.indicatorOptions?.length < 1} value={state?.selectedIndicators} @@ -122,18 +123,10 @@ const SearchFlyout = ({ state, controller }) => { )} - {!state.indicatorParams && ( - - )} - {state.indicatorParams && ( - - )} + { /> controller.search()} /> ); - if (state.loading) { return {Component}; } From 5c1d043f4210ed2d390d49077c4f136d4b6dfc2c Mon Sep 17 00:00:00 2001 From: okauppinen Date: Fri, 1 Mar 2024 19:44:22 +0200 Subject: [PATCH 005/181] fix time series init --- bundles/statistics/statsgrid/handler/SearchHandler.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 7a6dfa1f10..2e5926cd15 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -288,7 +288,7 @@ class SearchController extends StateHandler { const cur = current[key]; if (time) { if (searchTimeseries) { - return cur.length === 2 + return cur.length === 2 && cur[0] < cur[1]; } return Array.isArray(cur) && cur.some(id => allowed.includes(id)); } @@ -311,8 +311,7 @@ class SearchController extends StateHandler { Messaging.error(this.loc('errors.cannotDisplayAsSeries')); this.updateState({ searchTimeseries: false }); } else { - const last = allowed[allowed.length - 1]; - selected.push(last); + selected.unshift(allowed[allowed.length - 1]); } } selections[key] = selected; From c9b6c330f759c9afb83d5c94f4124c7afd700a66 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Fri, 1 Mar 2024 19:45:02 +0200 Subject: [PATCH 006/181] remove console.log --- bundles/statistics/statsgrid/view/search/ErrorPopup.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx index b9aa521605..61405acb1a 100644 --- a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx +++ b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx @@ -19,7 +19,6 @@ const Popup = ({errors}) => { {errors.map((indicator,i) => { const { name, partialSeries, selections } = indicator; - console.log(indicator.error); const selection = partialSeries ? getInvalidSerie(partialSeries) : getSelection(selections); From 56f9138c948cd8831ef3e12cf17d1b45683676c6 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Thu, 7 Mar 2024 09:10:53 +0200 Subject: [PATCH 007/181] move error messages from legend to errors --- .../classification/legend/InactiveLegend.jsx | 2 +- .../manualClassification/HistogramForm.jsx | 4 ++-- bundles/statistics/statsgrid/resources/locale/en.js | 13 +++++-------- bundles/statistics/statsgrid/resources/locale/fi.js | 13 +++++-------- bundles/statistics/statsgrid/resources/locale/fr.js | 13 +++++-------- bundles/statistics/statsgrid/resources/locale/is.js | 13 +++++-------- bundles/statistics/statsgrid/resources/locale/ru.js | 13 +++++-------- bundles/statistics/statsgrid/resources/locale/sv.js | 13 +++++-------- 8 files changed, 33 insertions(+), 51 deletions(-) diff --git a/bundles/statistics/statsgrid/components/classification/legend/InactiveLegend.jsx b/bundles/statistics/statsgrid/components/classification/legend/InactiveLegend.jsx index 21b4be9a90..204be80e5b 100644 --- a/bundles/statistics/statsgrid/components/classification/legend/InactiveLegend.jsx +++ b/bundles/statistics/statsgrid/components/classification/legend/InactiveLegend.jsx @@ -10,7 +10,7 @@ const Container = styled.div` export const InactiveLegend = ({ error }) => { return ( - + ); }; diff --git a/bundles/statistics/statsgrid/components/manualClassification/HistogramForm.jsx b/bundles/statistics/statsgrid/components/manualClassification/HistogramForm.jsx index 1494225c92..a549eff616 100644 --- a/bundles/statistics/statsgrid/components/manualClassification/HistogramForm.jsx +++ b/bundles/statistics/statsgrid/components/manualClassification/HistogramForm.jsx @@ -30,7 +30,7 @@ const Form = ({ if (!indicator) { return ( - + ); } @@ -74,7 +74,7 @@ const Form = ({ onChange={method => controller.updateClassification({ method })} options={getMethodOptions(indicator)}/>
- {error && } + {error && } diff --git a/bundles/statistics/statsgrid/resources/locale/en.js b/bundles/statistics/statsgrid/resources/locale/en.js index e1285b2ff9..7fa2c80063 100644 --- a/bundles/statistics/statsgrid/resources/locale/en.js +++ b/bundles/statistics/statsgrid/resources/locale/en.js @@ -74,13 +74,6 @@ Oskari.registerLocalization({ 'removeSource': 'Remove data', 'noIndicators': 'Start using thematic maps by adding an indicator on the map.' }, - 'legend': { - 'title': 'Classification', - 'noActive': 'Data was not selected, select data to see map classification.', - 'noEnough': 'The data is too small to be classified, try different data or change limitings.', - 'noData': 'Data is not available for the selected point in time.', - 'cannotCreateLegend': 'Legend cannot be created by chosen values, try different values.' - }, 'series': { 'speed': { 'label': 'Animation speed', @@ -177,7 +170,11 @@ Oskari.registerLocalization({ 'myIndicatorInvalidData': 'Data has invalid values.', 'cannotDisplayAsSeries': 'Indicator cannot be inspected as a series.', 'noDataForIndicators': 'Service did not return data for {indicators, plural, one {the indicator} other {indicators}}', - 'onlyPartialDataForIndicators': 'Service did not return all data for {indicators, plural, one {the indicator} other {indicators}}' + 'onlyPartialDataForIndicators': 'Service did not return all data for {indicators, plural, one {the indicator} other {indicators}}', + 'noActiveLegend': 'Data was not selected, select data to see map classification.', + 'noEnough': 'The data is too small to be classified, try different data or change limitings.', + 'noData': 'Data is not available for the selected point in time.', + 'cannotCreateLegend': 'Legend cannot be created by chosen values, try different values.' }, 'missing': { 'regionsetName': 'Unknown', diff --git a/bundles/statistics/statsgrid/resources/locale/fi.js b/bundles/statistics/statsgrid/resources/locale/fi.js index 4907d5471a..6317e94c93 100644 --- a/bundles/statistics/statsgrid/resources/locale/fi.js +++ b/bundles/statistics/statsgrid/resources/locale/fi.js @@ -74,13 +74,6 @@ Oskari.registerLocalization({ 'removeSource': 'Poista aineisto', 'noIndicators': 'Aloita teemakartan käyttö lisäämällä kartalle indikaattori.' }, - 'legend': { - 'title': 'Luokittelu', - 'noActive': 'Ei valittuna aineistoa, valitse aineisto nähdäksesi kartan luokittelun.', - 'noEnough': 'Aineisto on liian pieni luokittelun muodostamiseksi, kokeile eri aineistoa tai muuta rajauksia.', - 'noData': 'Aineistoa ei ole saatavilla valitsemaltasi ajankohdalta', - 'cannotCreateLegend': 'Legendaa ei saada tehtyä valitsemillasi arvoilla, kokeile eri arvoilla.' - }, 'series': { 'speed': { 'label': 'Animaationopeus', @@ -178,7 +171,11 @@ Oskari.registerLocalization({ 'myIndicatorInvalidData': 'Aineistossa on virheellisiä arvoja.', 'cannotDisplayAsSeries': 'Indikaattoria ei voida tarkastella sarjana', 'noDataForIndicators': 'Palvelusta ei saatu tietoja {indicators, plural, one {indikaattorille} other {indikaattoreille}}', - 'onlyPartialDataForIndicators': 'Palvelusta ei saatu kaikkia tietoja {indicators, plural, one {indikaattorille} other {indikaattoreille}}' + 'onlyPartialDataForIndicators': 'Palvelusta ei saatu kaikkia tietoja {indicators, plural, one {indikaattorille} other {indikaattoreille}}', + 'noActiveLegend': 'Ei valittuna aineistoa, valitse aineisto nähdäksesi kartan luokittelun.', + 'noEnough': 'Aineisto on liian pieni luokittelun muodostamiseksi, kokeile eri aineistoa tai muuta rajauksia.', + 'noData': 'Aineistoa ei ole saatavilla valitsemaltasi ajankohdalta', + 'cannotCreateLegend': 'Legendaa ei saada tehtyä valitsemillasi arvoilla, kokeile eri arvoilla.' }, 'missing': { 'regionsetName': 'Tuntematon', diff --git a/bundles/statistics/statsgrid/resources/locale/fr.js b/bundles/statistics/statsgrid/resources/locale/fr.js index fc8e777294..1169bec218 100644 --- a/bundles/statistics/statsgrid/resources/locale/fr.js +++ b/bundles/statistics/statsgrid/resources/locale/fr.js @@ -74,13 +74,6 @@ Oskari.registerLocalization( "orderByDescending": "Trier en ordre décroissant", "removeSource": "Supprimer les données" }, - "legend": { - "title": "Classification", - "noActive": "Aucune donnée sélectionnée. Veuillez sélectionner les données pour voir la classification de la carte.", - "noEnough": "Les données sont trop petites pour être classifiées. Veuillez essayer des données différentes ou modifier les limites.", - "noData": "Les données ne sont pas accessibles pour le moment précis sélectionné.", - "cannotCreateLegend": "Impossible de créer la légende à l'aide des valeurs choisies. Veuillez essayer des valeurs différentes." - }, "series": { "speed": { "label": "Vitesse d'animation", @@ -171,7 +164,11 @@ Oskari.registerLocalization( "myIndicatorYearInput": "Le champ Année ne peut pas être vide.", "myIndicatorRegionselect": "Le champ Sélection de la région ne peut pas être vide.", "myIndicatorDatasource": "Le champ Source de données est vide.", - "cannotDisplayAsSeries": "Impossible d'analyser l'indicateur en tant que série." + "cannotDisplayAsSeries": "Impossible d'analyser l'indicateur en tant que série.", + "noActiveLegend": "Aucune donnée sélectionnée. Veuillez sélectionner les données pour voir la classification de la carte.", + "noEnough": "Les données sont trop petites pour être classifiées. Veuillez essayer des données différentes ou modifier les limites.", + "noData": "Les données ne sont pas accessibles pour le moment précis sélectionné.", + "cannotCreateLegend": "Impossible de créer la légende à l'aide des valeurs choisies. Veuillez essayer des valeurs différentes." }, "datacharts": { "flyout": "Données recherchées", diff --git a/bundles/statistics/statsgrid/resources/locale/is.js b/bundles/statistics/statsgrid/resources/locale/is.js index 6dd6d0ebe8..75c7fff0ab 100644 --- a/bundles/statistics/statsgrid/resources/locale/is.js +++ b/bundles/statistics/statsgrid/resources/locale/is.js @@ -73,13 +73,6 @@ Oskari.registerLocalization({ "orderByDescending": "Sort descending", "removeSource": "Remove data" }, - "legend": { - "title": "Classification", - "noActive": "Data was not selected, select data to see map classification.", - "noEnough": "The data is too small to be classified, try different data or change limitings.", - "noData": "Data is not available for the selected point in time.", - "cannotCreateLegend": "Legend cannot be created by chosen values, try different values." - }, "series": { "speed": { "label": "Animation speed", @@ -172,7 +165,11 @@ Oskari.registerLocalization({ "myIndicatorRegionselect": "Regionselect cannot be empty.", "myIndicatorDatasource": "Datasource is empty.", "cannotDisplayAsSeries": "Indicator cannot be inspected as a series.", - "noDataForIndicators": "Service did not return data for {indicators, plural, one {the indicator} other {indicators}}" + "noDataForIndicators": "Service did not return data for {indicators, plural, one {the indicator} other {indicators}}", + "noActiveLegend": "Data was not selected, select data to see map classification.", + "noEnough": "The data is too small to be classified, try different data or change limitings.", + "noData": "Data is not available for the selected point in time.", + "cannotCreateLegend": "Legend cannot be created by chosen values, try different values." }, "missing": { "regionsetName": "Unknown" diff --git a/bundles/statistics/statsgrid/resources/locale/ru.js b/bundles/statistics/statsgrid/resources/locale/ru.js index 6b63133394..ee43aabdfb 100644 --- a/bundles/statistics/statsgrid/resources/locale/ru.js +++ b/bundles/statistics/statsgrid/resources/locale/ru.js @@ -80,13 +80,6 @@ Oskari.registerLocalization( "orderByDescending": "Сортировать по убыванию", "removeSource": "Удалить данные" }, - "legend": { - "title": "Классификация", - "noActive": "Данные не были выбраны, выберите данные для просмотра классификации карт.", - "noEnough": "Данные слишком малы для классификации, попробуйте другие данные или измените ограничения.", - "noData": "Данные недоступны для выбранного момента времени.", - "cannotCreateLegend": "Условные обозначения не могут быть созданы выбранными значениями, попробуйте разные значения." - }, "series": { "speed": { "label": "Скорость анимации", @@ -171,7 +164,11 @@ Oskari.registerLocalization( "datasetDelete": "Ошибка удаления набора данных.", "indicatorSave": "Индикатор сохранения ошибок", "myIndicatorYearInput": "Поле год не может быть пустым.", - "myIndicatorRegionselect": "Поле выбора региона не может быть пустым." + "myIndicatorRegionselect": "Поле выбора региона не может быть пустым.", + "noActiveLegend": "Данные не были выбраны, выберите данные для просмотра классификации карт.", + "noEnough": "Данные слишком малы для классификации, попробуйте другие данные или измените ограничения.", + "noData": "Данные недоступны для выбранного момента времени.", + "cannotCreateLegend": "Условные обозначения не могут быть созданы выбранными значениями, попробуйте разные значения." }, "datacharts": { "flyout": "Искомые данные", diff --git a/bundles/statistics/statsgrid/resources/locale/sv.js b/bundles/statistics/statsgrid/resources/locale/sv.js index 8ab283c2d1..edae1e0476 100644 --- a/bundles/statistics/statsgrid/resources/locale/sv.js +++ b/bundles/statistics/statsgrid/resources/locale/sv.js @@ -74,13 +74,6 @@ Oskari.registerLocalization({ 'removeSource': 'Radera datamängd', 'noIndicators': 'Börja användningen av tematiska kartor med att lägga till en indikator på kartan.' }, - 'legend': { - 'title': 'Klassificering', - 'noActive': 'Inga valda datamängder, välj datamängd för att se kartans klassificering.', - 'noEnough': 'Datamängden är för liten för att klassificeras, försök en annan datamängd eller avgränsning.', - 'noData': 'Ingen data vid den valda tidspunkten.', - 'cannotCreateLegend': 'Teckenförklaringen kan inte skapas utgående från de valda värden, vänligen försök andra värden.' - }, 'series': { 'speed': { 'label': 'Animeringshastighet', @@ -177,7 +170,11 @@ Oskari.registerLocalization({ 'myIndicatorInvalidData': 'Datamängder har ogiltiga värden.', 'cannotDisplayAsSeries': 'Indikatorn kan inte inspekteras som en serie.', 'noDataForIndicators': 'Tjänsten returnerade ingen data för {indicators, plural, one {indikatorn} other {indikatorer}}', - 'onlyPartialDataForIndicators': 'Tjänsten returnerade inte alla data för {indicators, plural, one {indikatorn} other {indikatorer}}' + 'onlyPartialDataForIndicators': 'Tjänsten returnerade inte alla data för {indicators, plural, one {indikatorn} other {indikatorer}}', + 'noActiveLegend': 'Inga valda datamängder, välj datamängd för att se kartans klassificering.', + 'noEnough': 'Datamängden är för liten för att klassificeras, försök en annan datamängd eller avgränsning.', + 'noData': 'Ingen data vid den valda tidspunkten.', + 'cannotCreateLegend': 'Teckenförklaringen kan inte skapas utgående från de valda värden, vänligen försök andra värden.' }, 'missing': { 'regionsetName': 'Okänd', From 766958bf90af29ac1705233442be9bcb2078c706 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Thu, 7 Mar 2024 09:58:13 +0200 Subject: [PATCH 008/181] process metadata --- .../statsgrid/handler/IndicatorHelper.js | 142 +++++++----------- 1 file changed, 54 insertions(+), 88 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/IndicatorHelper.js b/bundles/statistics/statsgrid/handler/IndicatorHelper.js index 0f2f65e058..0c887317d0 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorHelper.js +++ b/bundles/statistics/statsgrid/handler/IndicatorHelper.js @@ -1,6 +1,7 @@ import { getHash, getHashForIndicator, getDataProviderKey, populateData, populateSeriesData } from '../helper/StatisticsHelper'; import { updateIndicatorListInCache, removeIndicatorFromCache } from './SearchIndicatorOptionsHelper'; import { getRegionsAsync } from '../helper/RegionsHelper'; +import { BUNDLE_KEY } from '../constants'; const statsGridLocale = Oskari.getMsg.bind(null, 'StatsGrid'); // cache storage object @@ -14,54 +15,48 @@ const getDataCacheKey = (indicator, regionsetId) => { // for guest user's own indicators const updateIndicatorMetadataInCache = (indicator, regionsetId) => { - const { id, ds, selections = {} } = indicator; - const cacheKey = getMetaCacheKey(ds, id); + const { selections = {}, ...restToStore } = indicator; + const cacheKey = getMetaCacheKey(indicator.ds, indicator.id); const cachedResponse = indicatorMetadataStore[cacheKey]; - - const lang = Oskari.getLang(); + // user indicator has actually only one selector 'year' which is time param const selectors = Object.keys(selections).map(id => { const value = selections[id]; return { id, - name: id, time: true, - allowedValues: [{ name: value, id: value }] + values: [{ value, label: value }] }; }); + // store metadata like processMetadata if (!cachedResponse) { indicatorMetadataStore[cacheKey] = { + ...restToStore, public: true, - id, - ds, - name: { [lang]: indicator.name }, - description: { [lang]: indicator.description }, - source: { [lang]: indicator.source }, regionsets: regionsetId ? [regionsetId] : [], selectors }; return; } - cachedResponse.name[lang] = indicator.name; - cachedResponse.description[lang] = indicator.description; - cachedResponse.source[lang] = indicator.source; + cachedResponse.name = indicator.name; + cachedResponse.description = indicator.description; + cachedResponse.source = indicator.source; if (regionsetId && !cachedResponse.regionsets.includes(regionsetId)) { cachedResponse.regionsets.push(regionsetId); } // update allowed values const { selectors: cachedSelectors } = cachedResponse; selectors.forEach(selector => { - const { id, allowedValues } = selector; - const cached = cachedSelectors.find(s => s.id === id); + const cached = cachedSelectors.find(s => s.id === selector.id); if (!cached) { cachedSelectors.push(selector); return; } - allowedValues.forEach(value => { - if (cached.allowedValues.some(v => v.id === value.id)) { + selector.values.forEach(value => { + if (cached.values.some(v => v.id === value.id)) { // already added return; } - cached.allowedValues.push(value); + cached.values.push(value); }); }); }; @@ -72,6 +67,11 @@ const flushIndicatorMetadataCache = (indicator) => { indicatorMetadataStore[cacheKey] = null; }; +export const getCachedMetadata = (datasourceId, indicatorId) => { + const cacheKey = getMetaCacheKey(datasourceId, indicatorId); + return indicatorMetadataStore[cacheKey] || {}; +}; + export const getIndicatorMetadata = async (datasourceId, indicatorId) => { if (!datasourceId || !indicatorId) { throw new Error('Datasource or indicator missing'); @@ -97,16 +97,47 @@ export const getIndicatorMetadata = async (datasourceId, indicatorId) => { throw new Error(response.statusText); } const result = await response.json(); + const processed = processMetadata(result); // cache results - indicatorMetadataStore[cacheKey] = result; - return result; + indicatorMetadataStore[cacheKey] = processed; + return processed; } catch (error) { + // TODO: indicatorMetadataError throw new Error(error); } }; +const processMetadata = (meta) => { + const locValues = Oskari.getMsg(BUNDLE_KEY, 'panels.newSearch.selectionValues'); + const locParams = Oskari.getMsg(BUNDLE_KEY, 'parameters'); + const metaSelectors = meta.selectors || []; + const selectors = []; + metaSelectors.forEach(metaSelector => { + const { id, allowedValues, time = false } = metaSelector; + const values = allowedValues.map(val => { + // value or { name, id } + const value = val.id || val; + const name = val.name || value; + const label = locValues[id]?.[name] || name; + // use value, label to use as Select option + return { value, label }; + }); + const label = locParams[id] || id; + const selector = { id, values, time, label}; + if (time) { + selectors.unshift(selector); + } else { + selectors.push(selector); + } + }); + const name = Oskari.getLocalized(meta.name) || ''; + const source = Oskari.getLocalized(meta.source) || ''; + const description = Oskari.getLocalized(meta.description) || ''; + return {...meta, name, source, description, selectors}; +}; export const getDataForIndicator = async (indicator, regionset) => { const regions = await getRegionsAsync(regionset); + // TODO: regionsetsIsEmpty let data = {}; const fractionDigits = indicator?.classification?.fractionDigits; if (indicator.series) { @@ -116,11 +147,13 @@ export const getDataForIndicator = async (indicator, regionset) => { const value = values[i]; const selections = { ...indicator.selections, [id]: value }; const rawData = await getIndicatorData({ ...indicator, selections }, regionset); + // TODO: noData dataBySelection[value] = rawData; } data = populateSeriesData(dataBySelection, regions, regionset, fractionDigits); } else { const rawData = await getIndicatorData(indicator, regionset); + // TODO: noData data = populateData(rawData, regions, regionset, fractionDigits); } return data; @@ -296,70 +329,3 @@ export const deleteIndicator = async (indicator, regionsetId) => { throw new Error('Error on server'); } }; - -export const getIndicatorObjectToAdd = async (datasourceId, indicatorId, selections, series) => { - const { name, selectors: availableIndicatorSelectors, source } = await getIndicatorMetadata(datasourceId, indicatorId); - const localizedIndicatorName = Oskari.getLocalized(name); - const uiLabels = []; - Object.keys(selections).forEach(selectorId => { - const currentParameter = selections[selectorId]; - const indicatorSelector = getIndicatorSelector(availableIndicatorSelectors, selectorId); - if (!indicatorSelector) { - // function received a selection that the current indicator doesn't support - // this can happen when adding multiple indicators in one go - // selections are combined from all indicators for the UI when adding multiple indicators at once - // -> not all indicators might have all those parameters/selections -> just skip it in that case - return; - } - let selectorValue = indicatorSelector.allowedValues.find(v => currentParameter === v.id || currentParameter === v); - if (typeof selectorValue !== 'object') { - // normalize single value to object with id and value - selectorValue = { - id: selectorValue, - name: selectorValue - }; - } - const label = getSelectorValueLocalization(indicatorSelector.id, selectorValue.id) || selectorValue.name; - uiLabels.push({ - selectorId, - id: selectorValue.id, - label - }); - }); - let selectorsFormatted = ' (' + uiLabels.map(l => l.label).join(' / ') + ')'; - if (series) { - const range = String(series.values[0]) + ' - ' + String(series.values[series.values.length - 1]); - selectorsFormatted = range + ' ' + selectorsFormatted; - } - return { - datasource: Number(datasourceId), - indicator: indicatorId, - // selections are the params like year=2020 etc - selections, - // series is the time range for time series selection - series, - hash: getHash(datasourceId, indicatorId, selections, series), - labels: { - indicator: localizedIndicatorName, - source: Oskari.getLocalized(source), - params: selectorsFormatted, - full: localizedIndicatorName + ' ' + selectorsFormatted, - paramsAsObject: uiLabels - } - }; -}; - -const getSelectorValueLocalization = (paramName, paramValue) => { - const localeKey = 'panels.newSearch.selectionValues.' + paramName + '.' + paramValue; - const value = statsGridLocale(localeKey); - if (value === localeKey) { - // returned the localeKey -> we don't have a localized label for this - return null; - } - return value; -}; - -// returns the selector object from available IF it is supported -const getIndicatorSelector = (availableSelectors, selectorId) => { - return availableSelectors.find(s => s.id === selectorId); -}; From 2314b6c8037eb8ceae2bfdd7ee594fad018878ec Mon Sep 17 00:00:00 2001 From: okauppinen Date: Thu, 7 Mar 2024 10:13:40 +0200 Subject: [PATCH 009/181] use processed metadata --- .../description/MetadataCollapse.jsx | 26 +-- .../description/MetadataContent.jsx | 12 +- .../components/description/MetadataPopup.jsx | 12 +- .../statsgrid/handler/IndicatorFormHandler.js | 25 +-- .../statsgrid/handler/SearchHandler.js | 151 ++++++------------ .../statsgrid/helper/RegionsHelper.js | 2 +- .../statsgrid/helper/StatisticsHelper.js | 81 ++++------ 7 files changed, 110 insertions(+), 199 deletions(-) diff --git a/bundles/statistics/statsgrid/components/description/MetadataCollapse.jsx b/bundles/statistics/statsgrid/components/description/MetadataCollapse.jsx index ad2bff3277..cb841a3958 100644 --- a/bundles/statistics/statsgrid/components/description/MetadataCollapse.jsx +++ b/bundles/statistics/statsgrid/components/description/MetadataCollapse.jsx @@ -2,25 +2,27 @@ import React from 'react'; import PropTypes from 'prop-types'; import { MetadataContent } from './MetadataContent'; import { Collapse, CollapsePanel, Message } from 'oskari-ui'; +import { getCachedMetadata } from '../../handler/IndicatorHelper'; -export const MetadataCollapse = ({ data = [] }) => { +export const MetadataCollapse = ({ indicators }) => { + // Only selected indiator (map or search) has button to show popup => metadata is always cached + const data = indicators.map(ind => getCachedMetadata(ind.ds, ind.id)); if (!data.length) { return (); } // Description can include HTML so well have to wrap it as HTML content... return ( - Oskari.getLocalized(item.name))}> - { data.map(metadata => { - const name = Oskari.getLocalized(metadata.name) || ''; + item.id)}> + { data.map(({ name, description, source, metadata, id }) => { if (!name) { return null; } return ( - + ); @@ -30,10 +32,8 @@ export const MetadataCollapse = ({ data = [] }) => { }; MetadataCollapse.propTypes = { - data: PropTypes.arrayOf(PropTypes.shape({ - name: PropTypes.object, - desc: PropTypes.object, - source: PropTypes.object, - metadata: PropTypes.object + indicators: PropTypes.arrayOf(PropTypes.shape({ + ds: PropTypes.number, + id: PropTypes.string })) }; diff --git a/bundles/statistics/statsgrid/components/description/MetadataContent.jsx b/bundles/statistics/statsgrid/components/description/MetadataContent.jsx index fc362c8bb6..1801c0a44c 100644 --- a/bundles/statistics/statsgrid/components/description/MetadataContent.jsx +++ b/bundles/statistics/statsgrid/components/description/MetadataContent.jsx @@ -4,16 +4,14 @@ import { Message } from 'oskari-ui'; import styled from 'styled-components'; export const MetadataContent = ({description, source, metadata = {}}) => { - const desc = Oskari.getLocalized(description) || ''; - const datasource = Oskari.getLocalized(source) || ''; - if (!desc) { + if (!description) { return (); } // Description can include HTML so well have to wrap it as HTML content... return ( -

+

- +

@@ -32,7 +30,7 @@ const DataLabel = ({labelKey, value}) => { } MetadataContent.propTypes = { - description: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), - source: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), + description: PropTypes.string.isRequired, + source: PropTypes.string.isRequired, metadata: PropTypes.object }; diff --git a/bundles/statistics/statsgrid/components/description/MetadataPopup.jsx b/bundles/statistics/statsgrid/components/description/MetadataPopup.jsx index 25f5647575..470bf2d55e 100644 --- a/bundles/statistics/statsgrid/components/description/MetadataPopup.jsx +++ b/bundles/statistics/statsgrid/components/description/MetadataPopup.jsx @@ -10,19 +10,19 @@ const POPUP_OPTIONS = { id: BUNDLE_KEY + '-metadata' }; -export const showMedataPopup = (data = [], onClose) => { +export const showMedataPopup = (indicators, onClose) => { const controls = showPopup( - , + , ( - + ), onClose, POPUP_OPTIONS); return { ...controls, - update: (data) => { + update: (indicators) => { controls.update( - , + , ( - + ) ); controls.bringToTop(); diff --git a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js index 97c9a1324c..547a89a23a 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js +++ b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js @@ -80,28 +80,11 @@ class IndicatorFormController extends StateHandler { async getIndicatorDatasets (indicator) { try { - const ind = await getIndicatorMetadata(indicator.ds, indicator.id); + const { selectors, regionsets, name, source, description } = await getIndicatorMetadata(indicator.ds, indicator.id); const datasets = []; - for (const sel of ind?.selectors) { - for (const regionset of ind.regionsets) { - for (const value of sel.allowedValues) { - const data = {}; - if (typeof value === 'object') { - data[sel.id] = value.id; - } else { - data[sel.id] = value; - } - data.regionset = regionset; - datasets.push(data); - } - } - } - this.updateState({ - datasets, - indicatorName: Oskari.getLocalized(ind.name), - indicatorDescription: Oskari.getLocalized(ind.description), - indicatorSource: Oskari.getLocalized(ind.source) - }); + // create dataset for every value and regionset compination like {year: 2024, regionset: 2036} + selectors.forEach(s => s.values.forEach(valObj => regionsets.forEach(regionset => datasets.push({[s.id]: valObj.value, regionset})))); + this.updateState({ datasets, indicator: {...indicator, name, source, description } }); } catch (error) { Messaging.error(Oskari.getMsg('StatsGrid', 'errors.indicatorMetadataError')); } diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 59ad9eefce..b4d5eb6b8d 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -1,5 +1,4 @@ import { StateHandler, controllerMixin, Messaging } from 'oskari-ui/util'; -import { showMedataPopup } from '../components/description/MetadataPopup'; import { getHashForIndicator } from '../helper/StatisticsHelper'; import { populateIndicatorOptions, validateSelectionsForSearch } from './SearchIndicatorOptionsHelper'; import { getIndicatorMetadata } from './IndicatorHelper'; @@ -12,7 +11,6 @@ class SearchController extends StateHandler { this.instance = instance; this.stateHandler = stateHandler; this.setState(this.getInitialState()); - this.metadataPopup = null; this.loc = Oskari.getMsg.bind(null, 'StatsGrid'); this.log = Oskari.log('Oskari.statistics.statsgrid.SearchHandler'); } @@ -87,7 +85,8 @@ class SearchController extends StateHandler { } }, error => { - Messaging.error(this.loc(error)); + const errorMsg = this.loc('errors')[error] || this.loc('errors.indicatorListError'); + Messaging.error(errorMsg); this.updateState({ loading: false }); }); } catch (error) { @@ -116,7 +115,7 @@ class SearchController extends StateHandler { setSearchTimeseries (searchTimeseries) { const { selectors } = this.getState().indicatorParams; if (selectors && searchTimeseries) { - const keyWithTime = Object.keys(selectors).find((key) => selectors[key].time); + const keyWithTime = selectors.find(sel => sel.time)?.id; if (!keyWithTime) { Messaging.error(this.loc('errors.cannotDisplayAsSeries')); this.updateState({ searchTimeseries: false }); @@ -172,9 +171,6 @@ class SearchController extends StateHandler { setSelectedIndicators (selectedIndicators) { this.updateState({selectedIndicators}); - if (this.metadataPopup) { - this.openMetadataPopup(); - } if (!selectedIndicators.length) { this.updateState({ indicatorParams: {}, selectedRegionset: null }); return; @@ -186,61 +182,24 @@ class SearchController extends StateHandler { } } - // indicator is state handler's selected indicator (on map) - async openMetadataPopup (indicator = null) { - const datasource = indicator ? indicator.ds : this.getState().selectedDatasource; - const indicatorIds = indicator ? [indicator.id] : this.getState().selectedIndicators; - - const data = await this.prepareMetadataPopupData(datasource, indicatorIds); - if (this.metadataPopup) { - this.metadataPopup.update(data); - } else { - this.metadataPopup = showMedataPopup(data, () => this.closeMetadataPopup()); - } - } - - async prepareMetadataPopupData (datasource, indicatorIds) { - const result = []; - for (const id of indicatorIds) { - try { - const data = await getIndicatorMetadata(datasource, id); - if (!data) { - return; - } - result.push(data); - } catch (error) { - return; - } - } - return result; - } - - closeMetadataPopup () { - if (this.metadataPopup) { - this.metadataPopup.close(); - } - this.metadataPopup = null; - } - handleMultipleIndicatorParams (indicators) { - const combinedSelectors = {}; + const combinedSelectors = []; const regionsets = new Set(); const promise = new Promise((resolve, reject) => { indicators.forEach((indId, index) => { - this.handleSingleIndicatorParams(indId, (data) => { + this.handleSingleIndicatorParams(indId, (params) => { // include missing regionsets - data.regionsets.forEach(rs => regionsets.add(rs)); - Object.keys(data.selectors).forEach((name) => { - const { values, time } = data.selectors[name]; - const selector = combinedSelectors[name]; - if (!selector) { - combinedSelectors[name] = { values, time }; + params.regionsets.forEach(rs => regionsets.add(rs)); + params.selectors.forEach((selector) => { + const existing = combinedSelectors.find(s => s.id === selector.id); + if (!existing) { + combinedSelectors.push(selector); } else { - const existingIds = selector.values.map(s => s.id); - const newValues = values.filter(v => !existingIds.includes(v.id)); + const existingIds = existing.values.map(s => s.id); + const newValues = selector.values.filter(v => !existingIds.includes(v.id)); if (newValues.length) { - selector.values = [...selector.values, ...newValues].sort((a,b) => b.id - a.id); + existing.values = [...existing.values, ...newValues].sort((a,b) => b.value - a.value); } } }); @@ -258,29 +217,11 @@ class SearchController extends StateHandler { } async handleSingleIndicatorParams (indicatorId, cb) { - const loc = this.loc('panels.newSearch.selectionValues'); const { selectedDatasource } = this.getState(); try { - // TODO: metadata selector allowedValue can be value or { name, id } => handle in one place and cache {id, title} or {value, label} const meta = await getIndicatorMetadata(selectedDatasource, indicatorId); const { selectors = [], regionsets = [] } = meta; - const combinedSelectors = {}; - selectors.forEach((selector) => { - const { id, allowedValues, time = false } = selector; - const values = allowedValues.map(val => { - // value or { name, id } - const valueId = val.id || val; - const name = val.name || valueId; - const title = loc[id]?.[name] || name; - return { id: valueId, title }; - }); - combinedSelectors[id] = { values, time }; - }); - - const indicatorParams = { - selectors: combinedSelectors, - regionsets - }; + const indicatorParams = { selectors, regionsets }; if (typeof cb === 'function') { cb(indicatorParams); } else { @@ -299,30 +240,29 @@ class SearchController extends StateHandler { initParamSelections () { const { regionsetFilter, searchTimeseries, indicatorParams, selectedRegionset: rsId } = this.getState(); - const { selectors = {}, regionsets = [], selections: current = {}} = indicatorParams; + const { selectors = [], regionsets = [], selections: current = {}} = indicatorParams; const selections = {}; - const hasValidSelection = (key, allowed, time) => { - const cur = current[key]; + const hasValidSelection = (id, allowed, time) => { + const cur = current[id]; if (time) { if (searchTimeseries) { return cur.length === 2 && cur[0] < cur[1]; } - return Array.isArray(cur) && cur.some(id => allowed.includes(id)); + return Array.isArray(cur) && cur.some(value => allowed.includes(value)); } return allowed.includes(cur); }; // use selectors to get rid of removed selectors' selections - Object.keys(selectors).forEach(key => { - const { time, values } = selectors[key]; - const allowed = values.map(val => val.id); - if (hasValidSelection(key, allowed, time)) { + selectors.forEach(({ time, values, id }) => { + const allowed = values.map(v => v.value); + if (hasValidSelection(id, allowed, time)) { // has valid selection already, use it - selections[key] = current[key]; + selections[id] = current[id]; return; } - const id = allowed[0]; + const value = allowed[0]; // time has multi-select => use array - const selected = time ? [id] : id; + const selected = time ? [value] : value; if (time && searchTimeseries) { if (allowed.length < 2) { Messaging.error(this.loc('errors.cannotDisplayAsSeries')); @@ -331,7 +271,7 @@ class SearchController extends StateHandler { selected.unshift(allowed[allowed.length - 1]); } } - selections[key] = selected; + selections[id] = selected; }); // metadata regionsets doesn't have same order than all regionsets // select first allowed value from all regionsets @@ -409,12 +349,12 @@ class SearchController extends StateHandler { */ getRefinedSearch (id, metadata) { const { indicatorParams, searchTimeseries, selectedRegionset, selectedDatasource } = this.getState(); - const { selections = {}, selectors = {}} = indicatorParams; - const keyWithTime = Object.keys(selections).find((key) => selectors[key].time); + const { selections = {}, selectors = []} = indicatorParams; + const keyWithTime = Object.keys(selections).find((key) => selectors.find(s=> s.id === key && s.time)); const indSearchValues = { id, ds: selectedDatasource, - name: Oskari.getLocalized(metadata.name), // for showing error + name: metadata.name, // for showing error selections: {} }; @@ -424,10 +364,10 @@ class SearchController extends StateHandler { } const multiSelections = []; Object.keys(selections).forEach(key => { - const selector = metadata.selectors.find(selector => selector.id === key); - // TODO: simplify after metadata response is unified - const checkAllowed = value => selector.allowedValues.includes(value) || selector.allowedValues.find(obj => obj.id === value); - if (!selector) { + const metaSelector = metadata.selectors.find(selector => selector.id === key); + // use metadata for validity check. Params have combined selectors. + const checkAllowed = value => metaSelector.values.find(obj => obj.value === value); + if (!metaSelector) { indSearchValues.error = 'indicatorMetadataError'; return; } @@ -443,9 +383,10 @@ class SearchController extends StateHandler { // series if (key === keyWithTime && searchTimeseries) { const [first, last] = values; - const range = selectors[key].values - .map(val => val.id) - .filter(id => id >= first && id <= last) + // should always find values as keyWithTime is selected from selectors + const range = selectors.find(s=> s.id === keyWithTime).values + .map(val => val.value) + .filter(val => val >= first && val <= last) .reverse(); const allowedValues = range.filter(checkAllowed); indSearchValues.series = { @@ -492,12 +433,11 @@ class SearchController extends StateHandler { for (let i = 0; i < searchValues.length; i++) { const indicator = searchValues[i]; indicator.hash = getHashForIndicator(indicator); - const success = await this.stateHandler.addIndicator(indicator, regionset); - // TODO: refactor to return error instead of boolean + const { success, error } = await this.stateHandler.addIndicator(indicator, regionset); if (success) { latestHash = indicator.hash; - } else { - indicator.error = 'failedToAdd'; + } else if (error) { + indicator.error = error; } }; if (latestHash) { @@ -508,13 +448,24 @@ class SearchController extends StateHandler { showIndicatorForm () { const { selectedDatasource, selectedIndicators } = this.getState(); - const formHandler = this.instance.getViewHandler().formHandler; + const formHandler = this.instance.getViewHandler()?.formHandler; if (!formHandler) { return; } const indicator = selectedIndicators.length === 1 ? selectedIndicators[0] : null; formHandler.showIndicatorPopup(selectedDatasource, indicator); } + + openMetadataPopup (indicator) { + let indicators = []; + if (indicator) { + indicators = [indicator]; + } else { + const { selectedIndicators, selectedDatasource: ds } = this.getState(); + indicators = selectedIndicators.map(id =>({ id, ds })); + } + this.instance.getViewHandler()?.openMetadataPopup(indicators); + } } const wrapped = controllerMixin(SearchController, [ diff --git a/bundles/statistics/statsgrid/helper/RegionsHelper.js b/bundles/statistics/statsgrid/helper/RegionsHelper.js index 666ad10e86..4158b1ec90 100644 --- a/bundles/statistics/statsgrid/helper/RegionsHelper.js +++ b/bundles/statistics/statsgrid/helper/RegionsHelper.js @@ -12,7 +12,6 @@ export const getRegions = (regionsetId) => { export const getRegionsAsync = async (regionset) => { if (!regionset) { - // log error message throw new Error('Regionset id missing'); } const cachedResponse = regionsStore[regionset]; @@ -39,6 +38,7 @@ export const getRegionsAsync = async (regionset) => { regionsStore[regionset] = onlyWithNames; return onlyWithNames; } catch (error) { + // TODO: regionsDataError throw new Error(error); } }; diff --git a/bundles/statistics/statsgrid/helper/StatisticsHelper.js b/bundles/statistics/statsgrid/helper/StatisticsHelper.js index 8df01c7dfb..04b88ad51f 100644 --- a/bundles/statistics/statsgrid/helper/StatisticsHelper.js +++ b/bundles/statistics/statsgrid/helper/StatisticsHelper.js @@ -146,57 +146,36 @@ export const formatData = (data, classification) => { }; export const getUILabels = (ind, metadata) => { - const selectionValues = Oskari.getMsg('StatsGrid', 'panels.newSearch.selectionValues'); + if (!metadata) { + return; + } const { selections, series } = ind; - const getError = () => ({ - error: true, - indicator: '', - params: '', - full: '', - range: '', - paramsList: [] - }); - try { - if (!metadata) { - return getError(); + const { name, selectors, source } = metadata; + const paramsList = []; + Object.keys(selections).forEach(id => { + const selector = selectors.find(s => s.id === id); + if (!selector) { + return; } - const { name, selectors, source } = metadata; - const uiLabels = []; - Object.keys(selections).forEach(key => { - const selection = selections[key]; - const foundSelector = selectors.find(s => s.id === key); - if (foundSelector) { - const value = foundSelector.allowedValues.find(v => selection === v.id || selection === v); - const isObject = typeof value === 'object'; - const selector = foundSelector.id; - const id = isObject ? value.id : value; - let label; - if (isObject) { - label = value.name; - } else { - // try finding localization for the param - label = Oskari.util.keyExists(selectionValues, selector + '.' + value) ? selectionValues[selector][value] : value; - } - uiLabels.push({ selector, id, label }); - } - }); - const localizedName = Oskari.getLocalized(name); - let selectorsFormatted = '(' + uiLabels.map(l => l.label).join(' / ') + ')'; - const range = series ? series.values[0] + ' - ' + series.values[series.values.length - 1] : ''; - if (range) { - selectorsFormatted = range + ' ' + selectorsFormatted; + const selection = selections[id]; + const { value, label } = selector.values.find(v => v.value === selection) || {}; + if (value) { + paramsList.push({ id, value, label }); } - return { - indicator: localizedName, - source: Oskari.getLocalized(source), - params: selectorsFormatted, - full: localizedName + ' ' + selectorsFormatted, - paramsList: uiLabels, - range - }; - } catch (error) { - return getError(); + }); + let selectorsFormatted = '(' + paramsList.map(l => l.label).join(' / ') + ')'; + const range = series ? series.values[0] + ' - ' + series.values[series.values.length - 1] : ''; + if (range) { + selectorsFormatted = range + ' ' + selectorsFormatted; } + return { + indicator: name, + source, + params: selectorsFormatted, + full: name + ' ' + selectorsFormatted, + paramsList, + range + }; }; // series needs to update labels on selected change @@ -206,11 +185,11 @@ export const getUpdatedLabels = (labels, selections) => { return labels; } // Doesn't validate selectors - const paramsList = Object.keys(selections).map(selector => { - const value = selections[selector]; + const paramsList = Object.keys(selections).map(id => { + const value = selections[id]; return { - selector, - id: value, + id, + value, label: value }; }); From 4c0611559ebd98db15d2c7e3a0448400e595e39b Mon Sep 17 00:00:00 2001 From: okauppinen Date: Thu, 7 Mar 2024 10:14:54 +0200 Subject: [PATCH 010/181] return error --- .../statsgrid/handler/StatisticsHandler.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/StatisticsHandler.js b/bundles/statistics/statsgrid/handler/StatisticsHandler.js index 8067f2bb21..cfc02370d3 100644 --- a/bundles/statistics/statsgrid/handler/StatisticsHandler.js +++ b/bundles/statistics/statsgrid/handler/StatisticsHandler.js @@ -219,8 +219,11 @@ class StatisticsController extends StateHandlerBase { // remove indicator first to get updated indicator and data this.removeIndicator(indicator); } - await this.addIndicator(indicator, regionset); - this.setActiveIndicator(indicator.hash); + const response = await this.addIndicator(indicator, regionset); + if (response.success) { + this.setActiveIndicator(indicator.hash); + } + return response; } async addIndicator (indicator, regionset) { @@ -229,27 +232,24 @@ class StatisticsController extends StateHandlerBase { if (regionset !== this.getState().regionset) { await this.setActiveRegionset(regionset); } - return true; + return { success: true }; } try { this.updateState({ loading: true }); - // TODO: SearchHandler should select first value - if (indicator.series) { - const { id, values } = indicator.series; - indicator.selections[id] = values[0]; - } const indicatorToAdd = await this.getIndicatorToAdd(indicator, regionset); + if (indicatorToAdd.data.uniqueCount === 0) { + return { error: 'noData'}; + } this.instance.addDataProviderInfo(indicatorToAdd); this.updateState({ regionset, indicators: [...this.getState().indicators, indicatorToAdd] }); } catch (error) { - this.log.warn(error.message); - return false; + return { error: error.message }; } this.updateState({ loading: false }); - return true; + return { success: true }; } // gather all needed stuff for rendering components before adding indicator to state From 0f815918ee9e8060b40ba2a11b114a18023e3424 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Thu, 7 Mar 2024 10:16:37 +0200 Subject: [PATCH 011/181] return error key --- bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js | 3 ++- .../statsgrid/handler/SearchIndicatorOptionsHelper.js | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js b/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js index 582bfa3548..131e116d79 100644 --- a/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js +++ b/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js @@ -37,7 +37,8 @@ class IndicatorsHandler extends StateHandler { }, error => { this.updateState({ loading: false }); - Messaging.error(this.loc(error)); + const errorMsg = this.loc('errors')[error] || this.loc('errors.indicatorListError'); + Messaging.error(errorMsg); }); } catch (error) { Messaging.error(this.loc('errors.indicatorListError')); diff --git a/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js b/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js index d730c80cb6..c4a51c77d7 100644 --- a/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js +++ b/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js @@ -77,7 +77,7 @@ export const updateIndicatorListInCache = (indicator, regionsetId) => { export const populateIndicatorOptions = async (datasourceId, successCallback, errorCallback) => { if (!datasourceId) { if (typeof errorCallback === 'function') { - errorCallback('errors.myIndicatorDatasource'); + errorCallback('myIndicatorDatasource'); } return; } @@ -107,7 +107,7 @@ export const populateIndicatorOptions = async (datasourceId, successCallback, er successCallback(value); } catch (error) { if (typeof errorCallback === 'function') { - errorCallback('errors.indicatorListError'); + errorCallback('indicatorListError'); } else { throw new Error(error); } @@ -195,6 +195,6 @@ const getIndicatorListFromServer = async (datasourceId) => { } return await response.json(); } catch (error) { - throw new Error('errors.indicatorListIsEmpty'); + throw new Error(error); } }; From 89f82044cc63b0fa94b3443f5dd3936ec0377bd3 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Thu, 7 Mar 2024 10:17:12 +0200 Subject: [PATCH 012/181] handle metadata popup in viewhandler --- .../statsgrid/handler/ViewHandler.js | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/ViewHandler.js b/bundles/statistics/statsgrid/handler/ViewHandler.js index 80f0bc2fb0..705e915206 100644 --- a/bundles/statistics/statsgrid/handler/ViewHandler.js +++ b/bundles/statistics/statsgrid/handler/ViewHandler.js @@ -7,6 +7,7 @@ import { showIndicatorForm } from '../view/Form/IndicatorForm'; import { showClipboardPopup } from '../view/Form/ClipboardPopup'; import { showClassificationContainer } from '../components/classification/Classification'; import { showHistogramPopup } from '../components/manualClassification/HistogramForm'; +import { showMedataPopup } from '../components/description/MetadataPopup'; export const FLYOUTS = ['search', 'grid', 'diagram']; // to toggle tile @@ -144,9 +145,14 @@ class UIHandler extends StateHandler { } onSearchChange (state) { - const control = this.controls.search; - if (control) { - control.update(state, this.stateHandler.getState().indicators); + const { search, metadata} = this.controls; + if (search) { + search.update(state, this.stateHandler.getState().indicators); + } + if (metadata) { + const ds = state.selectedDatasource; + const indicators = state.selectedIndicators.map(id => ({ id, ds })); + metadata.update(indicators); } } @@ -198,7 +204,16 @@ class UIHandler extends StateHandler { this.show('search'); } - show (id) { + openMetadataPopup (indicators) { + const { metadata } = this.controls; + if (metadata) { + metadata.update(indicators); + return; + } + this.controls.metadata = showMedataPopup(indicators, () => this.close('metadata')); + } + + show (id) { // TODO: use optional options/params?? if (!id || this.controls[id]) { // already shown, do nothing return; @@ -222,13 +237,6 @@ class UIHandler extends StateHandler { controls = showClassificationContainer(state, this.getState(), controller, opts, showHistogram, onClose); } else if (id === 'histogram') { controls = showHistogramPopup(state, this.getState(), controller, onClose); - /* - // For now search handler opens metadata popup - } else if (id === 'metadata') { - const { selectedDatasource, selectedIndicators } = this.searchHandler.getState(); - const data = await this.prepareMetadataPopupData(selectedDatasource, selectedIndicators); - controls = showMedataPopup(data, onClose); - */ } else if (id === 'indicatorForm' && this.formHandler) { const onCloseWrapper = () => { this.close('clipboard'); @@ -302,7 +310,9 @@ const wrapped = controllerMixin(UIHandler, [ 'close', 'closeAll', 'updateClassificationState', - 'updateLayer' + 'updateLayer', + 'openMetadataPopup', + 'openSearchWithSelections' ]); export { wrapped as ViewHandler }; From b7f0c748aed29feb8e8af804afdca5a08bbcdfe7 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Thu, 7 Mar 2024 10:18:08 +0200 Subject: [PATCH 013/181] show error reason if available --- bundles/statistics/statsgrid/view/search/ErrorPopup.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx index 61405acb1a..06f8bef77b 100644 --- a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx +++ b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx @@ -14,15 +14,17 @@ const Content = styled.div` `; const Popup = ({errors}) => { - // TODO: show reason for error if available => errorLoc[indicator.error] + const errorLoc = Oskari.getMsg(BUNDLE_KEY, 'errors'); return ( {errors.map((indicator,i) => { - const { name, partialSeries, selections } = indicator; + const { name, partialSeries, selections, error } = indicator; const selection = partialSeries ? getInvalidSerie(partialSeries) : getSelection(selections); - return
{`${name} (${selection})`}
+ const cause = errorLoc[error]; + const errorMsg = cause ? `: ${cause}` : ''; + return
{`${name} (${selection})${errorMsg}`}
})}
); From ab9e16ae4e0ddfe38acb0411a659026dcdb499a3 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Thu, 7 Mar 2024 10:19:09 +0200 Subject: [PATCH 014/181] use indicator params selectors as array --- .../statsgrid/view/search/IndicatorParams.jsx | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx index 6763ab79bc..3580861e9f 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx @@ -23,14 +23,14 @@ const StyledSelect = styled(Select)` width: 100%; `; -const TimeSeriesParams = ({name, options, selectedValues, controller}) => ( +const TimeSeriesParams = ({id, options, selectedValues, controller}) => ( controller.setParamSelection(name, [value, selectedValues[1]])} + onChange={(value) => controller.setParamSelection(id, [value, selectedValues[1]])} /> @@ -38,7 +38,7 @@ const TimeSeriesParams = ({name, options, selectedValues, controller}) => ( controller.setParamSelection(name, [selectedValues[0], value])} + onChange={(value) => controller.setParamSelection(id, [selectedValues[0], value])} /> @@ -46,9 +46,8 @@ const TimeSeriesParams = ({name, options, selectedValues, controller}) => ( export const IndicatorParams = ({ state, allRegionsets, controller }) => { const { searchTimeseries, regionsetFilter, indicatorParams, selectedRegionset } = state; - const { selectors = {}, regionsets = [], selections = {} } = indicatorParams; - const paramKeys = Object.keys(selectors); - if (!paramKeys.length) { + const { selectors = [], regionsets = [], selections = {} } = indicatorParams; + if (!selectors.length) { return ( ); @@ -66,26 +65,24 @@ export const IndicatorParams = ({ state, allRegionsets, controller }) => { return (
- {paramKeys.map((param) => { - const value = selections[param]; - const { values = [], time } = selectors[param] || {}; - const options = values.map(value => ({ value: value.id, label: value.title })); + {selectors.map(({values, time, id, label}) => { + const value = selections[id]; if (time && searchTimeseries) { return ( - ); } return ( - - + + {label} controller.setParamSelection(param, value)} + onChange={(value) => controller.setParamSelection(id, value)} mode={time ? 'multiple' : ''} /> From a4725d6115f6081349cc2789acf47f8c14f4c8e4 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Fri, 8 Mar 2024 15:48:19 +0200 Subject: [PATCH 015/181] split user indicator form to info and data --- .../statsgrid/handler/IndicatorFormHandler.js | 335 +++++++----------- .../statsgrid/handler/SearchHandler.js | 10 +- .../statsgrid/view/Form/IndicatorCollapse.jsx | 25 ++ .../statsgrid/view/Form/IndicatorDatasets.jsx | 10 +- .../statsgrid/view/Form/IndicatorForm.jsx | 59 +-- .../statsgrid/view/Form/IndicatorInfo.jsx | 20 +- .../statsgrid/view/Form/StatisticalData.jsx | 35 +- .../statsgrid/view/Form/StatisticalInfo.jsx | 28 +- 8 files changed, 218 insertions(+), 304 deletions(-) create mode 100644 bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx diff --git a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js index 468d9701f6..e37da05744 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js +++ b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js @@ -2,7 +2,6 @@ import { StateHandler, controllerMixin, Messaging } from 'oskari-ui/util'; import { getHashForIndicator } from '../helper/StatisticsHelper'; import { getIndicatorMetadata, getIndicatorData, saveIndicator, saveIndicatorData, deleteIndicator } from './IndicatorHelper'; -import { getDatasources, getRegionsets } from '../helper/ConfigHelper'; import { getRegionsAsync } from '../helper/RegionsHelper'; const SELECTOR = 'year'; @@ -24,8 +23,8 @@ class IndicatorFormController extends StateHandler { async showIndicatorPopup (ds, id = null) { if (!ds) return; this.reset(); - await this.preparePopupData({ ds, id }); - this.instance.getViewHandler().show('indicatorForm'); + await this.preparePopupData(ds, id); + this.instance.getViewHandler()?.show('indicatorForm'); if (!id && !Oskari.user().isLoggedIn()) { Messaging.warn({ duration: 10, @@ -38,300 +37,223 @@ class IndicatorFormController extends StateHandler { this.instance.getViewHandler().show('clipboard'); } - getSelectedIndicator (full) { - const { indicatorName, indicatorDescription, indicatorSource, datasourceId, indicatorId, datasetYear } = this.getState(); - const indicator = { - ds: datasourceId, - id: indicatorId, - selections: datasetYear ? { [SELECTOR]: datasetYear } : {} - }; - if (!full) { - return indicator; - } - indicator.name = indicatorName; - indicator.description = indicatorDescription; - indicator.source = indicatorSource; - return indicator; - } - getInitState () { return { - indicatorName: '', - indicatorSource: '', - indicatorDescription: '', - indicatorId: null, - datasourceId: null, - regionsetOptions: null, - datasets: null, - datasetYear: '', - datasetRegionset: null, - formData: {} + indicator: {}, // TODO: is it ok to pass additional info (name,..) for other handlers?? + datasets: [], + selection: '', + regionset: null, + dataByRegions: [], + showDataTable: 'todo add button', + loading: false }; } reset () { this.updateState(this.getInitState()); } - async preparePopupData (indicator) { - const { id, ds } = indicator; - const { regionsets = [] } = getDatasources().find(({ id }) => id === ds) || {}; - const regionsetOptions = getRegionsets().filter(rs => regionsets.includes(rs.id)); - - if (id) { - await this.getIndicatorDatasets(indicator); - } - this.updateState({ datasourceId: ds, indicatorId: id, regionsetOptions }); + async preparePopupData (ds, id) { + this.updateState({ loading: true }); + const indicator = { id, ds }; + const datasets = await this.populateIndicatorFromMetadata(indicator); + this.updateState({ indicator, datasets, loading: false }); } - async getIndicatorDatasets (indicator) { - try { - const ind = await getIndicatorMetadata(indicator.ds, indicator.id); - const datasets = []; - for (const sel of ind?.selectors) { - for (const regionset of ind.regionsets) { - for (const value of sel.allowedValues) { - const data = {}; - if (typeof value === 'object') { - data[sel.id] = value.id; - } else { - data[sel.id] = value; - } - data.regionset = regionset; - datasets.push(data); - } - } + async populateIndicatorFromMetadata (indicator) { + const datasets = []; + if (indicator.id && indicator.ds) { + try { + const { selectors, regionsets, name, description, source } = await getIndicatorMetadata(indicator.ds, indicator.id); + indicator.name = name; + indicator.description = description; + indicator.source = source; + selectors.forEach(sel => regionsets.forEach(regionset => sel.allowedValues.forEach(val => datasets.push({[sel.id]: val.id, regionset})))); + } catch (error) { + Messaging.error(Oskari.getMsg('StatsGrid', 'errors.indicatorMetadataError')); } - this.updateState({ - datasets, - indicatorName: Oskari.getLocalized(ind.name), - indicatorDescription: Oskari.getLocalized(ind.description), - indicatorSource: Oskari.getLocalized(ind.source) - }); - } catch (error) { - Messaging.error(Oskari.getMsg('StatsGrid', 'errors.indicatorMetadataError')); } + return datasets; } - setIndicatorName (value) { - this.updateState({ - indicatorName: value - }); + updateIndicator (key, value) { + this.updateState({ indicator: {...this.getState().indicator, [key]: value }}); } - setIndicatorDescription (value) { - this.updateState({ - indicatorDescription: value - }); + setSelection (selection) { + this.updateState({ selection }); } - setindicatorSource (value) { - this.updateState({ - indicatorSource: value - }); - } - - setDatasetYear (value) { - this.updateState({ - datasetYear: value - }); - } - - setDatasetRegionset (value) { - this.updateState({ - datasetRegionset: value - }); + setRegionset (regionset) { + this.updateState({ regionset }); } addStatisticalData () { - const { datasetYear, datasetRegionset } = this.getState(); - if (datasetYear.length === 0 || isNaN(datasetYear)) { + const { selection, regionset } = this.getState(); + if (selection.length === 0 || isNaN(selection)) { Messaging.error(this.loc('errors.myIndicatorYearInput')); return; } - if (!datasetRegionset) { + if (!regionset) { Messaging.error(this.loc('errors.myIndicatorRegionselect')); return; } this.showDataTable(); } - updateFormData (value, regionId) { - const { formData } = this.getState(); - const regions = formData.regions?.map(region => { - if (region.id === regionId) { - return { - ...region, - value - }; - } - return region; - }); - this.updateState({ - formData: { - ...formData, - regions - } - }); + updateRegionValue (id, value) { + const dataByRegions = this.getState().dataByRegions + .map(region => region.id === id ? {...region, value } : region); + this.updateState({ dataByRegions }); } async showDataTable () { - const indicator = this.getSelectedIndicator(); - const { datasetRegionset: regionsetId, datasetYear } = this.getState(); - this.updateState({ - loading: true - }); - const { name } = getRegionsets().find(rs => rs.id === regionsetId) || {}; - const labels = { - regionset: name, - year: datasetYear - }; - let regions; + this.updateState({ loading: true, showDataTable: true }); + const { indicator, regionset, selection } = this.getState(); + const selections = { [SELECTOR]: selection }; try { - regions = await getRegionsAsync(regionsetId); + const regions = await getRegionsAsync(regionset); let data = {}; if (indicator.id) { try { - data = await getIndicatorData(indicator, regionsetId); + data = await getIndicatorData({...indicator, selections }, regionset); } catch (e) { + console.log(e.message); // no data saved for selections + // TODO: handle getIndicatorData properly } } - const formRegions = [...regions].sort((a, b) => a.name.localeCompare(b.name)).map((region) => { - return { - id: region.id, - name: region.name, - value: data[region.id] - }; - }); - this.updateState({ - loading: false, - formData: { - regions: formRegions, - labels - } - }); + const dataByRegions = regions + .map(({name, id}) => ({key: id, name, value: data[id]})) + .sort((a, b) => a.name.localeCompare(b.name)); + this.updateState({ dataByRegions, loading: false }); } catch (error) { Messaging.error(Oskari.getMsg('StatsGrid', 'errors.regionsDataError')); - this.cancelForm(); + this.closeDataTable(); } } - cancelForm () { + closeDataTable () { + // TODO: selection, regionset?? this.updateState({ loading: false, - datasetYear: '', - datasetRegionset: null, - formData: {} + showDataTable: false, + dataByRegions: [] }); } + async saveIndicator () { + this.updateState({ loading: true }); + const { indicator } = this.getState(); + try { + const id = await saveIndicator(indicator); + const updated = {...indicator, id}; + this.updateState({ indicator: updated, loading: false }); + this.notifyCacheUpdate(updated); + this.log.info(`Saved indicator with id: ${id}`, updated); + Messaging.success(this.loc('userIndicators.dialog.successMsg')); + } catch (error) { + this.updateState({ loading: false }); + Messaging.error(this.loc('errors.indicatorSave')); + } + } + async saveData () { + this.updateState({ loading: true }); + const { dataByRegions, regionset, selection } = this.getState(); + const selections = { [SELECTOR]: selection }; + const indicator = { ...this.getState().indicator, selections }; - async saveForm () { - this.updateState({ - loading: true - }); - const { formData, datasetRegionset, datasetYear } = this.getState(); - const indicator = this.getSelectedIndicator(true); if (typeof indicator.name !== 'string' || indicator.name.trim().length === 0) { + // TODO: disable button, mark name as mandatory? Messaging.warn(this.loc('errors.myIndicatorNameInput')); return; } - - const { regions = [] } = formData; const data = {}; - regions.forEach(region => { - if (typeof region.value === 'undefined') { + dataByRegions.forEach(({key, value}) => { + if (typeof value === 'undefined') { return; } - const value = `${region.value}`.trim().replace(/,/g, '.'); - if (!value || isNaN(value)) { + const valString = `${value}`.trim().replace(/,/g, '.'); + if (!valString || isNaN(valString)) { return; } - data[region.id] = Number(value); + data[key] = Number(valString); }); + if (!Object.keys(data).length) { + Messaging.warn(this.loc('errors.myIndicatorNoData')); + return; + } try { - indicator.id = await saveIndicator(indicator); - indicator.hash = getHashForIndicator(indicator); - this.log.info('Saved indicator', indicator); - if (Object.keys(data).length) { - await saveIndicatorData(indicator, data, datasetRegionset); - const indicatorInfo = `Indicator: ${indicator.id}, selection: ${datasetYear}, regionset: ${datasetRegionset}.`; - this.log.info('Saved data form values', data, indicatorInfo); - // add indicator only when data is saved - this.selectSavedIndicator(indicator, datasetRegionset); - } + await saveIndicatorData(indicator, data, regionset); + const indicatorInfo = `Indicator: ${indicator.id}, selection: ${selection}, regionset: ${regionset}.`; + this.log.info('Saved data form values', data, indicatorInfo); Messaging.success(this.loc('userIndicators.dialog.successMsg')); - this.cancelForm(); - this.preparePopupData(indicator); + // add indicator only when data is saved + const dataset = {...selections, regionset }; + this.selectIndicator(dataset); + + this.updateState({ datasets: [...this.getState().datasets, dataset] }); + this.closeDataTable(); this.notifyCacheUpdate(indicator); } catch (error) { + this.updateSate({ loading: false }); Messaging.error(this.loc('errors.indicatorSave')); - this.cancelForm(); } } notifyCacheUpdate (indicator) { - const { datasourceId } = this.getState(); - this.instance.getSearchHandler()?.onCacheUpdate({ datasourceId, indicator }); + this.instance.getSearchHandler()?.onCacheUpdate(indicator); } selectIndicator (dataset) { const selections = { [SELECTOR]: dataset[SELECTOR] }; - const indicator = { ...this.getSelectedIndicator(), selections }; + const indicator = { ...this.getState().indicator, selections }; indicator.hash = getHashForIndicator(indicator); this.instance.getStateHandler()?.getController().selectSavedIndicator(indicator, dataset.regionset); } - selectSavedIndicator (indicator, regionset) { - this.instance.getStateHandler()?.getController().selectSavedIndicator(indicator, regionset); - } importFromClipboard (data) { - const validRows = []; + const regionValues = {}; const lines = data.match(/[^\r\n]+/g); // loop through all the lines and parse municipalities (name or code) lines.forEach((line) => { - let area; - let value; // separator can be a tabulator or a semicolon const matches = line.match(/([^\t;]+) *[\t;]+ *(.*)/); if (matches && matches.length === 3) { - area = matches[1].trim(); - value = (matches[2] || '').replace(',', '.').replace(/\s/g, ''); - if (Number.isNaN(parseInt(value))) { - value = ''; + const region = matches[1].trim().toLowerCase(); + const value = (matches[2] || '').replace(',', '.').replace(/\s/g, ''); + if (!value || isNaN(value)) { + return; } - validRows.push({ - name: area, - value - }); + regionValues[region] = value; } }); - const formData = this.getState().formData.regions; - validRows.forEach(row => { - formData.forEach((data, index) => { - if (data.name.toLowerCase() === row.name.toLowerCase()) { - formData[index].value = row.value; - } - }); - }); - this.updateState({ - formData: { - ...this.getState().formData, - regions: formData + const dataByRegions = this.getState().dataByRegions.map(region => { + const { key, name } = region; + const value = regionValues[key] || regionValues[name.toLowerCase()]; + // String or undefined + if (value) { + return {...region, value}; } + return region; }); + this.updateState({ dataByRegions }); } editDataset (item = {}) { - const datasetYear = item[SELECTOR]; - const datasetRegionset = item.regionset; - this.updateState({ datasetYear, datasetRegionset }); + const selection = item[SELECTOR]; + const regionset = item.regionset; + this.updateState({ selection, regionset }); this.showDataTable(); } async deleteDataset (item = {}) { - const selections = { [SELECTOR]: item[SELECTOR] }; - const indicator = { ...this.getSelectedIndicator(), selections }; + const selection = item[SELECTOR]; + if (!selection) { + // without selection deletes all datasets + return; + } + const selections = { [SELECTOR]: selection }; + const indicator = { ...this.getState().indicator, selections }; indicator.hash = getHashForIndicator(indicator); const handler = this.instance.getStateHandler(); if (handler?.isIndicatorSelected(indicator, true)) { @@ -349,16 +271,15 @@ class IndicatorFormController extends StateHandler { } const wrapped = controllerMixin(IndicatorFormController, [ - 'setIndicatorName', - 'setIndicatorDescription', - 'setindicatorSource', - 'setDatasetYear', - 'setDatasetRegionset', + 'updateIndicator', + 'setSelection', + 'setRegionset', 'addStatisticalData', - 'updateFormData', - 'cancelForm', + 'updateRegionValue', + 'closeDataTable', 'showClipboardPopup', - 'saveForm', + 'saveIndicator', + 'saveData', 'importFromClipboard', 'editDataset', 'showIndicatorPopup', diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index b0885d7b82..cf4a80122a 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -58,12 +58,12 @@ class SearchController extends StateHandler { this.setSelectedIndicators(indicators); } - onCacheUpdate ({ datasourceId, indicator }) { + onCacheUpdate (indicator = {}) { const { selectedDatasource, selectedIndicators } = this.getState(); - if (datasourceId && selectedDatasource === datasourceId) { + if (selectedDatasource === indicator.ds) { this.fetchindicatorOptions(); } - if (indicator && selectedIndicators.includes(indicator.id)) { + if (selectedIndicators.includes(indicator.id)) { this.fetchIndicatorParams(); } } @@ -851,8 +851,8 @@ class SearchController extends StateHandler { if (!formHandler) { return; } - const indicator = selectedIndicators.length === 1 ? selectedIndicators[0] : null; - formHandler.showIndicatorPopup(selectedDatasource, indicator); + const indicatorId = selectedIndicators.length === 1 ? selectedIndicators[0] : null; + formHandler.showIndicatorPopup(selectedDatasource, indicatorId); } } diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx new file mode 100644 index 0000000000..0bd8bce0c3 --- /dev/null +++ b/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Message, Collapse, CollapsePanel } from 'oskari-ui'; +import { IndicatorInfo } from './IndicatorInfo'; + +import { IndicatorDatasets } from './IndicatorDatasets'; + +export const IndicatorCollapse = ({ state, controller }) => { + const items = [{ + key: 'info', + label: , + children: + }, { + key: 'data', + label: , + children: + }]; + // return + return ( + + {items.map(({key, label, children}) => ( + {children} + ))} + + ); +}; diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx index cf37025dbb..5badd0a0f0 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx @@ -3,6 +3,7 @@ import { Message } from 'oskari-ui'; import { Table } from 'oskari-ui/components/Table'; import { IconButton } from 'oskari-ui/components/buttons'; import styled from 'styled-components'; +import { getRegionsets } from '../../helper/ConfigHelper'; const StyledTable = styled(Table)` max-height: 475px; @@ -18,10 +19,11 @@ const ButtonContainer = styled.div` `; export const IndicatorDatasets = ({ state, controller }) => { - const { datasets, regionsetOptions } = state; - if (!datasets || !datasets.length) { + const { datasets } = state; + if (!datasets.length) { return null; } + const getRegionsetName = id => getRegionsets().find(rs => rs.id === id)?.name || ''; const columnSettings = [ { dataIndex: 'regionset', @@ -29,10 +31,10 @@ export const IndicatorDatasets = ({ state, controller }) => { width: 250, title: , render: (title, item) => { - const regionset = regionsetOptions.find(r => r.id === item.regionset) || {}; + const { year, regionset } = item; return ( controller.selectIndicator(item)}> - {item.year} - {regionset.name} + {year} - {getRegionsetName(regionset)} ); } diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx index 83cd5fdf45..3fb510c342 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx @@ -1,12 +1,11 @@ -import React, { useState } from 'react'; -import { Message, Collapse, CollapsePanel } from 'oskari-ui'; +import React from 'react'; +import { Message } from 'oskari-ui'; import { LocaleProvider } from 'oskari-ui/util'; -import { PrimaryButton, ButtonContainer } from 'oskari-ui/components/buttons'; +import { PrimaryButton, SecondaryButton, ButtonContainer } from 'oskari-ui/components/buttons'; import { showPopup } from 'oskari-ui/components/window'; -import { IndicatorInfo } from './IndicatorInfo'; -import { StatisticalInfo } from './StatisticalInfo'; import { StatisticalData } from './StatisticalData'; -import { IndicatorDatasets } from './IndicatorDatasets'; +import { IndicatorCollapse } from './IndicatorCollapse'; + import styled from 'styled-components'; const BUNDLE_KEY = 'StatsGrid'; @@ -18,49 +17,17 @@ const Content = styled('div')` min-width: 365px; `; -const IndicatorForm = ({ state, controller }) => { - const [activePanel, setActivePanel] = useState(state.indicator ? 'data' : 'info'); - - const onPanelChange = () => { - // Because collapse onChange doesn't give the new key as argument - let panel = 'info'; - if (activePanel === 'info') { - panel = 'data'; - } - setActivePanel(panel); - }; - +const IndicatorForm = ({ state, controller, onClose }) => { + const { showDataTable } = state; + const Component = showDataTable ? StatisticalData : IndicatorCollapse; + const onSave = () => showDataTable ? controller.saveData() : controller.saveIndicator(); + const onCancel = () => showDataTable ? controller.closeDataTable() : onClose(); return ( - - }> - - - }> - {state.formData.regions ? ( - - ) : ( -
- - -
- )} -
-
+ - controller.saveForm()} - /> + +
); diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx index 3e1f2ec923..398b01a160 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { TextInput, TextAreaInput } from 'oskari-ui'; import styled from 'styled-components'; +import { BUNDLE_KEY } from '../../constants'; const Content = styled('div')` display: flex; @@ -15,23 +16,24 @@ const StyledTextArea = styled(TextAreaInput)` `; export const IndicatorInfo = ({ state, controller }) => { + const { name = '', description = '', source = '' } = state.indicator; return ( controller.setIndicatorName(e.target.value)} + placeholder={Oskari.getMsg(BUNDLE_KEY, 'userIndicators.panelGeneric.formName')} + value={name} + onChange={(e) => controller.updateIndicator('name', e.target.value)} /> controller.setIndicatorDescription(e.target.value)} + value={description} + onChange={(e) => controller.updateIndicator('description', e.target.value)} /> controller.setindicatorSource(e.target.value)} + placeholder={Oskari.getMsg(BUNDLE_KEY, 'userIndicators.panelGeneric.formDatasource')} + value={source} + onChange={(e) => controller.updateIndicator('source', e.target.value)} /> ); diff --git a/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx b/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx index f1bb516cdb..efe29d2d34 100644 --- a/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx +++ b/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx @@ -1,8 +1,10 @@ import React from 'react'; import { TextInput, Message, Button } from 'oskari-ui'; import { Table } from 'oskari-ui/components/Table'; -import { ButtonContainer, SecondaryButton } from 'oskari-ui/components/buttons'; +import { ButtonContainer } from 'oskari-ui/components/buttons'; import styled from 'styled-components'; +import { StatisticalInfo } from './StatisticalInfo'; +import { getRegionsets } from '../../helper/ConfigHelper'; const Content = styled('div')` display: flex; @@ -15,25 +17,26 @@ const StyledTable = styled(Table)` overflow-y: scroll; `; -export const StatisticalData = ({ data, controller }) => { - const { regions = [], labels = {} } = data; +export const StatisticalData = ({ state, controller }) => { + const { selection, regionset, dataByRegions } = state; + const { name = '' } = getRegionsets().find(rs => rs.id === regionset) || {}; const columnSettings = [ { dataIndex: 'name', align: 'left', width: 250, - title: : {labels.regionset} + title: : {name} }, { dataIndex: 'value', align: 'left', width: 125, - title: : {labels.year}, + title: : {selection}, render: (title, item) => { return ( controller.updateFormData(e.target.value, item.id)} + onChange={(e) => controller.updateRegionValue(item.id, e.target.value)} /> ); } @@ -42,22 +45,14 @@ export const StatisticalData = ({ data, controller }) => { return ( - + { regionset && ({ - key: region.id, - ...region - }))} - pagination={false} - /> + dataSource={dataByRegions} + pagination={false} /> + } - controller.cancelForm()} - /> - diff --git a/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx b/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx index 7149e4af56..1accb28f65 100644 --- a/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx +++ b/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx @@ -1,6 +1,9 @@ import React from 'react'; -import { TextInput, Select, Button, Message } from 'oskari-ui'; +import { TextInput, Select, Message } from 'oskari-ui'; import styled from 'styled-components'; +import { PrimaryButton } from 'oskari-ui/components/buttons'; +import { getDatasources, getRegionsets } from '../../helper/ConfigHelper'; + const Content = styled('div')` display: flex; @@ -17,25 +20,24 @@ const YearField = styled(TextInput)` `; export const StatisticalInfo = ({ state, controller }) => { + const { regionset, indicator, selection } = state; + const { regionsets = [] } = getDatasources().find(({ id }) => id === indicator.ds) || {}; + const options = getRegionsets() + .filter(rs => regionsets.includes(rs.id)) + .map(rs => ({value: rs.id, label: rs.name})); return ( controller.setDatasetYear(e.target.value)} - /> + value={selection} + onChange={(e) => controller.setSelection(e.target.value)} /> } - value={state.datasetRegionset} - onChange={(value) => controller.setDatasetRegionset(value)} - options={state.regionsetOptions?.map(rs => ({value: rs.id, label: rs.name}))} - /> - + value={regionset} + onChange={(value) => controller.setRegionset(value)} + options={options}/> + controller.addStatisticalData()} /> ); }; From ed2fc87a3dd5b50bf4ba7c476a39622f2786bf17 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Fri, 8 Mar 2024 15:48:42 +0200 Subject: [PATCH 016/181] add localization --- bundles/statistics/statsgrid/resources/locale/en.js | 4 +++- bundles/statistics/statsgrid/resources/locale/fi.js | 4 +++- bundles/statistics/statsgrid/resources/locale/fr.js | 4 +++- bundles/statistics/statsgrid/resources/locale/ru.js | 4 +++- bundles/statistics/statsgrid/resources/locale/sv.js | 4 +++- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/bundles/statistics/statsgrid/resources/locale/en.js b/bundles/statistics/statsgrid/resources/locale/en.js index e1285b2ff9..d616e5ba6a 100644 --- a/bundles/statistics/statsgrid/resources/locale/en.js +++ b/bundles/statistics/statsgrid/resources/locale/en.js @@ -99,7 +99,9 @@ Oskari.registerLocalization({ 'Vuosi': 'Year', 'regionset': 'Regional division', 'from': 'from', - 'to': 'to' + 'to': 'to', + 'value': 'Value', + 'region': 'Region' }, 'datatable': 'Table', 'published': { diff --git a/bundles/statistics/statsgrid/resources/locale/fi.js b/bundles/statistics/statsgrid/resources/locale/fi.js index 4907d5471a..ee42e90711 100644 --- a/bundles/statistics/statsgrid/resources/locale/fi.js +++ b/bundles/statistics/statsgrid/resources/locale/fi.js @@ -99,7 +99,9 @@ Oskari.registerLocalization({ 'Vuosi': 'Vuosi', 'regionset': 'Aluejako', 'from': 'alkaen', - 'to': 'päättyen' + 'to': 'päättyen', + 'value': 'Arvo', + 'region': 'Alue' }, 'datatable': 'Taulukko', 'published': { diff --git a/bundles/statistics/statsgrid/resources/locale/fr.js b/bundles/statistics/statsgrid/resources/locale/fr.js index fc8e777294..0b1678239d 100644 --- a/bundles/statistics/statsgrid/resources/locale/fr.js +++ b/bundles/statistics/statsgrid/resources/locale/fr.js @@ -98,7 +98,9 @@ Oskari.registerLocalization( "year": "Année", "regionset": "Superficie", "from": "de", - "to": "à" + "to": "à", + "value": "Valeur", + "region": "Région" }, "datatable": "Tableau", "published": { diff --git a/bundles/statistics/statsgrid/resources/locale/ru.js b/bundles/statistics/statsgrid/resources/locale/ru.js index 6b63133394..eba3abaa88 100644 --- a/bundles/statistics/statsgrid/resources/locale/ru.js +++ b/bundles/statistics/statsgrid/resources/locale/ru.js @@ -104,7 +104,9 @@ Oskari.registerLocalization( "year": "Год", "regionset": "Облаcть", "from": "от", - "to": "до" + "to": "до", + "value": "Значение", + "region": "Облаcть" }, "datatable": "Таблица", "published": { diff --git a/bundles/statistics/statsgrid/resources/locale/sv.js b/bundles/statistics/statsgrid/resources/locale/sv.js index 8ab283c2d1..7dff8d15b9 100644 --- a/bundles/statistics/statsgrid/resources/locale/sv.js +++ b/bundles/statistics/statsgrid/resources/locale/sv.js @@ -99,7 +99,9 @@ Oskari.registerLocalization({ 'Vuosi': 'År', 'regionset': 'Områdesindelning', 'from': 'från', - 'to': 'tills' + 'to': 'tills', + 'value': 'Värde', + 'region': 'Område' }, 'datatable': 'Tabell', 'published': { From 4b7cf1067a13fca8492d670a42e230c326d62e12 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Wed, 13 Mar 2024 12:57:56 +0200 Subject: [PATCH 017/181] rename table --- .../view/Form/{IndicatorDatasets.jsx => DatasetsTable.jsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bundles/statistics/statsgrid/view/Form/{IndicatorDatasets.jsx => DatasetsTable.jsx} (100%) diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx b/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx similarity index 100% rename from bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx rename to bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx From 2520068c5b611b137a5861628e2956708ebec4a0 Mon Sep 17 00:00:00 2001 From: okauppinen Date: Wed, 13 Mar 2024 16:20:40 +0200 Subject: [PATCH 018/181] update title and show warning for guest inside form --- .../statsgrid/view/Form/IndicatorForm.jsx | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx index 3fb510c342..1c27e53ad8 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx @@ -1,10 +1,11 @@ import React from 'react'; -import { Message } from 'oskari-ui'; +import { Message, Button } from 'oskari-ui'; import { LocaleProvider } from 'oskari-ui/util'; import { PrimaryButton, SecondaryButton, ButtonContainer } from 'oskari-ui/components/buttons'; import { showPopup } from 'oskari-ui/components/window'; import { StatisticalData } from './StatisticalData'; import { IndicatorCollapse } from './IndicatorCollapse'; +import { getRegionsets } from '../../helper/ConfigHelper'; import styled from 'styled-components'; @@ -17,16 +18,43 @@ const Content = styled('div')` min-width: 365px; `; +const Warning = styled.div` + width: 350px; + font-style: italic; + margin-top: 10px; +`; + +const Title = ({ indicator, showDataTable, regionset, selection }) => { + if (showDataTable) { + const regionsetName = getRegionsets().find(rs => rs.id === regionset)?.name || ''; + return ( + + + {`: ${selection} - ${regionsetName}`} + + ); + } + const key = !indicator.id ? 'userIndicators.add' : 'userIndicators.edit'; + return +}; + const IndicatorForm = ({ state, controller, onClose }) => { - const { showDataTable } = state; + const { showDataTable, indicator } = state; + const showWarning = !showDataTable && !indicator.id && !Oskari.user().isLoggedIn(); const Component = showDataTable ? StatisticalData : IndicatorCollapse; const onSave = () => showDataTable ? controller.saveData() : controller.saveIndicator(); const onCancel = () => showDataTable ? controller.closeDataTable() : onClose(); return ( + {showWarning && ()} + {showDataTable && ( + + )} @@ -34,12 +62,10 @@ const IndicatorForm = ({ state, controller, onClose }) => { }; export const showIndicatorForm = (state, controller, onClose) => { - - const title = ; const controls = showPopup( - title, + , <LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> - <IndicatorForm state={state} controller={controller} /> + <IndicatorForm state={state} controller={controller} onClose={onClose} /> </LocaleProvider>, onClose ); @@ -47,9 +73,9 @@ export const showIndicatorForm = (state, controller, onClose) => { return { ...controls, update: (state) => controls.update( - title, + <Title {...state}/>, <LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> - <IndicatorForm state={state} controller={controller} /> + <IndicatorForm state={state} controller={controller} onClose={onClose}/> </LocaleProvider> ) }; From edb58509a0b47f21e863aef1526bf7f69201f168 Mon Sep 17 00:00:00 2001 From: okauppinen <olli.kauppinen@nls.fi> Date: Wed, 13 Mar 2024 16:22:45 +0200 Subject: [PATCH 019/181] split component --- .../statsgrid/view/Form/IndicatorCollapse.jsx | 10 +++++----- .../statsgrid/view/Form/IndicatorDatasets.jsx | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx index 0bd8bce0c3..c210da305b 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx @@ -1,22 +1,22 @@ import React from 'react'; import { Message, Collapse, CollapsePanel } from 'oskari-ui'; import { IndicatorInfo } from './IndicatorInfo'; - import { IndicatorDatasets } from './IndicatorDatasets'; export const IndicatorCollapse = ({ state, controller }) => { + const defaultActiveKey = state.indicator.id ? ['data'] : ['info']; const items = [{ key: 'info', - label: <Message messageKey='userIndicators.panelGeneric.title' />, + label: <Message messageKey='userIndicators.info.title' />, children: <IndicatorInfo state={state} controller={controller} /> }, { key: 'data', - label: <Message messageKey='userIndicators.panelData.title' />, + label: <Message messageKey='userIndicators.datasets.title' />, children: <IndicatorDatasets state={state} controller={controller}/> }]; - // return <Collapse items={items} defaultActiveKey={['info', 'data']}/> + // return <Collapse items={items} defaultActiveKey={defaultActiveKey}/> return ( - <Collapse items={items} defaultActiveKey={['info', 'data']}> + <Collapse defaultActiveKey={defaultActiveKey}> {items.map(({key, label, children}) => ( <CollapsePanel key={key} header={label}>{children}</CollapsePanel> ))} diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx new file mode 100644 index 0000000000..4d0b8b3d1d --- /dev/null +++ b/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Message } from 'oskari-ui'; +import { DatasetsTable } from './DatasetsTable'; +import { StatisticalInfo } from './StatisticalInfo'; + +export const IndicatorDatasets = ({ state, controller }) => { + if (!state.indicator.id) { + return <Message messageKey='userIndicators.datasets.noIndicator'/>; + } + + return ( + <div> + <DatasetsTable state={state} controller={controller}/> + <StatisticalInfo state={state} controller={controller}/> + </div> + ); +}; From 390de76ea60252c793e29c8f3dc7957a432085cb Mon Sep 17 00:00:00 2001 From: okauppinen <olli.kauppinen@nls.fi> Date: Wed, 13 Mar 2024 16:24:52 +0200 Subject: [PATCH 020/181] move button and dataset info from table to flyout --- .../statsgrid/view/Form/DatasetsTable.jsx | 23 ++++------ .../statsgrid/view/Form/StatisticalData.jsx | 46 ++++++------------- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx b/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx index 5badd0a0f0..be23b5b087 100644 --- a/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx +++ b/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx @@ -18,10 +18,10 @@ const ButtonContainer = styled.div` } `; -export const IndicatorDatasets = ({ state, controller }) => { +export const DatasetsTable = ({ state, controller }) => { const { datasets } = state; if (!datasets.length) { - return null; + return <Message messageKey='userIndicators.datasets.noDatasets'/>; } const getRegionsetName = id => getRegionsets().find(rs => rs.id === id)?.name || ''; const columnSettings = [ @@ -29,7 +29,7 @@ export const IndicatorDatasets = ({ state, controller }) => { dataIndex: 'regionset', align: 'left', width: 250, - title: <Message messageKey='userIndicators.modify.title' />, + title: <Message messageKey='userIndicators.datasets.dataset' />, render: (title, item) => { const { year, regionset } = item; return ( @@ -59,14 +59,11 @@ export const IndicatorDatasets = ({ state, controller }) => { } ]; - return ( - <StyledTable - columns={columnSettings} - dataSource={datasets?.map(ds => ({ - key: `${ds.regionset}-${ds.year}`, - ...ds - }))} - pagination={false} - /> - ); + return <StyledTable + columns={columnSettings} + dataSource={datasets?.map(ds => ({ + key: `${ds.regionset}-${ds.year}`, + ...ds + }))} + pagination={false}/>; }; diff --git a/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx b/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx index efe29d2d34..e3c363c943 100644 --- a/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx +++ b/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx @@ -1,61 +1,43 @@ import React from 'react'; -import { TextInput, Message, Button } from 'oskari-ui'; +import { TextInput, Message } from 'oskari-ui'; import { Table } from 'oskari-ui/components/Table'; -import { ButtonContainer } from 'oskari-ui/components/buttons'; import styled from 'styled-components'; -import { StatisticalInfo } from './StatisticalInfo'; -import { getRegionsets } from '../../helper/ConfigHelper'; -const Content = styled('div')` - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-between; -`; const StyledTable = styled(Table)` max-height: 475px; - overflow-y: scroll; + overflow-y: auto; `; export const StatisticalData = ({ state, controller }) => { - const { selection, regionset, dataByRegions } = state; - const { name = '' } = getRegionsets().find(rs => rs.id === regionset) || {}; + const { regionset, dataByRegions } = state; + if (!regionset || !dataByRegions.length) { + return <Message messageKey='errors.regionsetsIsEmpty' /> + } const columnSettings = [ { dataIndex: 'name', align: 'left', width: 250, - title: <span><Message messageKey='parameters.regionset' />: {name}</span> + title: <Message messageKey='parameters.region' /> }, { dataIndex: 'value', align: 'left', width: 125, - title: <span><Message messageKey='parameters.year' />: {selection}</span>, + title: <Message messageKey='parameters.value' />, render: (title, item) => { return ( <TextInput - value={item.value} - onChange={(e) => controller.updateRegionValue(item.id, e.target.value)} + value={item.value || ''} + onChange={(e) => controller.updateRegionValue(item.key, e.target.value)} /> ); } } ]; - return ( - <Content> - <StatisticalInfo state={state} controller={controller}/> - { regionset && <StyledTable - columns={columnSettings} - dataSource={dataByRegions} - pagination={false} /> - } - <ButtonContainer> - <Button onClick={() => controller.showClipboardPopup()} > - <Message messageKey='userIndicators.import.title' /> - </Button> - </ButtonContainer> - </Content> - ); + return <StyledTable + columns={columnSettings} + dataSource={dataByRegions} + pagination={false} /> }; From 736b29c07fd1eef4696d405734b0de9523d0f5b6 Mon Sep 17 00:00:00 2001 From: okauppinen <olli.kauppinen@nls.fi> Date: Wed, 13 Mar 2024 23:36:31 +0200 Subject: [PATCH 021/181] move user indicator localization under it --- .../statsgrid/resources/locale/en.js | 72 +++++++++--------- .../statsgrid/resources/locale/fi.js | 74 +++++++++---------- .../statsgrid/resources/locale/fr.js | 59 +++++++-------- .../statsgrid/resources/locale/is.js | 34 +-------- .../statsgrid/resources/locale/ru.js | 60 +++++++-------- .../statsgrid/resources/locale/sv.js | 66 ++++++++--------- 6 files changed, 150 insertions(+), 215 deletions(-) diff --git a/bundles/statistics/statsgrid/resources/locale/en.js b/bundles/statistics/statsgrid/resources/locale/en.js index d616e5ba6a..edfc328751 100644 --- a/bundles/statistics/statsgrid/resources/locale/en.js +++ b/bundles/statistics/statsgrid/resources/locale/en.js @@ -169,14 +169,7 @@ Oskari.registerLocalization({ 'regionsetsIsEmpty': 'Area selections could not be fetched for chosen data.', 'regionsDataError': 'Error occurred in area value search.', 'regionsDataIsEmpty': 'Area values could not be fetched for chosen data.', - 'datasetSave': 'Error saving dataset.', - 'datasetDelete': 'Error deleting dataset.', - 'indicatorSave': 'Error saving indicator', - 'myIndicatorNameInput': 'Name field cannot be empty.', - 'myIndicatorYearInput': 'Year field cannot be empty.', - 'myIndicatorRegionselect': 'Regionselect cannot be empty.', - 'myIndicatorDatasource': 'Datasource is empty.', - 'myIndicatorInvalidData': 'Data has invalid values.', + 'datasourceIsEmpty': 'Datasource is empty.', 'cannotDisplayAsSeries': 'Indicator cannot be inspected as a series.', 'noDataForIndicators': 'Service did not return data for {indicators, plural, one {the indicator} other {indicators}}', 'onlyPartialDataForIndicators': 'Service did not return all data for {indicators, plural, one {the indicator} other {indicators}}' @@ -242,35 +235,25 @@ Oskari.registerLocalization({ }, 'popup': { 'deletetitle': 'Delete Indicator', - 'deletemsg': 'You are deleting the indicator "{name}". Do you want to delete the indicator?', - 'deleteSuccess': 'Indicator removed' - }, - 'button': { - 'cancel': 'Cancel', - 'ok': 'OK' - }, - 'error': { - 'title': 'Error', - 'notfound': 'The indicator was not found.', - 'notdeleted': 'The indicator was not removed.' + 'deletemsg': 'You are deleting the indicator "{name}". Do you want to delete the indicator?' } }, 'userIndicators': { - 'flyoutTitle': 'Add new indicator', - 'buttonTitle': 'Add new indicator', - 'buttonAddIndicator': 'Add data', - 'panelGeneric': { + 'title': 'My indicators', + 'add': 'Add new indicator', + 'edit': 'Edit indicator', + 'notLoggedInWarning': 'Without logging in the data cannot be saved and it will only be available until page reload. Log in before adding the indicator to preserve the data.', + 'info': { 'title': 'Indicator data', - 'formName': 'Name', - 'formDescription': 'Description', - 'formDatasource': 'Datasource' - }, - 'panelData': { - 'title': 'Statistical data' + 'name': 'Name', + 'description': 'Description', + 'datasource': 'Datasource' }, - 'dialog': { - 'successTitle': 'Saved', - 'successMsg': 'The data has been saved.' + 'datasets': { + 'title': 'Statistical data', + 'dataset': 'Dataset', + 'noIndicator': 'Save indicator information to add datasets.', + 'noDatasets': 'Indicator doesn\'t have any dataset.' }, 'import': { 'title': 'Import from the clipboard', @@ -278,12 +261,25 @@ Oskari.registerLocalization({ 'Sample 1: Helsinki;1234 \n' + 'Sample 2: 011;5678' }, - 'notLoggedInTitle': 'Warning', - 'notLoggedInWarning': 'Without logging in the data cannot be saved and it will only be available until page reload. Log in before adding the indicator to preserve the data.', - 'modify': { - 'title': 'Indicator', - 'edit': 'Edit', - 'remove': 'Remove' + 'success': { + 'indicatorSave': 'Indicator saved', + 'indicatorDelete': 'Indicator removed', + 'datasetSave': 'The dataset has been saved', + 'datasetDelete': 'The dataset has been removed', + }, + 'error': { + 'indicatorSave': 'Error saving indicator', + 'indicatorDelete': 'The indicator was not removed', + 'indicatorNotfound': 'The indicator was not found', + 'datasetSave': 'Error saving dataset', + 'datasetDelete': 'Error deleting dataset' + }, + 'validate': { + 'name': 'Name field cannot be empty', + 'year': 'Year field cannot be empty', + 'regionset': 'Regionselect cannot be empty', + 'noData': 'Data doesn\'t have values', + 'invalidData': 'Data has invalid values' } }, 'indicatorList': { diff --git a/bundles/statistics/statsgrid/resources/locale/fi.js b/bundles/statistics/statsgrid/resources/locale/fi.js index 40bfd0d8fd..fccc920a43 100644 --- a/bundles/statistics/statsgrid/resources/locale/fi.js +++ b/bundles/statistics/statsgrid/resources/locale/fi.js @@ -169,16 +169,7 @@ Oskari.registerLocalization({ 'regionsetsIsEmpty': 'Aluevalintoja ei saatu valitulle aineistolle.', 'regionsDataError': 'Alueen arvojen haussa tapahtui virhe.', 'regionsDataIsEmpty': 'Valitulle aineistolle ei saatu alueiden arvoja.', - 'datasetSave': 'Virhe tallennetaessa aineistoa.', - 'datasetDelete': 'Virhe poistaessa aineistoa.', - 'indicatorSave': 'Virhe tallennettaessa muuttujaa.', - 'indicatorDelete': 'Virhe poistaessa muuttujaa.', - 'myIndicatorNameInput': 'Nimi kenttä ei voi olla tyhjä.', - 'myIndicatorYearInput': 'Vuosi kenttä ei voi olla tyhjä.', - 'myIndicatorRegionselect': 'Aluejako ei voi olla tyhjä.', - 'myIndicatorDatasource': 'Tietolähde on tyhjä.', - 'myIndicatorNoData': 'Aineiston arvoja ei ole annettu.', - 'myIndicatorInvalidData': 'Aineistossa on virheellisiä arvoja.', + 'datasourceIsEmpty': 'Tietolähde on tyhjä.', 'cannotDisplayAsSeries': 'Indikaattoria ei voida tarkastella sarjana', 'noDataForIndicators': 'Palvelusta ei saatu tietoja {indicators, plural, one {indikaattorille} other {indikaattoreille}}', 'onlyPartialDataForIndicators': 'Palvelusta ei saatu kaikkia tietoja {indicators, plural, one {indikaattorille} other {indikaattoreille}}' @@ -244,35 +235,25 @@ Oskari.registerLocalization({ }, 'popup': { 'deletetitle': 'Poista indikaattori', - 'deletemsg': 'Haluatko poistaa indikaattorin "{name}"?', - 'deleteSuccess': 'Indikaattori poistettu' - }, - 'button': { - 'cancel': 'Peruuta', - 'ok': 'OK' - }, - 'error': { - 'title': 'Virhe', - 'notfound': 'Indikaattoria ei löytynyt', - 'notdeleted': 'Indikaattorin poisto epäonnistui' + 'deletemsg': 'Haluatko poistaa indikaattorin "{name}"?' } }, 'userIndicators': { - 'flyoutTitle': 'Omat indikaattorit', - 'buttonTitle': 'Lisää uusi indikaattori', - 'buttonAddIndicator': 'Syötä dataa', - 'panelGeneric': { + 'title': 'Omat indikaattorit', + 'add': 'Lisää uusi indikaattori', + 'edit': 'Muokkaa indikaattoria', + 'notLoggedInWarning': 'Kirjautumattomana oman indikaattorin tiedot ovat käytettävissä vain tämän session ajan. Kirjaudu sisään tallentaaksesi indikaattori.', + 'info': { 'title': 'Indikaattorin tiedot', - 'formName': 'Nimi', - 'formDescription': 'Kuvaus', - 'formDatasource': 'Lähde' - }, - 'panelData': { - 'title': 'Tilastotieto' + 'name': 'Nimi', + 'description': 'Kuvaus', + 'source': 'Lähde' }, - 'dialog': { - 'successTitle': 'Tallennettu', - 'successMsg': 'Tiedot tallennettu.' + 'datasets': { + 'title': 'Tilastotieto', + 'dataset': 'Aineisto', + 'noIndicator': 'Tallenna indikaattorin tiedot lisätäksesi aineistoja.', + 'noDatasets': 'Indikaattorilla ei ole tallennettuja aineistoja.' }, 'import': { 'title': 'Tuo leikepöydältä', @@ -280,12 +261,25 @@ Oskari.registerLocalization({ 'Esimerkki 1: Helsinki;1234 \n' + 'Esimerkki 2: 011;5678' }, - 'notLoggedInTitle': 'Varoitus', - 'notLoggedInWarning': 'Kirjautumattomana oman indikaattorin tiedot ovat käytettävissä vain tämän session ajan. Kirjaudu sisään tallentaaksesi indikaattori.', - 'modify': { - 'title': 'Indikaattori', - 'edit': 'Muokkaa', - 'remove': 'Poista' + 'success': { + 'indicatorSave': 'Indikaattorin tiedot tallennettu', + 'indicatorDelete': 'Indikaattorin tiedot poistettu', + 'datasetSave': 'Aineisto tallennettu', + 'datasetDelete': 'Aineisto poistettu' + }, + 'error': { + 'indicatorSave': 'Indikaattorin tallennus epäonnistui', + 'indicatorDelete': 'Indikaattorin poisto epäonnistui', + 'IndicatorNotfound': 'Indikaattoria ei löytynyt', + 'datasetSave': 'Virhe tallennetaessa aineistoa', + 'datasetDelete': 'Virhe poistaessa aineistoa' + }, + 'validate': { + 'name': 'Nimi kenttä ei voi olla tyhjä', + 'year': 'Vuosi kenttä ei voi olla tyhjä', + 'regionset': 'Aluejako ei voi olla tyhjä', + 'noData': 'Aineiston arvoja ei ole annettu', + 'invalidData': 'Aineistossa on virheellisiä arvoja' } }, 'indicatorList': { diff --git a/bundles/statistics/statsgrid/resources/locale/fr.js b/bundles/statistics/statsgrid/resources/locale/fr.js index 0b1678239d..e687c61112 100644 --- a/bundles/statistics/statsgrid/resources/locale/fr.js +++ b/bundles/statistics/statsgrid/resources/locale/fr.js @@ -167,12 +167,7 @@ Oskari.registerLocalization( "regionsetsIsEmpty": "Impossible d'aller chercher les sélections de superficie pour les données choisies.", "regionsDataError": "Une erreur est survenue lors de la recherche des valeurs de superficie.", "regionsDataIsEmpty": "Impossible d'aller chercher les valeurs de superficie pour les données choisies.", - "datasetSave": "Erreur lors de l'enregistrement du jeu de données.", - "datasetDelete": "Erreur lors de la suppression du jeu de données.", - "indicatorSave": "Erreur lors de l'enregistrement de l'indicateur", - "myIndicatorYearInput": "Le champ Année ne peut pas être vide.", - "myIndicatorRegionselect": "Le champ Sélection de la région ne peut pas être vide.", - "myIndicatorDatasource": "Le champ Source de données est vide.", + "datasourceIsEmpty": "Le champ Source de données est vide.", "cannotDisplayAsSeries": "Impossible d'analyser l'indicateur en tant que série." }, "datacharts": { @@ -230,45 +225,41 @@ Oskari.registerLocalization( "popup": { "deletetitle": "Supprimer l'indicateur", "deletemsg": "Vous supprimez l'indicateur \"{name}\". Souhaitez-vous supprimer l'indicateur?", - "deleteSuccess": "Indicateur supprimé" - }, - "button": { - "cancel": "Annuler", - "ok": "OK" - }, - "error": { - "title": "Erreur", - "notfound": "Impossible de trouver l'indicateur.", - "notdeleted": "L'indicateur n'a pas été supprimé." } }, "userIndicators": { - "flyoutTitle": "Ajouter un nouvel indicateur", - "buttonTitle": "Ajouter un nouvel indicateur", - "buttonAddIndicator": "Ajouter des données", - "panelGeneric": { + "title": "Mes indicateurs", + "add": "Ajouter un nouvel indicateur", + "edit": "Indicateur de modification", + "notLoggedInWarning": "Sans ouverture de session, impossible d'enregistrer les données, qui seront uniquement accessibles jusqu'à ce que la page soit rechargée. Ouvrir une session avant d'ajouter l'indicateur pour conserver les données.", + "info": { "title": "Données de l'indicateur", - "formName": "Nom", - "formDescription": "Description", - "formDatasource": "Source de données" + "name": "Nom", + "description": "Description", + "source": "Source de données" }, - "panelData": { + "datasets": { "title": "Données statistiques" }, - "dialog": { - "successTitle": "Enregistré", - "successMsg": "Les données ont été enregistrées." - }, "import": { "title": "Importation à partir du presse-papiers", "placeholder": "Saisir les données de l'indicateur ici. Chaque rangée doit contenir une région et sa valeur. Saisir le nom ou l'identifiant de la région. Utiliser le point-virgule en tant que délimiteur. On peut importer les données dans les formats suivants : \nExemple 1 : Helsinki;1234 \nExample 2 : 011;5678" }, - "notLoggedInTitle": "Avertissement", - "notLoggedInWarning": "Sans ouverture de session, impossible d'enregistrer les données, qui seront uniquement accessibles jusqu'à ce que la page soit rechargée. Ouvrir une session avant d'ajouter l'indicateur pour conserver les données.", - "modify": { - "title": "Indicateur", - "edit": "Modifier", - "remove": "Supprimer" + "success": { + "indicatorSave": "Les données ont été enregistrées", + "indicatorDelete": "Indicateur supprimé", + "datasetSave": "Les données ont été enregistrées" + }, + "error": { + "indicatorSave": "Erreur lors de l'enregistrement de l'indicateur", + "indicatorDelete": "L'indicateur n'a pas été supprimé", + "IndicatorNotfound": "Impossible de trouver l'indicateur", + "datasetSave": "Erreur lors de l'enregistrement du jeu de données", + "datasetDelete": "Erreur lors de la suppression du jeu de données" + }, + 'validate': { + "year": "Le champ Année ne peut pas être vide.", + "regionset": "Le champ Sélection de la région ne peut pas être vide." } } } diff --git a/bundles/statistics/statsgrid/resources/locale/is.js b/bundles/statistics/statsgrid/resources/locale/is.js index 6dd6d0ebe8..776bba2803 100644 --- a/bundles/statistics/statsgrid/resources/locale/is.js +++ b/bundles/statistics/statsgrid/resources/locale/is.js @@ -165,12 +165,7 @@ Oskari.registerLocalization({ "regionsetsIsEmpty": "Area selections could not be fetched for chosen data.", "regionsDataError": "Error occurred in area value search.", "regionsDataIsEmpty": "Area values could not be fetched for chosen data.", - "datasetSave": "Error saving dataset.", - "datasetDelete": "Error deleting dataset.", - "indicatorSave": "Error saving indicator", - "myIndicatorYearInput": "Year field cannot be empty.", - "myIndicatorRegionselect": "Regionselect cannot be empty.", - "myIndicatorDatasource": "Datasource is empty.", + "datasourceIsEmpty": "Datasource is empty.", "cannotDisplayAsSeries": "Indicator cannot be inspected as a series.", "noDataForIndicators": "Service did not return data for {indicators, plural, one {the indicator} other {indicators}}" }, @@ -245,33 +240,6 @@ Oskari.registerLocalization({ } }, "userIndicators": { - "flyoutTitle": "Add new indicator", - "buttonTitle": "Add new indicator", - "buttonAddIndicator": "Add data", - "panelGeneric": { - "title": "Indicator data", - "formName": "Name", - "formDescription": "Description", - "formDatasource": "Datasource" - }, - "panelData": { - "title": "Statistical data" - }, - "dialog": { - "successTitle": "Saved", - "successMsg": "The data has been saved. Add the indicator to the map using the statistics search functionality." - }, - "import": { - "title": "Import from the clipboard", - "placeholder": "Enter the indicators data here. Each row should contain a region and it's value. Enter the region's name or id. Use semicolon as a separator. Data can be imported in following formats:\nSample 1: Helsinki;1234\nSample 2: 011;5678" - }, - "notLoggedInTitle": "Warning", - "notLoggedInWarning": "Without logging in the data cannot be saved and it will only be available until page reload. Log in before adding the indicator to preserve the data.", - "modify": { - "title": "Indicator", - "edit": "Edit", - "remove": "Remove" - } }, "indicatorList": { "title": "Indicators", diff --git a/bundles/statistics/statsgrid/resources/locale/ru.js b/bundles/statistics/statsgrid/resources/locale/ru.js index eba3abaa88..74f0b7a270 100644 --- a/bundles/statistics/statsgrid/resources/locale/ru.js +++ b/bundles/statistics/statsgrid/resources/locale/ru.js @@ -168,12 +168,7 @@ Oskari.registerLocalization( "indicatorMetadataIsEmpty": "Выборка данных отсутствует.", "regionsetsIsEmpty": "Не удалось выбрать области для выборки данных.", "regionsDataError": "Произошла ошибка при поиске значения области.", - "regionsDataIsEmpty": "Невозможно получить значения области для выбранных данных.", - "datasetSave": "Ошибка сохранения набора данных.", - "datasetDelete": "Ошибка удаления набора данных.", - "indicatorSave": "Индикатор сохранения ошибок", - "myIndicatorYearInput": "Поле год не может быть пустым.", - "myIndicatorRegionselect": "Поле выбора региона не может быть пустым." + "regionsDataIsEmpty": "Невозможно получить значения области для выбранных данных." }, "datacharts": { "flyout": "Искомые данные", @@ -229,46 +224,41 @@ Oskari.registerLocalization( }, "popup": { "deletetitle": "Удалить индикатор", - "deletemsg": "Вы удаляете индикатор \"{name}\". Вы действительно хотите удалить индикатор?", - "deleteSuccess": "Индикатор удален" - }, - "button": { - "cancel": "Отменить", - "ok": "OK" - }, - "error": { - "title": "Ошибка", - "notfound": "Индикатор не найден.", - "notdeleted": "Индикатор не был удален." + "deletemsg": "Вы удаляете индикатор \"{name}\". Вы действительно хотите удалить индикатор?" } }, "userIndicators": { - "flyoutTitle": "Добавить новый индикатор", - "buttonTitle": "Добавить новый индикатор", - "buttonAddIndicator": "Добавить данные", - "panelGeneric": { + "add": "Добавить новый индикатор", + "notLoggedInWarning": "Без авторизации данные не могут быть сохранены и будут доступны только до перезагрузки страницы. Авторизируйтесь в системе перед добавлением индикатора для сохранения данных.", + "iinfo": { "title": "Индикатор данных", - "formName": "Название", - "formDescription": "Описание", - "formDatasource": "Источник данных" + "name": "Название", + "description": "Описание", + "source": "Источник данных" }, - "panelData": { + "datasets": { "title": "Статистические данные" }, - "dialog": { - "successTitle": "Сохранено", - "successMsg": "Данные сохранены." - }, "import": { "title": "", "placeholder": "" }, - "notLoggedInTitle": "Предупреждение", - "notLoggedInWarning": "Без авторизации данные не могут быть сохранены и будут доступны только до перезагрузки страницы. Авторизируйтесь в системе перед добавлением индикатора для сохранения данных.", - "modify": { - "title": "Индикатор", - "edit": "Редактировать", - "remove": "Удалить" + "success": { + "indicatorSave": "Данные сохранены.", + "indicatorDelete": "Индикатор удален", + "datasetSave": "Данные сохранены.", + "datasetDelete": "Индикатор удален" + }, + "error": { + "indicatorSave": "Индикатор сохранения ошибок", + "indicatorDelete": "Индикатор не был удален.", + "IndicatorNotfound": "Индикатор не найден.", + "datasetSave": "Ошибка сохранения набора данных.", + "datasetDelete": "Ошибка удаления набора данных." + }, + "validate": { + "year": "Поле год не может быть пустым.", + "regionset": "Поле выбора региона не может быть пустым." } }, 'indicatorList': { diff --git a/bundles/statistics/statsgrid/resources/locale/sv.js b/bundles/statistics/statsgrid/resources/locale/sv.js index 7dff8d15b9..134601f569 100644 --- a/bundles/statistics/statsgrid/resources/locale/sv.js +++ b/bundles/statistics/statsgrid/resources/locale/sv.js @@ -169,14 +169,7 @@ Oskari.registerLocalization({ 'regionsetsIsEmpty': 'Områdesindelningarna kunde inte hämtas för den valda datamängden.', 'regionsDataError': 'Ett fel uppstod vid sökningen av områdets värden.', 'regionsDataIsEmpty': 'Områdenas värden kunde inte ges till de valda datamängderna.', - 'datasetSave': 'Ett fel uppstod vid sparning av data.', - 'datasetDelete': 'Ett fel uppstod vid radering av data.', - 'indicatorSave': 'Ett fel uppstod vid sparning av egen indikator.', - 'myIndicatorNameInput': 'Namnfält kan inte vara tom.', - 'myIndicatorYearInput': 'Årsfält kan inte vara tom.', - 'myIndicatorRegionselect': 'Områdesindelning kan inte vara tom.', - 'myIndicatorDatasource': 'Datakällan är tom.', - 'myIndicatorInvalidData': 'Datamängder har ogiltiga värden.', + 'datasourceIsEmpty': 'Datakällan är tom.', 'cannotDisplayAsSeries': 'Indikatorn kan inte inspekteras som en serie.', 'noDataForIndicators': 'Tjänsten returnerade ingen data för {indicators, plural, one {indikatorn} other {indikatorer}}', 'onlyPartialDataForIndicators': 'Tjänsten returnerade inte alla data för {indicators, plural, one {indikatorn} other {indikatorer}}' @@ -242,35 +235,25 @@ Oskari.registerLocalization({ }, 'popup': { 'deletetitle': 'Ta bort indikatorn', - 'deletemsg': 'Vill du ta bort indikatorn "{name}"?', - 'deleteSuccess': 'Indikatorn borttagen' - }, - 'button': { - 'cancel': 'Avbryt', - 'ok': 'OK' - }, - 'error': { - 'title': 'Fel', - 'notfound': 'Indikator saknas', - 'notdeleted': 'Borttagning av indikatorn misslyckades' + 'deletemsg': 'Vill du ta bort indikatorn "{name}"?' } }, 'userIndicators': { - 'flyoutTitle': 'Skapa indikator', - 'buttonTitle': 'Skapa indikator', - 'buttonAddIndicator': 'Ange värden', - 'panelGeneric': { + 'title': 'Mina indikatorer', + 'add': 'Skapa indikator', + 'edit': 'Redigera indikator', + 'notLoggedInWarning': 'Som utloggad användare kommer de skapade indikatorerna kunna användas endast under denna session. Logga in för att spara indikatorerna.', + 'info': { 'title': 'Information', 'formName': 'Namn', 'formDescription': 'Beskrivning', 'formDatasource': 'Källa' }, - 'panelData': { - 'title': 'Statistisk information' - }, - 'dialog': { - 'successTitle': 'Sparad', - 'successMsg': 'Informationen sparad.' + 'datasets': { + 'title': 'Statistisk information', + 'dataset': 'Datamängder', + 'noIndicator': 'Tallenna indikaattorin tiedot lisätäksesi aineistoja.', + 'noDatasets': 'Indikaattorilla ei ole tallennettuja aineistoja.' }, 'import': { 'title': 'Hämta från urklipp', @@ -279,12 +262,25 @@ Oskari.registerLocalization({ 'Exempel 1: Helsinki;1234 \n' + 'Exempel 2: 011;5678' }, - 'notLoggedInTitle': 'Varning', - 'notLoggedInWarning': 'Som utloggad användare kommer de skapade indikatorerna kunna användas endast under denna session. Logga in för att spara indikatorerna.', - 'modify': { - 'title': 'Indikator', - 'edit': 'Redigera', - 'remove': 'Ta bort' + 'success': { + 'indicatorSave': 'Indikatorns information sparad', + 'indicatorDelete': 'Indikatorn borttagen', + 'datasetSave': 'Datamängder har sparats', + 'datasetDelete': 'Datamängder har tagits bort', + }, + 'error': { + 'indicatorSave': 'Ett fel uppstod vid sparning av egen indikator.', + 'indicatorDelete': 'Borttagning av indikatorn misslyckades', + 'indicatorNotfound': 'Indikator saknas', + 'datasetSave': 'Ett fel uppstod vid sparning av data', + 'datasetDelete': 'Ett fel uppstod vid radering av data.' + }, + 'validate': { + 'name': 'Namnfält kan inte vara tom', + 'year': 'Årsfält kan inte vara tom', + 'regionset': 'Områdesindelning kan inte vara tom', + 'noData': 'Datamängder har inga värden', + 'invalidData': 'Datamängder har ogiltiga värden', } }, 'indicatorList': { From 319cd581830d12d51ffabff0f1ef829d8fd34f23 Mon Sep 17 00:00:00 2001 From: okauppinen <olli.kauppinen@nls.fi> Date: Wed, 13 Mar 2024 23:40:39 +0200 Subject: [PATCH 022/181] update localization keys --- .../statistics/statsgrid/MyIndicatorsList.jsx | 2 +- .../statsgrid/handler/IndicatorFormHandler.js | 37 +++++++------------ .../handler/SearchIndicatorOptionsHelper.js | 2 +- .../statsgrid/view/Form/IndicatorInfo.jsx | 6 +-- .../statsgrid/view/search/SearchFlyout.jsx | 3 +- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/bundles/statistics/statsgrid/MyIndicatorsList.jsx b/bundles/statistics/statsgrid/MyIndicatorsList.jsx index 59855644c8..5c3b39f4fc 100644 --- a/bundles/statistics/statsgrid/MyIndicatorsList.jsx +++ b/bundles/statistics/statsgrid/MyIndicatorsList.jsx @@ -75,7 +75,7 @@ export const MyIndicatorsList = ({ controller, indicators = [], loading }) => { <> <ButtonContainer> <Button type='primary' onClick={() => controller.addNewIndicator()}> - <Message messageKey='userIndicators.buttonTitle' /> + <Message messageKey='userIndicators.add' /> </Button> </ButtonContainer> <Table diff --git a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js index e37da05744..6155a9bf54 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js +++ b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js @@ -1,5 +1,4 @@ import { StateHandler, controllerMixin, Messaging } from 'oskari-ui/util'; - import { getHashForIndicator } from '../helper/StatisticsHelper'; import { getIndicatorMetadata, getIndicatorData, saveIndicator, saveIndicatorData, deleteIndicator } from './IndicatorHelper'; import { getRegionsAsync } from '../helper/RegionsHelper'; @@ -25,12 +24,6 @@ class IndicatorFormController extends StateHandler { this.reset(); await this.preparePopupData(ds, id); this.instance.getViewHandler()?.show('indicatorForm'); - if (!id && !Oskari.user().isLoggedIn()) { - Messaging.warn({ - duration: 10, - content: this.loc('userIndicators.notLoggedInWarning') - }); - } } showClipboardPopup () { @@ -44,7 +37,7 @@ class IndicatorFormController extends StateHandler { selection: '', regionset: null, dataByRegions: [], - showDataTable: 'todo add button', + showDataTable: false, loading: false }; } @@ -90,19 +83,19 @@ class IndicatorFormController extends StateHandler { addStatisticalData () { const { selection, regionset } = this.getState(); if (selection.length === 0 || isNaN(selection)) { - Messaging.error(this.loc('errors.myIndicatorYearInput')); + Messaging.error(this.loc('userIndicators.validate.year')); return; } if (!regionset) { - Messaging.error(this.loc('errors.myIndicatorRegionselect')); + Messaging.error(this.loc('userIndicators.validate.regionset')); return; } this.showDataTable(); } - updateRegionValue (id, value) { + updateRegionValue (key, value) { const dataByRegions = this.getState().dataByRegions - .map(region => region.id === id ? {...region, value } : region); + .map(region => region.key === key ? {...region, value } : region); this.updateState({ dataByRegions }); } @@ -117,11 +110,10 @@ class IndicatorFormController extends StateHandler { try { data = await getIndicatorData({...indicator, selections }, regionset); } catch (e) { - console.log(e.message); // no data saved for selections - // TODO: handle getIndicatorData properly } } + // use key to use as datasource for Table const dataByRegions = regions .map(({name, id}) => ({key: id, name, value: data[id]})) .sort((a, b) => a.name.localeCompare(b.name)); @@ -149,10 +141,10 @@ class IndicatorFormController extends StateHandler { this.updateState({ indicator: updated, loading: false }); this.notifyCacheUpdate(updated); this.log.info(`Saved indicator with id: ${id}`, updated); - Messaging.success(this.loc('userIndicators.dialog.successMsg')); + Messaging.success(this.loc('userIndicators.success.indicatorSave')); } catch (error) { this.updateState({ loading: false }); - Messaging.error(this.loc('errors.indicatorSave')); + Messaging.error(this.loc('userIndicators.error.indicatorSave')); } } async saveData () { @@ -162,8 +154,7 @@ class IndicatorFormController extends StateHandler { const indicator = { ...this.getState().indicator, selections }; if (typeof indicator.name !== 'string' || indicator.name.trim().length === 0) { - // TODO: disable button, mark name as mandatory? - Messaging.warn(this.loc('errors.myIndicatorNameInput')); + Messaging.warn(this.loc('userIndicators.validate.name')); return; } const data = {}; @@ -178,14 +169,14 @@ class IndicatorFormController extends StateHandler { data[key] = Number(valString); }); if (!Object.keys(data).length) { - Messaging.warn(this.loc('errors.myIndicatorNoData')); + Messaging.warn(this.loc('userIndicators.validate.noData')); return; } try { await saveIndicatorData(indicator, data, regionset); const indicatorInfo = `Indicator: ${indicator.id}, selection: ${selection}, regionset: ${regionset}.`; this.log.info('Saved data form values', data, indicatorInfo); - Messaging.success(this.loc('userIndicators.dialog.successMsg')); + Messaging.success(this.loc('userIndicators.success.datasetSave')); // add indicator only when data is saved const dataset = {...selections, regionset }; this.selectIndicator(dataset); @@ -195,7 +186,7 @@ class IndicatorFormController extends StateHandler { this.notifyCacheUpdate(indicator); } catch (error) { this.updateSate({ loading: false }); - Messaging.error(this.loc('errors.indicatorSave')); + Messaging.error(this.loc('userIndicators.error.datasetSave')); } } @@ -261,9 +252,9 @@ class IndicatorFormController extends StateHandler { } try { await deleteIndicator(indicator, item.regionset); - Messaging.success(this.loc('tab.popup.deleteSuccess')); + Messaging.success(this.loc('userIndicators.success.datasetDelete')); } catch (error) { - Messaging.error(this.loc('errors.datasetDelete')); + Messaging.error(this.loc('userIndicators.error.datasetDelete')); } this.preparePopupData(indicator); this.notifyCacheUpdate(indicator); diff --git a/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js b/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js index bc1ea5a19e..d0be393ce8 100644 --- a/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js +++ b/bundles/statistics/statsgrid/handler/SearchIndicatorOptionsHelper.js @@ -77,7 +77,7 @@ export const updateIndicatorListInCache = (indicator, regionsetId) => { export const populateIndicatorOptions = async (datasourceId, successCallback, errorCallback) => { if (!datasourceId) { if (typeof errorCallback === 'function') { - errorCallback('errors.myIndicatorDatasource'); + errorCallback('errors.datasourceIsEmpty'); } return; } diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx index 398b01a160..32a5ae88af 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx @@ -20,18 +20,18 @@ export const IndicatorInfo = ({ state, controller }) => { return ( <Content> <StyledInput - placeholder={Oskari.getMsg(BUNDLE_KEY, 'userIndicators.panelGeneric.formName')} + placeholder={Oskari.getMsg(BUNDLE_KEY, 'userIndicators.info.name')} value={name} onChange={(e) => controller.updateIndicator('name', e.target.value)} /> <StyledTextArea - placeholder={Oskari.getMsg(BUNDLE_KEY, 'userIndicators.panelGeneric.formDescription')} + placeholder={Oskari.getMsg(BUNDLE_KEY, 'userIndicators.info.description')} rows={2} value={description} onChange={(e) => controller.updateIndicator('description', e.target.value)} /> <StyledInput - placeholder={Oskari.getMsg(BUNDLE_KEY, 'userIndicators.panelGeneric.formDatasource')} + placeholder={Oskari.getMsg(BUNDLE_KEY, 'userIndicators.info.source')} value={source} onChange={(e) => controller.updateIndicator('source', e.target.value)} /> diff --git a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx index c7a9f15b47..106b04bb6a 100644 --- a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx +++ b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx @@ -41,8 +41,7 @@ const IndicatorField = styled('div')` const UserIndicator = ({isSingle, onClick}) => { const type = isSingle ? 'edit' : 'add'; - const title = isSingle ? 'userIndicators.modify.edit' : 'userIndicators.buttonTitle'; - return <UserIndicatorButton bordered type={type} title={<Message messageKey={title} />} onClick={onClick} /> + return <UserIndicatorButton bordered type={type} title={<Message messageKey={`userIndicators.${type}`} />} onClick={onClick} /> }; // For preventing checkbox clickable area from stretching to 100% of content width From 955c82b22dc3ca0cd34cf503b1eaa0c8fc6c76f1 Mon Sep 17 00:00:00 2001 From: okauppinen <olli.kauppinen@nls.fi> Date: Thu, 14 Mar 2024 00:31:03 +0200 Subject: [PATCH 023/181] typo fixes --- bundles/statistics/statsgrid/resources/locale/en.js | 2 +- bundles/statistics/statsgrid/resources/locale/sv.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/statistics/statsgrid/resources/locale/en.js b/bundles/statistics/statsgrid/resources/locale/en.js index edfc328751..ae96280ca9 100644 --- a/bundles/statistics/statsgrid/resources/locale/en.js +++ b/bundles/statistics/statsgrid/resources/locale/en.js @@ -247,7 +247,7 @@ Oskari.registerLocalization({ 'title': 'Indicator data', 'name': 'Name', 'description': 'Description', - 'datasource': 'Datasource' + 'source': 'Source' }, 'datasets': { 'title': 'Statistical data', diff --git a/bundles/statistics/statsgrid/resources/locale/sv.js b/bundles/statistics/statsgrid/resources/locale/sv.js index 134601f569..2ab7c6c449 100644 --- a/bundles/statistics/statsgrid/resources/locale/sv.js +++ b/bundles/statistics/statsgrid/resources/locale/sv.js @@ -245,9 +245,9 @@ Oskari.registerLocalization({ 'notLoggedInWarning': 'Som utloggad användare kommer de skapade indikatorerna kunna användas endast under denna session. Logga in för att spara indikatorerna.', 'info': { 'title': 'Information', - 'formName': 'Namn', - 'formDescription': 'Beskrivning', - 'formDatasource': 'Källa' + 'name': 'Namn', + 'description': 'Beskrivning', + 'source': 'Källa' }, 'datasets': { 'title': 'Statistisk information', From b77056c01023f268b04f67ecc41e5c47f1bbcbac Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 25 Sep 2024 12:18:08 +0300 Subject: [PATCH 024/181] update localization --- bundles/framework/oskariui/resources/locale/en.js | 3 ++- bundles/framework/oskariui/resources/locale/fi.js | 3 ++- bundles/framework/oskariui/resources/locale/sv.js | 3 ++- bundles/mapping/layerswipe/resources/locale/en.js | 14 ++++++++------ bundles/mapping/layerswipe/resources/locale/fi.js | 14 ++++++++------ bundles/mapping/layerswipe/resources/locale/sv.js | 14 ++++++++------ 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/bundles/framework/oskariui/resources/locale/en.js b/bundles/framework/oskariui/resources/locale/en.js index 661d1efd4b..e21d57cdc4 100644 --- a/bundles/framework/oskariui/resources/locale/en.js +++ b/bundles/framework/oskariui/resources/locale/en.js @@ -22,7 +22,8 @@ Oskari.registerLocalization({ clear: 'Clear', accept: 'Accept', reject: 'Reject', - info: 'Show more information' + info: 'Show more information', + move: 'Move' }, messages: { confirm: 'Are you sure you want to continue?', diff --git a/bundles/framework/oskariui/resources/locale/fi.js b/bundles/framework/oskariui/resources/locale/fi.js index 0aa96a0f58..7d85f98aa1 100644 --- a/bundles/framework/oskariui/resources/locale/fi.js +++ b/bundles/framework/oskariui/resources/locale/fi.js @@ -22,7 +22,8 @@ Oskari.registerLocalization({ clear: 'Tyhjennä', accept: 'Hyväksy', reject: 'Hylkää', - info: 'Näytä lisätietoa' + info: 'Näytä lisätietoa', + move: 'Siirry' }, messages: { confirm: 'Haluatko varmasti jatkaa?', diff --git a/bundles/framework/oskariui/resources/locale/sv.js b/bundles/framework/oskariui/resources/locale/sv.js index 28dfe34a51..8542e80d14 100644 --- a/bundles/framework/oskariui/resources/locale/sv.js +++ b/bundles/framework/oskariui/resources/locale/sv.js @@ -22,7 +22,8 @@ Oskari.registerLocalization({ clear: 'Rensa', accept: 'Acceptera', reject: 'Avvisa', - info: 'Visa mera information' + info: 'Visa mera information', + move: 'Flytta' }, messages: { confirm: 'Är du säker på att du vill fortsätta?', diff --git a/bundles/mapping/layerswipe/resources/locale/en.js b/bundles/mapping/layerswipe/resources/locale/en.js index edeab4b739..c499001267 100644 --- a/bundles/mapping/layerswipe/resources/locale/en.js +++ b/bundles/mapping/layerswipe/resources/locale/en.js @@ -4,12 +4,14 @@ Oskari.registerLocalization({ value: { toolLayerSwipe: 'Swipe: Compare the topmost map layer with other map layers', alert: { - ok: 'OK', - move: 'Move', - swipeNoRasterTitle: 'There is no layer selected to use with the map swipe tool', - swipeNoRasterMessage: 'Set a map layer visible.', - swipeLayerNotVisibleTitle: 'Swipe feature is not working in the current view', - swipeLayerNotVisibleMessage: 'The topmost layer is not visible in the current view. Move to a location where it can be seen.' + noRaster: { + title: 'There is no layer selected to use with the map swipe tool', + message: 'Set a map layer visible.' + }, + notVisible: { + title: 'Swipe feature is not working in the current view', + message: 'The topmost layer is not visible in the current view. Move to a location where it can be seen.' + } }, tool: { "label": "Compare map layers", diff --git a/bundles/mapping/layerswipe/resources/locale/fi.js b/bundles/mapping/layerswipe/resources/locale/fi.js index a16d936a41..afaaddaa9b 100644 --- a/bundles/mapping/layerswipe/resources/locale/fi.js +++ b/bundles/mapping/layerswipe/resources/locale/fi.js @@ -4,12 +4,14 @@ Oskari.registerLocalization({ value: { toolLayerSwipe: 'Vertaa ylintä karttatasoa alempiin karttatasoihin', alert: { - ok: 'OK', - move: 'Siirry', - swipeNoRasterTitle: 'Yhtään karttatasoa ei ole valittuna käytettäväksi vertailutyökalun kanssa', - swipeNoRasterMessage: 'Aseta karttatasovalikosta taso näkyväksi', - swipeLayerNotVisibleTitle: 'Karttatasojen vertailu ei toimi tässä näkymässä', - swipeLayerNotVisibleMessage: 'Ylin karttataso ei näy tässä näkymässä. Siirry sijaintiin, jossa se näkyy.' + noRaster: { + title: 'Yhtään karttatasoa ei ole valittuna käytettäväksi vertailutyökalun kanssa', + message: 'Aseta karttatasovalikosta taso näkyväksi' + }, + notVisible: { + title: 'Karttatasojen vertailu ei toimi tässä näkymässä', + message: 'Ylin karttataso ei näy tässä näkymässä. Siirry sijaintiin, jossa se näkyy.' + } }, tool: { "label": "Tasojen vertailutyökalu", diff --git a/bundles/mapping/layerswipe/resources/locale/sv.js b/bundles/mapping/layerswipe/resources/locale/sv.js index aa1662e3cf..a9637965da 100644 --- a/bundles/mapping/layerswipe/resources/locale/sv.js +++ b/bundles/mapping/layerswipe/resources/locale/sv.js @@ -4,12 +4,14 @@ Oskari.registerLocalization({ value: { toolLayerSwipe: 'Jämför kartlagret högst uppe med nedanstående kartlager', alert: { - ok: 'OK', - move: 'Flytta', - swipeNoRasterTitle: 'Inget kartlager har valts för jämföring', - swipeNoRasterMessage: 'Lägg till kartlager i kartvyn.', - swipeLayerNotVisibleTitle: 'Jämförelse av kartlager fungerar inte i vyn', - swipeLayerNotVisibleMessage: 'Den kartlagret högst uppe kan inte ses i vyn.' + noRaster: { + title: 'Inget kartlager har valts för jämföring', + message: 'Lägg till kartlager i kartvyn.' + }, + notVisible: { + title: 'Jämförelse av kartlager fungerar inte i vyn', + message: 'Den kartlagret högst uppe kan inte ses i vyn.' + } }, tool: { "label": "Jämför kartlagret högst uppe med nedanstående kartlager", From 37fb18e3ce88cbff1b5f226cc09b2e41e53659fe Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 25 Sep 2024 12:57:43 +0300 Subject: [PATCH 025/181] add state handler --- .../layerswipe/handler/SwipeHandler.js | 201 +++++++++++ bundles/mapping/layerswipe/instance.js | 326 +++--------------- 2 files changed, 240 insertions(+), 287 deletions(-) create mode 100644 bundles/mapping/layerswipe/handler/SwipeHandler.js diff --git a/bundles/mapping/layerswipe/handler/SwipeHandler.js b/bundles/mapping/layerswipe/handler/SwipeHandler.js new file mode 100644 index 0000000000..17ab7237b6 --- /dev/null +++ b/bundles/mapping/layerswipe/handler/SwipeHandler.js @@ -0,0 +1,201 @@ +import React from 'react'; +import ReactDOM, { unmountComponentAtNode } from 'react-dom'; +import { StateHandler, controllerMixin, ThemeProvider } from 'oskari-ui/util'; +import { showAlertPopup } from '../view/AlertPopup'; +import { LayerSwipe, SPLITTER_WIDTH } from '../view/LayerSwipe'; +import { getRenderPixel } from 'ol/render'; +import { unByKey } from 'ol/Observable'; + +const Alerts = { + NO_RASTER: 'noRaster', + NOT_VISIBLE: 'notVisible' +}; + +class UIHandler extends StateHandler { + constructor (instance) { + super(); + this.instance = instance; + const mapSize = this.getMapSize(); + this.setState({ + active: false, + layerId: null, + position: (mapSize.width - SPLITTER_WIDTH) / 2, + mapSize + }); + this.alertPopupControls = null; + this.element = null; + this.eventListenerKeys = []; + this.addStateListener(() => this.render()); + }; + + getMapSize () { + return this.instance.getMapModule().getSize(); + } + + initTopmostLayer () { + const { layerId: current } = this.getState(); + const sb = this.instance.getSandbox(); + const layer = sb.findAllSelectedMapLayers().findLast(l => l.isVisible()); + const layerId = layer?.getId(); + if (layerId && current === layerId) { + // no need to update + return; + } + this.unregisterEventListeners(); + if (!layerId) { + this.showAlert(Alerts.NO_RASTER); + } + + const olLayers = this.instance.getMapModule().getOLMapLayers(layerId) || []; + this.registerEventListeners(olLayers); + this.checkVisibility(layer); + this.updateState({ layerId }); + } + + checkVisibility (layer) { + if (!layer) { + return; + } + const { inScale, geometryMatch, unsupported } = layer.getVisibilityInfo(); + if (unsupported) { + const message = unsupported.getDescription() + ' ' + unsupported.getActionText(); + const action = unsupported.getAction(); + this.showAlert(Alerts.NOT_VISIBLE, action, message); + } + if (!inScale || !geometryMatch) { + const zoomToExtent = !geometryMatch; + const action = () => this.instance.getSandbox().postRequestByName('MapModulePlugin.MapMoveByLayerContentRequest', [layer.getId(), zoomToExtent]); + this.showAlert(Alerts.NOT_VISIBLE, action); + } + } + + onMapLayerEvent () { + if (this.getState().active) { + this.initTopmostLayer(); + } + } + + onMapSizeChange () { + const { active, position } = this.getState(); + if (!active) { + return; + } + const mapSize = this.getMapSize(); + const max = mapSize.width - SPLITTER_WIDTH; + if (position < max) { + this.updateState({ mapSize }); + return; + } + this.updateState({ mapSize, position: max }); + } + + setActive (active) { + if (this.getState().active === active) { + return; + } + const mapModule = this.instance.getMapModule(); + const root = mapModule.getMapDOMEl(); + if (active) { + if (!this.element) { + this.element = document.createElement('div'); + this.element.classList.add('oskari-react-splitter-container'); + root.appendChild(this.element); + } + this.initTopmostLayer(); + } else if (!active && this.element) { + unmountComponentAtNode(this.element); + root.removeChild(this.element); + this.element = null; + } + mapModule.getMap().render(); + this.instance.getSandbox().getService('Oskari.mapframework.service.VectorFeatureService')?.setHoverEnabled(!active); + this.updateState({ active }); + } + + toggleTool () { + const { active } = this.getState(); + this.setActive(!active); + } + + render () { + if (!this.element) { + return; + } + ReactDOM.render( + <ThemeProvider> + <LayerSwipe { ...this.getState() } controller={this.getController()} isMobile={Oskari.util.mouseExists()}/> + </ThemeProvider>, this.element); + } + + resetPosition () { + const { width } = this.getMapSize(); + const position = (width - SPLITTER_WIDTH) / 2; + this.setPosition(position); + } + + setPosition (position) { + const { layerId } = this.getState(); + this.instance.getMapModule().getMap().render(); + this.instance.getSandbox().postRequestByName('GetInfoPlugin.SwipeStatusRequest', [layerId, position]); + this.updateState({ position }); + } + + showAlert (type, action, message) { + if (this.alertPopupControls) { + this.alertPopupControls.close(); + } + this.alertPopupControls = showAlertPopup(type, action, message, () => this.closeAlert()); + } + + closeAlert () { + if (this.alertPopupControls) { + this.alertPopupControls.close(); + } + this.alertPopupControls = null; + } + + registerEventListeners (olLayers) { + olLayers.forEach((olLayer) => { + const prerenderKey = olLayer.on('prerender', (event) => { + const { active, position, mapSize: { height } } = this.getState(); + const ctx = event.context; + if (!active) { + ctx.restore(); + return; + } + + const tl = getRenderPixel(event, [0, 0]); + const tr = getRenderPixel(event, [position, 0]); + const bl = getRenderPixel(event, [0, height]); + const br = getRenderPixel(event, [position, height]); + + ctx.save(); + ctx.beginPath(); + ctx.moveTo(tl[0], tl[1]); + ctx.lineTo(bl[0], bl[1]); + ctx.lineTo(br[0], br[1]); + ctx.lineTo(tr[0], tr[1]); + ctx.closePath(); + ctx.clip(); + }); + this.eventListenerKeys.push(prerenderKey); + const postrenderKey = olLayer.on('postrender', (event) => { + event.context.restore(); + }); + this.eventListenerKeys.push(postrenderKey); + }); + } + + unregisterEventListeners () { + this.eventListenerKeys.forEach((key) => unByKey(key)); + this.eventListenerKeys = []; + } +} + +const wrapped = controllerMixin(UIHandler, [ + 'setActive', + 'setPosition', + 'toggleTool' +]); + +export { wrapped as SwipeHandler }; diff --git a/bundles/mapping/layerswipe/instance.js b/bundles/mapping/layerswipe/instance.js index 42130f461a..e02fda1133 100644 --- a/bundles/mapping/layerswipe/instance.js +++ b/bundles/mapping/layerswipe/instance.js @@ -1,52 +1,27 @@ -import { getRenderPixel } from 'ol/render'; -import { unByKey } from 'ol/Observable'; -const SwipeAlertTypes = { - NO_RASTER: 'noRaster', - NOT_VISIBLE: 'notVisible' -}; +import { SwipeHandler } from './handler/SwipeHandler'; Oskari.clazz.define( 'Oskari.mapframework.bundle.layerswipe.LayerSwipeBundleInstance', function () { - this.splitter = null; - this.splitterWidth = 5; - this.cropSize = null; this.mapModule = null; - this.olLayers = null; // ol layers (always an array of ol layers bound to a given oskarilayer, e.g. clustering uses two layers) - this.oskariLayerId = null; - this.popupService = null; - this.popup = null; + this.sandbox = null; this.plugin = null; - this.loc = Oskari.getMsg.bind(null, 'LayerSwipe'); - this.eventListenerKeys = []; - - this.alertTimer = null; - this.alertDebounceTime = 500; - - this.alertTitles = { - [SwipeAlertTypes.NO_RASTER]: this.loc('alert.swipeNoRasterTitle'), - [SwipeAlertTypes.NOT_VISIBLE]: this.loc('alert.swipeLayerNotVisibleTitle') - }; - this.alertMessages = { - [SwipeAlertTypes.NO_RASTER]: this.loc('alert.swipeNoRasterMessage'), - [SwipeAlertTypes.NOT_VISIBLE]: this.loc('alert.swipeLayerNotVisibleMessage') - }; - }, - { + this.loc = Oskari.getMsg.bind(null, this.getName()); + }, { __name: 'LayerSwipe', _startImpl: function (sandbox) { - this.mapModule = Oskari.getSandbox().findRegisteredModuleInstance('MainMapModule'); - this.popupService = sandbox.getService('Oskari.userinterface.component.PopupService'); + this.sandbox = sandbox; + this.mapModule = sandbox.findRegisteredModuleInstance('MainMapModule'); + this.handler = new SwipeHandler(this); - this.theme = this.mapModule.getMapTheme(); - Oskari.getSandbox().registerAsStateful(this.mediator.bundleId, this); + sandbox.registerAsStateful(this.mediator.bundleId, this); if (Oskari.dom.isEmbedded()) { - const plugin = Oskari.clazz.create('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlugin', this.conf); - this.plugin = plugin; - this.mapModule.registerPlugin(plugin); - this.mapModule.startPlugin(plugin); + this.plugin = Oskari.clazz.create('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlugin', this.conf); + this.plugin.setHandler(this.handler); + this.mapModule.registerPlugin(this.plugin); + this.mapModule.startPlugin(this.plugin); } else { const addToolButtonBuilder = Oskari.requestBuilder('Toolbar.AddToolButtonRequest'); const buttonConf = { @@ -54,8 +29,8 @@ Oskari.clazz.define( tooltip: this.loc('toolLayerSwipe'), sticky: true, callback: () => { - if (this.isActive()) { - this.activateDefaultMapTool(); + if (this.getState().active) { + this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); } else { this.setActive(true); } @@ -63,55 +38,27 @@ Oskari.clazz.define( }; sandbox.request(this, addToolButtonBuilder('LayerSwipe', 'basictools', buttonConf)); } - - if (this.isActive()) { - // when starting we need to force setup even when state is "already active" - // we need to set state.active to false and then init the functionality by setting it back to true - // otherwise eventlisteners will do wrong things - this.state.active = false; - this.setActive(true); - } - }, - - setActive: function (active) { - if (this.isActive() === active) { - // not changing state, nothing to do - return; - } - if (active) { - this.updateSwipeLayer(); - this.showSplitter(); - if (this.cropSize === null) { - this.resetMapCropping(); + if (this.state.active) { + if (!this.plugin) { + // AddToolButtonRequest prefixes group and have to use prefixed group on select tool + // It's little confusing that tool can't be selected using group which was used on add + this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', ['LayerSwipe', 'default-basictools']); } - Oskari.getSandbox().getService('Oskari.mapframework.service.VectorFeatureService').setHoverEnabled(false); - } else { - this.unregisterEventListeners(); - this.hideSplitter(); - Oskari.getSandbox().getService('Oskari.mapframework.service.VectorFeatureService').setHoverEnabled(true); - this.setSwipeStatus(null, null); + Oskari.on('app.start', () => this.setActive(true)); } - this.state = { - ...this.getState(), - active: !!active - }; - this.mapModule.getMap().render(); - // refresh the button state if we have the plugin running - this.plugin?.refresh(); }, - isActive: function () { - return !!this.getState().active; - }, - setSwipeStatus: function (layerId, cropX) { - this.oskariLayerId = layerId; - const reqSwipeStatus = Oskari.requestBuilder('GetInfoPlugin.SwipeStatusRequest')(layerId, cropX); - Oskari.getSandbox().request(this, reqSwipeStatus); + + getSandbox: function () { + return this.sandbox; }, - setState: function (newState = {}) { - this.setActive(!!newState?.active); + getMapModule: function () { + return this.mapModule; }, getState: function () { - return this.state || {}; + return this.handler?.getState() || {}; + }, + setActive: function (active) { + this.handler?.setActive(active); }, getStateParameters: function () { const { active } = this.getState(); @@ -120,224 +67,29 @@ Oskari.clazz.define( } return ''; }, - updateSwipeLayer: function (forceDeactivate) { - this.unregisterEventListeners(); - const topLayer = this.getTopmostLayer(); - - // no top layer & flag set === no layers & this is not a timing thing -> deactivate tool - if (forceDeactivate && !topLayer) { - this.setActive(false); - } - this.olLayers = topLayer?.ol || null; - if (this?.olLayers === null) { - return; - } - - if (topLayer.layerId !== null) { - this.setSwipeStatus(topLayer.layerId, this.cropSize); - } - - if (this.alertTimer) { - clearTimeout(this.alertTimer); - } - if (this?.olLayers === null) { - // When switching the background map, multiple events including - // remove, add and re-arrange will be triggered in order. The remove - // layer event causes the NO_RASTER alert to be shown when the - // background map layer itself swipe layer. Using a timer to delay - // the swipe tool deactivation and alert. - this.alertTimer = setTimeout(() => { - this.activateDefaultMapTool(); - this.showAlert(SwipeAlertTypes.NO_RASTER); - }, this.alertDebounceTime); - return; - } - this.registerEventListeners(); - }, - - activateDefaultMapTool: function () { - // reset toolbar to use the default tool - Oskari.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); - }, - getAlertPopup: function () { - if (this.popup) { - this.popup.close(); - } - this.popup = this.popupService.createPopup(); - return this.popup; - }, - showAlert: function (alertType) { - const popup = this.getAlertPopup(); - const title = this.alertTitles[alertType]; - const message = this.alertMessages[alertType]; - popup.show(title, message, [popup.createCloseButton()]); - }, - showNotVisibleAlert: function (layerId, zoomToExtent) { - const popup = this.getAlertPopup(); - const moveBtn = Oskari.clazz.create('Oskari.userinterface.component.Button'); - moveBtn.setTitle(this.loc('alert.move')); - moveBtn.setHandler(() => { - Oskari.getSandbox().postRequestByName('MapModulePlugin.MapMoveByLayerContentRequest', [layerId, zoomToExtent]); - popup.close(); - }); - const title = this.alertTitles[SwipeAlertTypes.NOT_VISIBLE]; - const message = this.alertMessages[SwipeAlertTypes.NOT_VISIBLE]; - const buttons = [moveBtn, popup.createCloseButton()]; - popup.show(title, message, buttons); - }, - isInGeometry: function (layer) { - const geometries = layer.getGeometry(); - if (!geometries || geometries.length === 0) { - // we might not have the coverage geometry so assume all is good if we don't know for sure - return true; - } - const viewBounds = this.mapModule.getCurrentExtent(); - const olExtent = [viewBounds.left, viewBounds.bottom, viewBounds.right, viewBounds.top]; - return geometries[0].intersectsExtent(olExtent); - }, - - getTopmostLayer: function () { - const layers = Oskari.getSandbox() - .findAllSelectedMapLayers() - .filter(l => l.isVisible()); - if (!layers.length) { - return null; - } - const topLayer = layers[layers.length - 1]; - const layerId = topLayer.getId(); - const isInGeometry = this.isInGeometry(topLayer); - if (!isInGeometry || !topLayer.isInScale(this.mapModule.getMapScale())) { - this.showNotVisibleAlert(layerId, !isInGeometry); - } - const olLayers = this.mapModule.getOLMapLayers(layerId); - return { - ol: olLayers.length !== 0 ? olLayers : null, - layerId - }; - }, - - registerEventListeners: function () { - if (this.olLayers === null) { - return; - } - - this.olLayers.forEach((olLayer) => { - const prerenderKey = olLayer.on('prerender', (event) => { - const ctx = event.context; - if (!this.isActive()) { - ctx.restore(); - return; - } - - const mapSize = this.mapModule.getMap().getSize(); - const tl = getRenderPixel(event, [0, 0]); - const tr = getRenderPixel(event, [this.cropSize, 0]); - const bl = getRenderPixel(event, [0, mapSize[1]]); - const br = getRenderPixel(event, [this.cropSize, mapSize[1]]); - - ctx.save(); - ctx.beginPath(); - ctx.moveTo(tl[0], tl[1]); - ctx.lineTo(bl[0], bl[1]); - ctx.lineTo(br[0], br[1]); - ctx.lineTo(tr[0], tr[1]); - ctx.closePath(); - ctx.clip(); - }); - this.eventListenerKeys.push(prerenderKey); - const postrenderKey = olLayer.on('postrender', (event) => { - event.context.restore(); - }); - this.eventListenerKeys.push(postrenderKey); - }); - }, - - unregisterEventListeners: function () { - this.eventListenerKeys.forEach((key) => unByKey(key)); - this.eventListenerKeys = []; - }, - - getSplitterElement: function () { - if (!this.splitter) { - // Use background color from theme in inline style. Fall back to paikkatietoikkuna-yellow. - // This is less than satisfying but we'll have a classier implementation when we reactify the whole component. - const splitterColor = this.theme?.color?.accent || '#ffd400'; - this.splitter = jQuery('<div class="layer-swipe-splitter" style="background-color:' + splitterColor + ';"></div>'); - this.splitter.draggable({ - containment: this.mapModule.getMapEl(), - axis: 'x', - drag: () => { - this.updateMapCropping(); - }, - stop: () => { - this.updateMapCropping(); - } - }); - } - return this.splitter; - }, - resetMapCropping: function () { - const { left: mapLeft } = this.mapModule.getMapEl().offset(); - const mapWidth = this.mapModule.getMap().getSize()[0]; - const left = (mapWidth - this.splitterWidth) / 2 + mapLeft; - this.getSplitterElement().offset({ left }); - this.updateMapCropping(); - }, - - updateMapCropping: function () { - const mapOffset = this.mapModule.getMapEl().offset(); - const splitterOffset = this.getSplitterElement().offset(); - this.cropSize = splitterOffset.left - mapOffset.left + this.splitterWidth / 2; - this.mapModule.getMap().render(); - this.setSwipeStatus(this.oskariLayerId, this.cropSize); - }, - - showSplitter: function () { - this.mapModule.getMapEl().append(this.getSplitterElement()); - }, - - hideSplitter: function () { - this.getSplitterElement().detach(); - }, - eventHandlers: { 'Toolbar.ToolSelectedEvent': function (event) { if (event.getToolId() !== 'LayerSwipe' && event.getToolId() !== 'link' && event.getToolId() !== 'save_view') { // This bundle generates state for both link and saving views, but only if it's active. // If we deactivate when link or save view tools are clicked the state is not stored as expected. // So we need to keep the swipe tool active when these tools are clicked. - this.setActive(false); + this.handler?.setActive(false); } }, - 'AfterMapLayerAddEvent': function (event) { - if (this.isActive()) { - this.updateSwipeLayer(); - } + 'AfterMapLayerAddEvent': function () { + this.handler?.onMapLayerEvent(); }, - 'AfterMapLayerRemoveEvent': function (event) { - if (this.isActive()) { - this.updateSwipeLayer(true); - } + 'AfterMapLayerRemoveEvent': function () { + this.handler?.onMapLayerEvent(); }, - 'AfterRearrangeSelectedMapLayerEvent': function (event) { - if (this.isActive()) { - this.updateSwipeLayer(); - } + 'AfterRearrangeSelectedMapLayerEvent': function () { + this.handler?.onMapLayerEvent(); }, 'MapLayerVisibilityChangedEvent': function (event) { - if (this.isActive()) { - this.updateSwipeLayer(true); - } + this.handler?.onMapLayerEvent(); }, - 'MapSizeChangedEvent': function (event) { - if (this.isActive()) { - const { left } = this.getSplitterElement().offset(); - const width = jQuery(window).width() - this.splitterWidth; - if (left > width) { - this.getSplitterElement().offset({ left: width }); - this.updateMapCropping(); - } - } + 'MapSizeChangedEvent': function () { + this.handler?.onMapSizeChange(); } } }, From e7d68bf4b3d5f56857fba3709cdb7b9fd6ff3772 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 25 Sep 2024 13:00:20 +0300 Subject: [PATCH 026/181] update function calls --- .../layerswipe/handler/SwipeToolHandler.js | 2 +- .../layerswipe/plugin/LayerSwipePlugin.js | 96 +++++++------------ 2 files changed, 38 insertions(+), 60 deletions(-) diff --git a/bundles/mapping/layerswipe/handler/SwipeToolHandler.js b/bundles/mapping/layerswipe/handler/SwipeToolHandler.js index 7ec1b67aae..e70d723f1e 100644 --- a/bundles/mapping/layerswipe/handler/SwipeToolHandler.js +++ b/bundles/mapping/layerswipe/handler/SwipeToolHandler.js @@ -40,7 +40,7 @@ class UIHandler extends StateHandler { */ setAutoStart (value) { - this.tool.getPlugin().toggleToolState(!!value); + this.tool.getPlugin().setToolState(!!value); this.updateConfig2State(); } diff --git a/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js b/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js index 5e7b3d7b16..1fdd869633 100644 --- a/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js +++ b/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js @@ -2,6 +2,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { SwipeIcon } from '../resources/icons/Swipe'; import { MapModuleButton } from '../../mapmodule/MapModuleButton'; +import { refresh } from 'less'; /** * @class Oskari.mapframework.bundle.mapmodule.plugin.LayerSelectionPlugin @@ -15,56 +16,47 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug * @method create called automatically on construction * @static */ - function (config) { - const me = this; - me._config = config || {}; - me._clazz = + function (conf) { + this.instance = null; + this._config = conf || {}; + this._clazz = 'Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlugin'; - me._defaultLocation = 'top left'; - me._index = 70; - me._name = 'LayerSwipePlugin'; - - me.initialSetup = true; - me.templates = {}; + this._defaultLocation = 'top left'; + this._index = 70; + this._name = 'LayerSwipePlugin'; + this.handler = null; + this.template = jQuery('<div class="mapplugin layerswipe"></div>'); }, { - /** - * @private @method _initImpl - * Interface method for the module protocol. Initializes the request - * handlers/templates. - */ - _initImpl: function () { - this._title = Oskari.getMsg('LayerSwipe', 'toolLayerSwipe'); - this.templates.main = jQuery('<div class="mapplugin layerswipe"></div>'); - }, _startPluginImpl: function () { this.addToPluginContainer(this._createControlElement()); this.refresh(); return true; }, - toggleToolState: function (active) { - this.getInstance()?.setActive(active); - this.refresh(); - }, - isActive: function () { - return !!this.getInstance()?.isActive(); + _stopPluginImpl: function () { + this.getInstance()?.handler.setActive(false); + this.teardownUI(); }, - resetUI: function () { + setHandler: function (handler) { + if (this.handler || !handler) { + // already set or no handler + return; + } + handler.addStateListener(() => this.refresh()); + this.handler = handler; }, getInstance: function () { - // We need instance as it manages the `active` state. - if (!this._instance) { - if (!this.sandbox) { - // wacky stuff we do since sandbox might be provided - // by mapmodule or not depending if the plugin has been started etc - this.sandbox = this.getSandbox(); - } - if (!this.sandbox) { - // just get a ref to sandbox since we really need it here to get the instance (see TODO above) - this.sandbox = Oskari.getSandbox(); - } - this._instance = this.sandbox.findRegisteredModuleInstance('LayerSwipe'); + if (!this.instance) { + this.instance = this.getSandbox()?.findRegisteredModuleInstance('LayerSwipe'); + this.setHandler(this.instance.handler); } - return this._instance; + return this.instance; + }, + isActive: function () { + const { active = false } = this.getInstance()?.getState() || {}; + return active; + }, + setToolState: function (active) { + this.getInstance()?.setActive(active); }, hasUI: function () { return !this.getConfig()?.noUI; @@ -84,14 +76,7 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug * If the placeholder doesn't exist the plugin is written to the mapmodules div element. */ _createControlElement: function () { - const el = this.templates.main.clone(); - return el; - }, - - teardownUI: function () { - // remove old element - this.toggleToolState(false); - this.removeFromPluginContainer(this.getElement()); + return this.template.clone(); }, refresh: function () { @@ -99,28 +84,21 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug if (!el) { return; } - + const active = this.isActive(); + const title = this.getInstance()?.loc('toolLayerSwipe'); ReactDOM.render( <MapModuleButton className='t_layerswipe' icon={<SwipeIcon />} visible={this.hasUI()} - title={this._title} - onClick={(e) => this.toggleToolState(!this.isActive())} - iconActive={this.isActive()} + title={title} + onClick={() => this.setToolState(!active)} + iconActive={active} position={this.getLocation()} iconSize='20px' />, el[0] ); - }, - - /** - * @method _stopPluginImpl BasicMapModulePlugin method override - * @param {Oskari.Sandbox} sandbox - */ - _stopPluginImpl: function (sandbox) { - this.teardownUI(); } }, { extend: ['Oskari.mapping.mapmodule.plugin.BasicMapModulePlugin'], From 59aea40dc247d217b92bde0bac69967482aacd3f Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 25 Sep 2024 13:16:44 +0300 Subject: [PATCH 027/181] add react components --- .../mapping/layerswipe/view/AlertPopup.jsx | 38 +++++++ .../mapping/layerswipe/view/LayerSwipe.jsx | 107 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 bundles/mapping/layerswipe/view/AlertPopup.jsx create mode 100644 bundles/mapping/layerswipe/view/LayerSwipe.jsx diff --git a/bundles/mapping/layerswipe/view/AlertPopup.jsx b/bundles/mapping/layerswipe/view/AlertPopup.jsx new file mode 100644 index 0000000000..4d7865b128 --- /dev/null +++ b/bundles/mapping/layerswipe/view/AlertPopup.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import styled from 'styled-components'; +import { showPopup } from 'oskari-ui/components/window'; +import { Message } from 'oskari-ui'; +import { LocaleProvider } from 'oskari-ui/util'; +import { PrimaryButton, SecondaryButton, ButtonContainer } from 'oskari-ui/components/buttons'; + +const BUNDLE = 'LayerSwipe'; + +const Content = styled.div` + margin: 12px 24px 24px; +`; + +export const showAlertPopup = (type, action, message, onClose) => { + const title = Oskari.getMsg(BUNDLE, `alert.${type}.title`); + const showButtons = typeof action === 'function'; + const onAction = () => { + action(); + onClose(); + }; + const content = ( + <LocaleProvider value={{ bundleKey: BUNDLE }}> + <Content> + { message + ? <span>{message}</span> + : <Message messageKey={`alert.${type}.message`}/> + } + { showButtons && + <ButtonContainer> + <SecondaryButton type='close' onClick={() => onClose()}/> + <PrimaryButton type='move' onClick={onAction}/> + </ButtonContainer> + } + </Content> + </LocaleProvider> + ); + return showPopup(title, content, onClose, { id: BUNDLE }); +}; diff --git a/bundles/mapping/layerswipe/view/LayerSwipe.jsx b/bundles/mapping/layerswipe/view/LayerSwipe.jsx new file mode 100644 index 0000000000..2179970535 --- /dev/null +++ b/bundles/mapping/layerswipe/view/LayerSwipe.jsx @@ -0,0 +1,107 @@ +import React, { useCallback, useRef } from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; +import { ThemeConsumer } from 'oskari-ui/util'; +import { getHeaderTheme } from 'oskari-ui/theme/ThemeHelper'; + +export const SPLITTER_WIDTH = 5; +export const MARGIN = { + desktop: 2, + mobile: 5 +}; + +const DragWrapper = styled.div` + position: absolute; + top: 0; + bottom: 0; + width: ${props => props.$width}px; + cursor: grab; +`; + +const Splitter = styled.div` + width: ${SPLITTER_WIDTH}px; + background-color: ${props => props.$color}; + height: 100%; + margin-left: ${props => props.isMobile ? MARGIN.mobile : MARGIN.desktop}px; +`; + +const createDraggable = (elementRef, position, limits, setPosition) => { + const element = elementRef.current; + // previousTouch is assigned in onTouchMove to track change and onMouseUp for reset + let previousTouch; + const { min, max } = limits; + const onTouchMove = (event) => { + // prevents text selection from other elements while dragging + event.preventDefault(); + if (!element) { + return; + } + const touch = event.touches[0]; + if (previousTouch) { + onGenericMove(touch.pageX - previousTouch.pageX); + }; + previousTouch = touch; + }; + const onMouseMove = (event) => { + // prevents text selection from other elements while dragging + event.preventDefault(); + if (!element) { + return; + } + onGenericMove(event.movementX); + }; + const onGenericMove = (deltaX) => { + position += deltaX; + const outFromLeft = position < min; + const outFromRight = position > max; + const outOfScreen = outFromLeft || outFromRight; + if (!outOfScreen) { + setPosition(position); + } else if (outFromLeft) { + setPosition(min); + } else if (outFromRight) { + setPosition(max); + } + }; + // window.visualViewport. + const onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + document.removeEventListener('touchmove', onTouchMove); + document.removeEventListener('touchend', onMouseUp); + document.removeEventListener('touchcancel', onMouseUp); + previousTouch = null; + }; + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + document.addEventListener('touchmove', onTouchMove, { passive: false }); + document.addEventListener('touchend', onMouseUp); + document.addEventListener('touchcancel', onMouseUp); +}; + +export const LayerSwipe = ThemeConsumer(({ theme, position, mapWidth, isMobile, controller }) => { + const elementRef = useRef(); + const helper = getHeaderTheme(theme); + + const margin = isMobile ? MARGIN.mobile : MARGIN.desktop; + const width = SPLITTER_WIDTH + 2 * margin; + const limits = { min: -margin, max: mapWidth - SPLITTER_WIDTH }; + const style = { transform: `translate(${position - margin}px`, width }; + + const onEvent = useCallback(() => createDraggable(elementRef, position, limits, controller.setPosition), [position, limits]); + return ( + <DragWrapper + ref={elementRef} + style={style} + onMouseDown={() => onEvent()} + onTouchStart={() => onEvent()} > + <Splitter $color={helper.getAccentColor()} isMobile={isMobile}/> + </DragWrapper> + ); +}); + +LayerSwipe.propTypes = { + position: PropTypes.number, + mapWidth: PropTypes.number, + controller: PropTypes.object +}; From 0b96abc1e1a644df156915039364b903f010bc0e Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 25 Sep 2024 13:17:42 +0300 Subject: [PATCH 028/181] add 'move' type --- src/react/components/buttons/PrimaryButton.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/react/components/buttons/PrimaryButton.jsx b/src/react/components/buttons/PrimaryButton.jsx index 1bb2dfa4a6..4086a2f8e3 100644 --- a/src/react/components/buttons/PrimaryButton.jsx +++ b/src/react/components/buttons/PrimaryButton.jsx @@ -14,5 +14,5 @@ export const PrimaryButton = ({ type, ...other }) => ( ); PrimaryButton.propTypes = { - type: PropTypes.oneOf(['add', 'close', 'delete', 'edit', 'save', 'yes', 'submit', 'import', 'next', 'print', 'search', 'copy']).isRequired + type: PropTypes.oneOf(['add', 'close', 'delete', 'edit', 'save', 'yes', 'submit', 'import', 'next', 'print', 'search', 'copy', 'move']).isRequired }; From 60ec3f03ee95700ecf3e4e8d9b22e79ce2df6f40 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 25 Sep 2024 13:19:57 +0300 Subject: [PATCH 029/181] simplify crop check --- bundles/mapping/mapmodule/plugin/getinfo/GetInfoPlugin.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bundles/mapping/mapmodule/plugin/getinfo/GetInfoPlugin.js b/bundles/mapping/mapmodule/plugin/getinfo/GetInfoPlugin.js index 1350b583ba..e99b9e3114 100755 --- a/bundles/mapping/mapmodule/plugin/getinfo/GetInfoPlugin.js +++ b/bundles/mapping/mapmodule/plugin/getinfo/GetInfoPlugin.js @@ -339,9 +339,8 @@ Oskari.clazz.define( let layerIds = me._buildLayerIdList(requestedLayers); const mapVO = me.getSandbox().getMap(); const px = me.getMapModule().getPixelFromCoordinate(lonlat); - - if (this._swipeStatus.cropX && this._swipeStatus.layerId) { - layerIds = layerIds.filter(l => l !== this._swipeStatus.layerId || px.x < this._swipeStatus.cropX); + if (this._swipeStatus.layerId && px.x > this._swipeStatus.cropX) { + layerIds = layerIds.filter(l => l !== this._swipeStatus.layerId); } if (layerIds.length === 0) { From d598665147b16049ce97ddeb2118b0696db30f32 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 9 Oct 2024 10:03:06 +0300 Subject: [PATCH 030/181] add TextIcon --- src/react/components/icons/TextIcon.jsx | 16 ++++++++++++++++ src/react/components/icons/index.js | 1 + 2 files changed, 17 insertions(+) create mode 100644 src/react/components/icons/TextIcon.jsx diff --git a/src/react/components/icons/TextIcon.jsx b/src/react/components/icons/TextIcon.jsx new file mode 100644 index 0000000000..a42c57f2ce --- /dev/null +++ b/src/react/components/icons/TextIcon.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import styled from 'styled-components'; + +const StyledIcon = styled.div` + font-weight: bold; + font-family: Open Sans,Arial,sans-serif; +`; + +export const TextIcon = ({iconSize, text, style={} }) => { + const modifiedStyle = iconSize ? { ...style, fontSize: iconSize } : style; + return ( + <StyledIcon style={modifiedStyle}> + {text} + </StyledIcon> + ); +}; diff --git a/src/react/components/icons/index.js b/src/react/components/icons/index.js index 4dcdce431b..b6ca97a535 100644 --- a/src/react/components/icons/index.js +++ b/src/react/components/icons/index.js @@ -12,3 +12,4 @@ export { Drag as DragIcon } from './Drag'; export { LayerIcon } from './LayerIcon'; export { EyeOpen } from './EyeOpen'; export { EyeShut } from './EyeShut'; +export { TextIcon } from './TextIcon'; From d60150cde6f83c9bf348ed67b0e89713a80e443a Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 9 Oct 2024 10:07:31 +0300 Subject: [PATCH 031/181] use TextIcon --- .../plugin/CoordinateToolPlugin.js | 9 ++------- .../mapmodule/plugin/zoombar/ZoomSlider.jsx | 20 ++++++++----------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinateToolPlugin.js b/bundles/framework/coordinatetool/plugin/CoordinateToolPlugin.js index 47d857295f..0eda962779 100755 --- a/bundles/framework/coordinatetool/plugin/CoordinateToolPlugin.js +++ b/bundles/framework/coordinatetool/plugin/CoordinateToolPlugin.js @@ -2,13 +2,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { MapModuleButton } from '../../../mapping/mapmodule/MapModuleButton'; import { CoordinatePluginHandler } from './CoordinatePluginHandler'; -import styled from 'styled-components'; +import { TextIcon } from 'oskari-ui/components/icons'; -const StyledDiv = styled('div')` - font-weight: bold; - font-family: Open Sans,Arial,sans-serif; -`; -const CoordinateIcon = () => (<StyledDiv>XY</StyledDiv>); /** * @class Oskari.mapframework.bundle.coordinatetool.plugin.CoordinateToolPlugin * Provides a coordinate display for map @@ -89,7 +84,7 @@ Oskari.clazz.define('Oskari.mapframework.bundle.coordinatetool.plugin.Coordinate className='t_coordinatetool' visible={this.hasUI()} title={this._locale('display.tooltip.tool')} - icon={<CoordinateIcon />} + icon={<TextIcon text='XY' />} onClick={() => this.handler.getController().showPopup()} iconActive={!!this.popupOpen} position={this.getLocation()} diff --git a/bundles/mapping/mapmodule/plugin/zoombar/ZoomSlider.jsx b/bundles/mapping/mapmodule/plugin/zoombar/ZoomSlider.jsx index faecf7193e..c719a59f43 100644 --- a/bundles/mapping/mapmodule/plugin/zoombar/ZoomSlider.jsx +++ b/bundles/mapping/mapmodule/plugin/zoombar/ZoomSlider.jsx @@ -5,6 +5,7 @@ import { Slider } from 'oskari-ui'; import { PlusOutlined, MinusOutlined } from '@ant-design/icons'; import { ThemeConsumer, ThemeProvider } from 'oskari-ui/util'; import { getNavigationTheme } from 'oskari-ui/theme'; +import { TextIcon } from 'oskari-ui/components/icons'; const Container = styled('div')` display: flex; @@ -13,12 +14,6 @@ const Container = styled('div')` align-items: center; margin: 0 10px 10px 10px; `; -const StyledMinus = styled(MinusOutlined)` - shape-rendering: optimizespeed; -`; -const StyledPlus = styled(PlusOutlined)` - shape-rendering: optimizespeed; -`; const StyledSlider = styled(Slider)` height: 150px; @@ -122,7 +117,6 @@ export const ZoomSlider = ({ changeZoom, zoom = 0, maxZoom, isMobile = false, .. onClick={() => { changeZoom(zoom < 100 ? zoom + 1 : 100); }} - size='32px' className='t_plus' /> <MapModuleButton @@ -130,7 +124,6 @@ export const ZoomSlider = ({ changeZoom, zoom = 0, maxZoom, isMobile = false, .. onClick={() => { changeZoom(zoom > 0 ? zoom - 1 : 0); }} - size='32px' className='t_minus' /> </MobileContainer> @@ -138,16 +131,19 @@ export const ZoomSlider = ({ changeZoom, zoom = 0, maxZoom, isMobile = false, .. } const mapModule = Oskari.getSandbox().findRegisteredModuleInstance('MainMapModule'); + // Use text icon for desktop as we are using small buttons and icons. + // Antd 5+ Minus/PlusOutlined icon rendering doesn't look nice with small buttons + // Note! minus icon text is minus sign (U+2212, not hyphen) return ( <Container> <MapModuleButton - icon={<StyledPlus />} + icon={<TextIcon text='+'/>} className='t_plus' onClick={() => { changeZoom(zoom < 100 ? zoom + 1 : 100); }} + iconSize='14px' size='18px' - iconSize='12px' noMargin /> @@ -168,13 +164,13 @@ export const ZoomSlider = ({ changeZoom, zoom = 0, maxZoom, isMobile = false, .. </ThemeProvider> <MapModuleButton - icon={<StyledMinus />} + icon={<TextIcon text='−'/>} className='t_minus' onClick={() => { changeZoom(zoom > 0 ? zoom - 1 : 0); }} + iconSize='14px' size='18px' - iconSize='12px' noMargin /> </Container> From 4ef289852ecab46f6374ba574827786f5f4e2951 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 9 Oct 2024 10:08:18 +0300 Subject: [PATCH 032/181] update antd 5.20.4 => 5.21.2 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3c7d4f08f9..150b39cab6 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "postinstall": "node webpack/hacks/postinstall.js" }, "dependencies": { - "@ant-design/icons": "5.2.6", + "@ant-design/icons": "5.5.1", "@babel/core": "7.25.2", "@babel/preset-env": "7.25.4", "@babel/preset-react": "7.24.7", @@ -42,7 +42,7 @@ "@testing-library/jest-dom": "5.7.0", "@testing-library/react": "12.1.5", "@testing-library/user-event": "14.4.3", - "antd": "5.20.4", + "antd": "5.21.2", "babel-loader": "8.3.0", "babel-plugin-styled-components": "2.1.4", "babel-plugin-transform-remove-strict-mode": "0.0.2", From c52c5baadbc8c6ab1985ae6e766f9c2f646125ca Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 9 Oct 2024 23:59:36 +0300 Subject: [PATCH 033/181] use movable container --- .../plugin/TimeControl3dPlugin.js | 177 +++++------------- .../time-control-3d/view/TimeControl3d.jsx | 42 +++-- .../view/TimeControl3dHandler.js | 6 +- bundles/mapping/time-control-3d/view/index.js | 31 ++- 4 files changed, 108 insertions(+), 148 deletions(-) diff --git a/bundles/mapping/time-control-3d/plugin/TimeControl3dPlugin.js b/bundles/mapping/time-control-3d/plugin/TimeControl3dPlugin.js index 9b1ad0ca4a..5ed436c419 100644 --- a/bundles/mapping/time-control-3d/plugin/TimeControl3dPlugin.js +++ b/bundles/mapping/time-control-3d/plugin/TimeControl3dPlugin.js @@ -1,10 +1,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { MapModuleButton } from '../../mapmodule/MapModuleButton'; -import { LocaleProvider, ThemeProvider } from 'oskari-ui/util'; -import { TimeControl3d, TimeControl3dHandler } from '../view'; +import { getPopupOptions } from '../../mapmodule/plugin/pluginPopupHelper'; +import { TimeControl3dHandler, showTimeControl3dContainer } from '../view'; import { ControlIcon } from '../view/icons'; -import { getNavigationTheme } from 'oskari-ui/theme'; const BasicMapModulePlugin = Oskari.clazz.get('Oskari.mapping.mapmodule.plugin.BasicMapModulePlugin'); /** @@ -19,110 +18,64 @@ class TimeControl3dPlugin extends BasicMapModulePlugin { this._defaultLocation = 'top right'; this._log = Oskari.log(this._name); this.loc = Oskari.getMsg.bind(null, 'TimeControl3d'); - this._toolOpen = false; - this._isMobile = Oskari.util.isMobile(); - this._element = null; this._index = 90; - this._popupContent = null; - this._popup = null; - this._mountPoint = jQuery('<div class="mapplugin time-control-3d"><div></div></div>'); - this._popupTemplate = jQuery('<div></div>'); - - const sandbox = Oskari.getSandbox(); - const mapmodule = sandbox.findRegisteredModuleInstance('MainMapModule'); - const initialTime = mapmodule.getTime ? mapmodule.getTime() : new Date(); - this.stateHandler = new TimeControl3dHandler((date, time) => { - sandbox.postRequestByName('SetTimeRequest', [date, time]); - }, initialTime); + this._popupControls = null; + this._mountPoint = jQuery('<div class="mapplugin time-control-3d"/>'); } + getName () { return this._name; } + isOpen () { - return this._toolOpen; - } - setOpen (bln) { - this._toolOpen = bln; - this.refresh(); + return !!this._popupControls; } + resetUI () { - if (this.isOpen()) { - this._toggleToolState(); - } + this.closePopup(); } - /** - * @method resetState - * Resets the state in the plugin - */ - resetState () { - this.redrawUI(Oskari.util.isMobile()); + + redrawUI () { + this.refresh(); } - redrawUI (mapInMobileMode, forced) { - if (this.getElement()) { - this.teardownUI(); - } else { - this._createUI(forced); - } + _createControlElement () { + return this._mountPoint.clone(); + } - this.refresh(); + teardownUI () { + this.removeFromPluginContainer(this.getElement()); + this.closePopup(); } _createEventHandlers () { return { TimeChangedEvent: function (event) { - this.stateHandler.update(event.getDate(), event.getTime()); + this.stateHandler?.update(event.getDate(), event.getTime()); } }; } - getElement () { - return this._element; - } - - getPopUp () { - return this._popup; - } + _startPluginImpl (sandbox) { + const sb = sandbox || Oskari.getSandbox(); + const mapmodule = sb.findRegisteredModuleInstance('MainMapModule'); + const initialTime = mapmodule.getTime ? mapmodule.getTime() : new Date(); + this.stateHandler = new TimeControl3dHandler(sb, initialTime); - teardownUI () { - const popup = this.getPopUp(); - const el = this.getElement(); - if (popup) { - popup.close(true); - } - if (!el) { - return; - } - ReactDOM.unmountComponentAtNode(el.get(0)); - this.removeFromPluginContainer(el); + this.setElement(this._createControlElement()); + this.addToPluginContainer(this.getElement()); + this.refresh(); } _stopPluginImpl () { this.teardownUI(); } - unmountReactPopup () { - ReactDOM.unmountComponentAtNode(this._popupContent.get(0)); - } - - _createUI (forced) { - const el = this._createControlElement(); - this.addToPluginContainer(el); - this.refresh(); - } - - _createControlElement () { - const el = this._mountPoint.clone(); - this._element = el; - return el; - } - _toggleToolState () { - const popup = this.getPopUp(); - if (!this.isOpen()) { - this._showPopup(); - } else if (popup) { - popup.close(true); + if (this.isOpen()) { + this.closePopup(); + } else { + this.showPopup(); } } @@ -144,61 +97,23 @@ class TimeControl3dPlugin extends BasicMapModulePlugin { ); } - renderPopup () { - const popupContent = this._popupTemplate.clone(); - ReactDOM.render( - <LocaleProvider value={{ bundleKey: 'TimeControl3d' }}> - <ThemeProvider> - <TimeControl3d {... this.stateHandler.getState()} - controller={this.stateHandler.getController()} - isMobile = {Oskari.util.isMobile()} /> - </ThemeProvider> - </LocaleProvider>, - popupContent.get(0)); - this._popupContent = popupContent; + showPopup () { + if (this.isOpen()) { + return; + } + const state = this.stateHandler.getState(); + const controller = this.stateHandler.getController(); + const options = getPopupOptions(this); + const onClose = () => this.closePopup(); + this._popupControls = showTimeControl3dContainer(state, controller, options, onClose); + this.refresh(); } - _showPopup () { - const me = this; - const popupTitle = this.loc('title'); - const mapmodule = this.getMapModule(); - const popupService = this.getSandbox().getService('Oskari.userinterface.component.PopupService'); - - this._popup = popupService.createPopup(); - this.renderPopup(); - - // create close icon - this._popup.createCloseIcon(); - this._popup.onClose(function () { - me.unmountReactPopup(); - me.setOpen(false); - me.refresh(); - }); - - const theme = mapmodule.getMapTheme(); - // TODO: Should use getHeaderTheme() to be consistent with other popups, but it doesn't look as good. - const helper = getNavigationTheme(theme); - this._popup.makeDraggable(); - this._popup.addClass('time-control-3d'); - - this._popup.show(popupTitle, this._popupContent); - const elem = this.getElement(); - - const isDark = Oskari.util.isDarkColor(helper.getPrimary()); - const popupCloseIcon = (isDark) ? 'icon-close-white' : undefined; - this._popup.setColourScheme({ - 'bgColour': helper.getPrimary(), - 'bodyBgColour': helper.getPrimary(), - 'titleColour': helper.getTextColor(), - 'opacity': 0.8, - 'iconCls': popupCloseIcon - }); - let popupLocation = this.getLocation().includes('left') ? 'right' : 'left'; - if (this._isMobile) { - popupLocation = 'bottom'; + closePopup () { + if (this._popupControls) { + this._popupControls.close(); } - this._popup.moveTo(elem, popupLocation, true); - this.setOpen(true); + this._popupControls = null; this.refresh(); } } @@ -206,7 +121,7 @@ class TimeControl3dPlugin extends BasicMapModulePlugin { Oskari.clazz.defineES('Oskari.mapping.time-control-3d.TimeControl3dPlugin', TimeControl3dPlugin, { - 'protocol': [ + protocol: [ 'Oskari.mapframework.module.Module', 'Oskari.mapframework.ui.module.common.mapmodule.Plugin' ] diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d.jsx index 073716f39a..41c570adff 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d.jsx @@ -1,6 +1,9 @@ import React, { useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; -import { Controller } from 'oskari-ui/util'; +import { Controller, ThemeConsumer } from 'oskari-ui/util'; +import { getNavigationTheme } from 'oskari-ui/theme'; +import { CloseIcon } from '../../../../src/react/components/window/CloseIcon'; +import { Message } from 'oskari-ui'; import styled from 'styled-components'; import { TimeControl } from './TimeControl3d/TimeControl'; import { DateControl } from './TimeControl3d/DateControl'; @@ -38,21 +41,32 @@ function useInterval (callback, delay) { savedCallback.current(); } if (delay !== null) { - let id = setInterval(tick, delay); + const id = setInterval(tick, delay); return () => clearInterval(id); } }, [delay]); } -const Background = styled.div(({ isMobile }) => ({ - minHeight: isMobile ? '120px !important' : '90px !imoprtant', - width: isMobile ? '260px !important' : '720px !important', - backgroundColor: '#3c3c3c', - padding: '20px', - margin: '-10px' -})); +const Background = styled.div` + cursor: grab; + width: ${props => props.isMobile ? '260px' : '720px'} !important; + background-color: ${props => props.theme.getNavigationBackgroundColor()}; + padding: 10px 20px 20px; +`; + +const Header = styled.div` + color: ${props => props.theme.getTextColor()}; + display: flex; + justify-content: space-between; + > button { + margin-top: -5px; + color: ${props => props.theme.getTextColor()}; + } +`; + +export const TimeControl3d = ThemeConsumer(({ theme, controller, date, time, isMobile, onClose }) => { + const navigationTheme = getNavigationTheme(theme); -export const TimeControl3d = ({ controller, date, time, isMobile }) => { const [timeValue, setTime] = useState(time); const [dateValue, setDate] = useState(date); const [sliderTimeValue, setSliderTime] = useState(sliderValueForTime(time)); @@ -122,7 +136,11 @@ export const TimeControl3d = ({ controller, date, time, isMobile }) => { }; return ( - <Background isMobile={isMobile}> + <Background isMobile={isMobile} theme={navigationTheme}> + <Header theme={navigationTheme}> + <h3><Message messageKey='title'/></h3> + <CloseIcon onClose={onClose}/> + </Header> <DateControl isMobile={isMobile} changeHandler={changeDate} @@ -142,7 +160,7 @@ export const TimeControl3d = ({ controller, date, time, isMobile }) => { /> </Background> ); -}; +}); TimeControl3d.propTypes = { controller: PropTypes.instanceOf(Controller).isRequired, diff --git a/bundles/mapping/time-control-3d/view/TimeControl3dHandler.js b/bundles/mapping/time-control-3d/view/TimeControl3dHandler.js index c1fcc61bce..7f6ddca5b5 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3dHandler.js +++ b/bundles/mapping/time-control-3d/view/TimeControl3dHandler.js @@ -4,9 +4,9 @@ import customParseFormat from 'dayjs/plugin/customParseFormat'; dayjs.extend(customParseFormat); class UIService extends StateHandler { - constructor (requestFn, initialDate) { + constructor (sandbox, initialDate) { super(); - this.requestFunction = requestFn; + this.sandbox = sandbox; const time = dayjs(initialDate); this.state = { time: time.format('H:mm'), @@ -28,7 +28,7 @@ class UIService extends StateHandler { } requestNewDateAndTime (date, time) { - this.requestFunction(date, time); + this.sandbox.postRequestByName('SetTimeRequest', [date, time]); } } diff --git a/bundles/mapping/time-control-3d/view/index.js b/bundles/mapping/time-control-3d/view/index.js index 4a3e562e07..48daaff7fc 100644 --- a/bundles/mapping/time-control-3d/view/index.js +++ b/bundles/mapping/time-control-3d/view/index.js @@ -1,2 +1,29 @@ -export { TimeControl3d } from './TimeControl3d'; -export { TimeControl3dHandler } from './TimeControl3dHandler.js'; +import React from 'react'; +import { LocaleProvider } from 'oskari-ui/util'; +import { TimeControl3d } from './TimeControl3d'; +import { showMovableContainer } from 'oskari-ui/components/window'; + +export { TimeControl3dHandler } from './TimeControl3dHandler'; + +const BUNDLE_KEY = 'TimeControl3d'; + +export const showTimeControl3dContainer = (state, controller, options, onClose) => { + const isMobile = Oskari.util.isMobile(); + const getComponent = state => ( + <LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> + <TimeControl3d + { ...state } + isMobile={isMobile} + controller={controller} + onClose={onClose}/> + </LocaleProvider> + ); + + const controls = showMovableContainer(getComponent(state), onClose, options); + return { + ...controls, + update: (state) => { + controls.update(getComponent(state)); + } + }; +}; From cac0a69c346780683904408f586cc34f17ae2dc4 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 10 Oct 2024 00:03:09 +0300 Subject: [PATCH 034/181] remove .scss --- bundles/mapping/time-control-3d/resources/scss/style.scss | 3 --- packages/mapping/time-control-3d/bundle.js | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 bundles/mapping/time-control-3d/resources/scss/style.scss diff --git a/bundles/mapping/time-control-3d/resources/scss/style.scss b/bundles/mapping/time-control-3d/resources/scss/style.scss deleted file mode 100644 index e985b00a50..0000000000 --- a/bundles/mapping/time-control-3d/resources/scss/style.scss +++ /dev/null @@ -1,3 +0,0 @@ -.ant-select-dropdown { - z-index: 2147483647 !important; -} diff --git a/packages/mapping/time-control-3d/bundle.js b/packages/mapping/time-control-3d/bundle.js index 617a9d5bff..176246407a 100644 --- a/packages/mapping/time-control-3d/bundle.js +++ b/packages/mapping/time-control-3d/bundle.js @@ -22,9 +22,6 @@ Oskari.clazz.define("Oskari.mapping.time-control-3d.bundle", function () { }, { "type": "text/javascript", "src": "../../../bundles/mapping/time-control-3d/tool/TimeControl3dTool.js" - }, { - "type": "text/css", - "src": "../../../bundles/mapping/time-control-3d/resources/scss/style.scss" }], "locales": [{ "lang": "fi", From 0fa5d88b9098f06a14f37b8e2ef665639ecc9052 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 15 Oct 2024 17:25:28 +0300 Subject: [PATCH 035/181] props validation and eslint fixes --- .../statsgrid/view/search/ErrorPopup.jsx | 12 ++++++++---- .../statsgrid/view/search/IndicatorCollapse.jsx | 8 ++++++++ .../statsgrid/view/search/IndicatorParams.jsx | 17 +++++++++++++++-- .../statsgrid/view/search/IndicatorRow.jsx | 6 ++++++ .../statsgrid/view/search/SearchFlyout.jsx | 16 ++++++++++++++-- 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx index 06f8bef77b..6e9282868d 100644 --- a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx +++ b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import styled from 'styled-components'; import { showPopup } from 'oskari-ui/components/window'; import { Message } from 'oskari-ui'; @@ -13,28 +14,31 @@ const Content = styled.div` padding: 20px; `; -const Popup = ({errors}) => { +const Popup = ({ errors }) => { const errorLoc = Oskari.getMsg(BUNDLE_KEY, 'errors'); return ( <Content> - {errors.map((indicator,i) => { + {errors.map((indicator, i) => { const { name, partialSeries, selections, error } = indicator; const selection = partialSeries ? getInvalidSerie(partialSeries) : getSelection(selections); const cause = errorLoc[error]; const errorMsg = cause ? `: ${cause}` : ''; - return <div key={i}>{`${name} (${selection})${errorMsg}`}</div> + return <div key={i}>{`${name} (${selection})${errorMsg}`}</div>; })} </Content> ); }; +Popup.propTypes = { + errors: PropTypes.array.isRequired +}; export const showSearchErrorPopup = (errors, isPartial) => { // no need to update const titleKey = isPartial ? 'errors.onlyPartialDataForIndicators' : 'errors.noDataForIndicators'; showPopup( - <Message messageKey={titleKey} bundleKey={BUNDLE_KEY} messageArgs={{indicators: errors.length}}/>, + <Message messageKey={titleKey} bundleKey={BUNDLE_KEY} messageArgs={{ indicators: errors.length }}/>, (<LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> <Popup errors={errors}/> </LocaleProvider>), () => {}, POPUP_OPTIONS); diff --git a/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx b/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx index 23ffc78ab1..ae53c41ee2 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Message, Collapse, CollapsePanel } from 'oskari-ui'; import { DeleteButton } from 'oskari-ui/components/buttons'; import { IndicatorRow } from './IndicatorRow'; @@ -35,3 +36,10 @@ export const IndicatorCollapse = ({ indicators = [], removeIndicator, removeAll, </StyledCollapse> ); }; + +IndicatorCollapse.propTypes = { + indicators: PropTypes.array, + removeIndicator: PropTypes.func.isRequired, + removeAll: PropTypes.func.isRequired, + showMetadata: PropTypes.func.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx index 3580861e9f..18c0493514 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Select, Message } from 'oskari-ui'; import styled from 'styled-components'; @@ -23,7 +24,7 @@ const StyledSelect = styled(Select)` width: 100%; `; -const TimeSeriesParams = ({id, options, selectedValues, controller}) => ( +const TimeSeriesParams = ({ id, options, selectedValues, controller }) => ( <Timeseries> <TimeseriesField> <b><Message messageKey='parameters.from' /></b> @@ -43,6 +44,12 @@ const TimeSeriesParams = ({id, options, selectedValues, controller}) => ( </TimeseriesField> </Timeseries> ); +TimeSeriesParams.propTypes = { + id: PropTypes.string.isRequired, + options: PropTypes.array.isRequired, + selectedValues: PropTypes.array.isRequired, + controller: PropTypes.object.isRequired +}; export const IndicatorParams = ({ state, allRegionsets, controller }) => { const { searchTimeseries, regionsetFilter, indicatorParams, selectedRegionset } = state; @@ -65,7 +72,7 @@ export const IndicatorParams = ({ state, allRegionsets, controller }) => { return ( <div> - {selectors.map(({values, time, id, label}) => { + {selectors.map(({ values, time, id, label }) => { const value = selections[id]; if (time && searchTimeseries) { return ( @@ -99,3 +106,9 @@ export const IndicatorParams = ({ state, allRegionsets, controller }) => { </div> ); }; + +IndicatorParams.propTypes = { + state: PropTypes.object.isRequired, + allRegionsets: PropTypes.array.isRequired, + controller: PropTypes.object.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/search/IndicatorRow.jsx b/bundles/statistics/statsgrid/view/search/IndicatorRow.jsx index c56a00da1e..055023d84e 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorRow.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorRow.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { IconButton } from 'oskari-ui/components/buttons'; import { InfoCircleOutlined } from '@ant-design/icons'; import { IndicatorName } from '../IndicatorName'; @@ -37,3 +38,8 @@ export const IndicatorRow = ({ indicator, removeIndicator, showMetadata }) => { </Row> ); }; +IndicatorRow.propTypes = { + indicator: PropTypes.object.isRequired, + removeIndicator: PropTypes.func.isRequired, + showMetadata: PropTypes.func.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx index bb337538e0..8bbbe45e90 100644 --- a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx +++ b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Checkbox, Select, Message, Spin } from 'oskari-ui'; import { showFlyout } from 'oskari-ui/components/window'; import { ButtonContainer, PrimaryButton, SecondaryButton, IconButton } from 'oskari-ui/components/buttons'; @@ -40,14 +41,21 @@ const IndicatorField = styled('div')` width: 100%; `; -const UserIndicator = ({isSingle, onClick}) => { +const UserIndicator = ({ isSingle, onClick }) => { const type = isSingle ? 'edit' : 'add'; const title = isSingle ? 'userIndicators.modify.edit' : 'userIndicators.buttonTitle'; - return <UserIndicatorButton bordered type={type} title={<Message messageKey={title} />} onClick={onClick} /> + return <UserIndicatorButton bordered type={type} title={<Message messageKey={title} />} onClick={onClick} />; +}; +UserIndicator.propTypes = { + isSingle: PropTypes.bool.isRequired, + onClick: PropTypes.func.isRequired }; // For preventing checkbox clickable area from stretching to 100% of content width const ClickableArea = ({ children }) => <div>{children}</div>; +ClickableArea.propTypes = { + children: PropTypes.any +}; const SearchFlyout = ({ state, controller }) => { const datasources = getDatasources(); @@ -143,6 +151,10 @@ const SearchFlyout = ({ state, controller }) => { } return Component; }; +SearchFlyout.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.func.isRequired +}; export const showSearchFlyout = (state, indicators = [], searchController, stateController, onClose) => { const title = <Message bundleKey={BUNDLE_KEY} messageKey='tile.search' />; From 7b23b7510fc5a1bea99de21573bf54a5a5615566 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 15 Oct 2024 17:29:55 +0300 Subject: [PATCH 036/181] eslint fixes --- .../components/description/MetadataPopup.jsx | 5 ++- .../statsgrid/handler/SearchHandler.js | 36 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/bundles/statistics/statsgrid/components/description/MetadataPopup.jsx b/bundles/statistics/statsgrid/components/description/MetadataPopup.jsx index 470bf2d55e..86a89bacb5 100644 --- a/bundles/statistics/statsgrid/components/description/MetadataPopup.jsx +++ b/bundles/statistics/statsgrid/components/description/MetadataPopup.jsx @@ -1,4 +1,3 @@ - import React from 'react'; import { showPopup } from 'oskari-ui/components/window'; import { Message } from 'oskari-ui'; @@ -12,7 +11,7 @@ const POPUP_OPTIONS = { export const showMedataPopup = (indicators, onClose) => { const controls = showPopup( - <Message messageKey="metadataPopup.title" bundleKey={BUNDLE_KEY} messageArgs={{indicators: indicators.length}}/>, + <Message messageKey="metadataPopup.title" bundleKey={BUNDLE_KEY} messageArgs={{ indicators: indicators.length }}/>, (<LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> <MetadataCollapse indicators={indicators}/> </LocaleProvider>), onClose, POPUP_OPTIONS); @@ -20,7 +19,7 @@ export const showMedataPopup = (indicators, onClose) => { ...controls, update: (indicators) => { controls.update( - <Message messageKey="metadataPopup.title" bundleKey={BUNDLE_KEY} messageArgs={{indicators: indicators.length}}/>, + <Message messageKey="metadataPopup.title" bundleKey={BUNDLE_KEY} messageArgs={{ indicators: indicators.length }}/>, (<LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> <MetadataCollapse indicators={indicators} /> </LocaleProvider>) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 503b45097d..f858b56154 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -1,5 +1,4 @@ import { AsyncStateHandler, controllerMixin, Messaging } from 'oskari-ui/util'; -import { showMedataPopup } from '../components/description/MetadataPopup'; import { getHashForIndicator } from '../helper/StatisticsHelper'; import { populateIndicatorOptions, validateSelectionsForSearch } from './SearchIndicatorOptionsHelper'; import { getIndicatorMetadata } from './IndicatorHelper'; @@ -126,8 +125,9 @@ class SearchController extends AsyncStateHandler { this.updateState({ searchTimeseries }); this.initParamSelections(); } + setSelectedRegionset (selectedRegionset) { - this.updateState({selectedRegionset}); + this.updateState({ selectedRegionset }); } setRegionsetFilter (value) { @@ -171,7 +171,7 @@ class SearchController extends AsyncStateHandler { } setSelectedIndicators (selectedIndicators) { - this.updateState({selectedIndicators}); + this.updateState({ selectedIndicators }); if (!selectedIndicators.length) { this.updateState({ indicatorParams: {}, selectedRegionset: null }); return; @@ -200,7 +200,7 @@ class SearchController extends AsyncStateHandler { const existingIds = existing.values.map(s => s.id); const newValues = selector.values.filter(v => !existingIds.includes(v.id)); if (newValues.length) { - existing.values = [...existing.values, ...newValues].sort((a,b) => b.value - a.value); + existing.values = [...existing.values, ...newValues].sort((a, b) => b.value - a.value); } } }); @@ -235,13 +235,13 @@ class SearchController extends AsyncStateHandler { setIndicatorParams (params) { const { indicatorParams } = this.getState(); - this.updateState({indicatorParams: {...indicatorParams, ...params}}); + this.updateState({ indicatorParams: { ...indicatorParams, ...params } }); this.initParamSelections(); } initParamSelections () { const { regionsetFilter, searchTimeseries, indicatorParams, selectedRegionset: rsId } = this.getState(); - const { selectors = [], regionsets = [], selections: current = {}} = indicatorParams; + const { selectors = [], regionsets = [], selections: current = {} } = indicatorParams; const selections = {}; const hasValidSelection = (id, allowed, time) => { const cur = current[id]; @@ -278,16 +278,16 @@ class SearchController extends AsyncStateHandler { // select first allowed value from all regionsets const allowedIds = regionsetFilter.length ? regionsets.filter(id => regionsetFilter.includes(id)) : regionsets; const selectedRegionset = allowedIds.includes(rsId) ? rsId : getRegionsets().find(rs => allowedIds.includes(rs.id))?.id; - this.updateState({ selectedRegionset, indicatorParams: {...indicatorParams, selections }}); + this.updateState({ selectedRegionset, indicatorParams: { ...indicatorParams, selections } }); } setParamSelection (param, value) { const { indicatorParams } = this.getState(); - const { selections = {}} = indicatorParams; + const { selections = {} } = indicatorParams; this.updateState({ indicatorParams: { ...indicatorParams, - selections: {...selections, [param]: value } + selections: { ...selections, [param]: value } } }); } @@ -321,10 +321,10 @@ class SearchController extends AsyncStateHandler { try { const metadata = await getIndicatorMetadata(datasourceId, indicatorId); if (!metadata) { - refinedSearchValues.push({ id: indicatorId, error: 'indicatorMetadataError'} ); + refinedSearchValues.push({ id: indicatorId, error: 'indicatorMetadataError' }); continue; } - const values = this.getRefinedSearch(indicatorId, metadata); + const values = this.getRefinedSearch(indicatorId, metadata); refinedSearchValues = [...refinedSearchValues, ...values]; } catch (error) { Messaging.error(this.loc('errors.indicatorMetadataError')); @@ -337,7 +337,7 @@ class SearchController extends AsyncStateHandler { const isPartial = errors.length !== refinedSearchValues.length; showSearchErrorPopup(errors, isPartial); } - this.updateState({loading: false}); + this.updateState({ loading: false }); } /** @@ -350,8 +350,8 @@ class SearchController extends AsyncStateHandler { */ getRefinedSearch (id, metadata) { const { indicatorParams, searchTimeseries, selectedRegionset, selectedDatasource } = this.getState(); - const { selections = {}, selectors = []} = indicatorParams; - const keyWithTime = Object.keys(selections).find((key) => selectors.find(s=> s.id === key && s.time)); + const { selections = {}, selectors = [] } = indicatorParams; + const keyWithTime = Object.keys(selections).find((key) => selectors.find(s => s.id === key && s.time)); const indSearchValues = { id, ds: selectedDatasource, @@ -385,7 +385,7 @@ class SearchController extends AsyncStateHandler { if (key === keyWithTime && searchTimeseries) { const [first, last] = values; // should always find values as keyWithTime is selected from selectors - const range = selectors.find(s=> s.id === keyWithTime).values + const range = selectors.find(s => s.id === keyWithTime).values .map(val => val.value) .filter(val => val >= first && val <= last) .reverse(); @@ -416,8 +416,8 @@ class SearchController extends AsyncStateHandler { const indicators = []; multiSelections.forEach(({ key, values, invalid }) => { values.forEach(val => { - const selections = {...indSearchValues.selections, [key]: val}; - const indicator = {...indSearchValues, selections}; + const selections = { ...indSearchValues.selections, [key]: val }; + const indicator = { ...indSearchValues, selections }; if (invalid.includes(val)) { indicator.error = 'invalidSelection'; } @@ -463,7 +463,7 @@ class SearchController extends AsyncStateHandler { indicators = [indicator]; } else { const { selectedIndicators, selectedDatasource: ds } = this.getState(); - indicators = selectedIndicators.map(id =>({ id, ds })); + indicators = selectedIndicators.map(id => ({ id, ds })); } this.instance.getViewHandler()?.openMetadataPopup(indicators); } From a68660cc598b577c5a0ad46ae57636b518ee4ef2 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 15 Oct 2024 23:25:46 +0300 Subject: [PATCH 037/181] remove CollapsePanel and use items --- .../description/MetadataCollapse.jsx | 30 +++----- .../view/search/IndicatorCollapse.jsx | 73 +++++++++++-------- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/bundles/statistics/statsgrid/components/description/MetadataCollapse.jsx b/bundles/statistics/statsgrid/components/description/MetadataCollapse.jsx index cb841a3958..3d985b2bf9 100644 --- a/bundles/statistics/statsgrid/components/description/MetadataCollapse.jsx +++ b/bundles/statistics/statsgrid/components/description/MetadataCollapse.jsx @@ -1,39 +1,27 @@ import React from 'react'; import PropTypes from 'prop-types'; import { MetadataContent } from './MetadataContent'; -import { Collapse, CollapsePanel, Message } from 'oskari-ui'; +import { Collapse, Message } from 'oskari-ui'; import { getCachedMetadata } from '../../handler/IndicatorHelper'; export const MetadataCollapse = ({ indicators }) => { // Only selected indiator (map or search) has button to show popup => metadata is always cached - const data = indicators.map(ind => getCachedMetadata(ind.ds, ind.id)); + const data = indicators.map(ind => getCachedMetadata(ind.ds, ind.id)).filter(d => d.name); if (!data.length) { return (<Message messageKey='metadataPopup.noMetadata' messageArgs={{ indicators: 1 }}/>); } // Description can include HTML so well have to wrap it as HTML content... - return ( - <Collapse defaultActiveKey={data.map(item => item.id)}> - { data.map(({ name, description, source, metadata, id }) => { - if (!name) { - return null; - } - return ( - <CollapsePanel header={name} key={id}> - <MetadataContent - description={description} - source={source} - metadata={metadata} - /> - </CollapsePanel> - ); - })} - </Collapse> - ); + const items = data.map(({ id, name, ...content }) => ({ + key: id, + label: name, + children: <MetadataContent { ...content } /> + })); + return <Collapse defaultActiveKey={data.map(item => item.id)} items={items} />; }; MetadataCollapse.propTypes = { indicators: PropTypes.arrayOf(PropTypes.shape({ ds: PropTypes.number, id: PropTypes.string - })) + })) }; diff --git a/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx b/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx index ae53c41ee2..256fda601f 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx @@ -1,45 +1,60 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Message, Collapse, CollapsePanel } from 'oskari-ui'; -import { DeleteButton } from 'oskari-ui/components/buttons'; +import { Message, Collapse } from 'oskari-ui'; +import { IconButton } from 'oskari-ui/components/buttons'; import { IndicatorRow } from './IndicatorRow'; import styled from 'styled-components'; const StyledCollapse = styled(Collapse)` margin-top: 20px; `; -const RemoveAll = styled(DeleteButton)` - display: flex; - justify-content: flex-end; - margin-top: 10px; +const RemoveAll = styled(IconButton)` + height: 20px; `; -export const IndicatorCollapse = ({ indicators = [], removeIndicator, removeAll, showMetadata }) => { - return ( - <StyledCollapse> - <CollapsePanel - header={<Message messageKey='indicatorList.title' />} - > - { !indicators.length && (<Message messageKey='indicatorList.emptyMsg' />) } - { - indicators.map((indicator) => ( - <IndicatorRow key={indicator.hash} indicator={indicator} removeIndicator={removeIndicator} showMetadata={showMetadata}/> - )) - } - { indicators.length > 1 && - <RemoveAll type='button' - tooltip={<Message messageKey='indicatorList.removeAll' />} - title={<Message messageKey='indicatorList.removeAll' />} - onConfirm={() => removeAll()}/> - } - </CollapsePanel> - </StyledCollapse> +const Content = ({ indicators = [], removeIndicator, showMetadata }) => { + if (!indicators.length) { + return <Message messageKey='indicatorList.emptyMsg' />; + } + return indicators.map(indicator => + <IndicatorRow + key={indicator.hash} + indicator={indicator} + removeIndicator={removeIndicator} + showMetadata={showMetadata}/> ); }; - -IndicatorCollapse.propTypes = { +Content.propTypes = { indicators: PropTypes.array, removeIndicator: PropTypes.func.isRequired, - removeAll: PropTypes.func.isRequired, showMetadata: PropTypes.func.isRequired }; + +const PanelExtra = ({ indicators = [], removeAll }) => { + if (indicators.length < 2) { + return null; + } + return ( + <div onClick={e => e.stopPropagation()}> + <RemoveAll + type='delete' + confirmTitle={<Message messageKey='indicatorList.removeAll' />} + title={<Message messageKey='indicatorList.removeAll' />} + onConfirm={() => removeAll()}/> + </div> + ); +}; +PanelExtra.propTypes = { + removeAll: PropTypes.func.isRequired, + indicators: PropTypes.array +}; + +export const IndicatorCollapse = (props) => { + const item = { + key: 'indicators', + label: <Message messageKey='indicatorList.title' />, + extra: <PanelExtra { ...props } />, + children: <Content { ...props } /> + }; + return <StyledCollapse items={[item]} />; +}; From 9e81f106ff062e65306c78dc459cd279502fc504 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 16 Oct 2024 00:13:53 +0300 Subject: [PATCH 038/181] use link button and eslint --- .../statsgrid/handler/IndicatorHelper.js | 5 ++--- .../statsgrid/handler/StatisticsHandler.js | 2 +- .../statsgrid/view/search/SearchFlyout.jsx | 16 +++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/IndicatorHelper.js b/bundles/statistics/statsgrid/handler/IndicatorHelper.js index 7671a999b2..8cc9ce1517 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorHelper.js +++ b/bundles/statistics/statsgrid/handler/IndicatorHelper.js @@ -3,7 +3,6 @@ import { updateIndicatorListInCache, removeIndicatorFromCache } from './SearchIn import { getRegionsAsync } from '../helper/RegionsHelper'; import { BUNDLE_KEY } from '../constants'; -const statsGridLocale = Oskari.getMsg.bind(null, 'StatsGrid'); // cache storage object const indicatorMetadataStore = {}; const indicatorDataStore = {}; @@ -123,7 +122,7 @@ const processMetadata = (meta) => { return { value, label }; }); const label = locParams[id] || id; - const selector = { id, values, time, label}; + const selector = { id, values, time, label }; if (time) { selectors.unshift(selector); } else { @@ -133,7 +132,7 @@ const processMetadata = (meta) => { const name = Oskari.getLocalized(meta.name) || ''; const source = Oskari.getLocalized(meta.source) || ''; const description = Oskari.getLocalized(meta.description) || ''; - return {...meta, name, source, description, selectors}; + return { ...meta, name, source, description, selectors }; }; export const getDataForIndicator = async (indicator, regionset) => { diff --git a/bundles/statistics/statsgrid/handler/StatisticsHandler.js b/bundles/statistics/statsgrid/handler/StatisticsHandler.js index d744f41234..436bfa079e 100644 --- a/bundles/statistics/statsgrid/handler/StatisticsHandler.js +++ b/bundles/statistics/statsgrid/handler/StatisticsHandler.js @@ -244,7 +244,7 @@ class StatisticsController extends AsyncStateHandler { this.updateState({ loading: true }); const indicatorToAdd = await this.getIndicatorToAdd(indicator, regionset); if (indicatorToAdd.data.uniqueCount === 0) { - return { error: 'noData'}; + return { error: 'noData' }; } this.instance.addDataProviderInfo(indicatorToAdd); this.updateState({ diff --git a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx index 8bbbe45e90..a1adbb3d74 100644 --- a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx +++ b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Checkbox, Select, Message, Spin } from 'oskari-ui'; +import { Checkbox, Select, Message, Spin, Button } from 'oskari-ui'; import { showFlyout } from 'oskari-ui/components/window'; import { ButtonContainer, PrimaryButton, SecondaryButton, IconButton } from 'oskari-ui/components/buttons'; import styled from 'styled-components'; @@ -40,6 +40,10 @@ const IndicatorField = styled('div')` flex-direction: column; width: 100%; `; +const MetadataButton = styled(Button)` + margin-right: auto; + padding: 0; +`; const UserIndicator = ({ isSingle, onClick }) => { const type = isSingle ? 'edit' : 'add'; @@ -122,11 +126,9 @@ const SearchFlyout = ({ state, controller }) => { </Row> </Field> {state.selectedIndicators && state.selectedIndicators.length > 0 && ( - <div> - <a onClick={() => controller.openMetadataPopup()}> - <Message messageKey='metadataPopup.open' messageArgs={{ indicators: state.selectedIndicators.length }} /> - </a> - </div> + <MetadataButton type='link' onClick={() => controller.openMetadataPopup()}> + <Message messageKey='metadataPopup.open' messageArgs={{ indicators: state.selectedIndicators.length }} /> + </MetadataButton> )} <b><Message messageKey='panels.newSearch.refineSearchLabel' /></b> <IndicatorParams @@ -153,7 +155,7 @@ const SearchFlyout = ({ state, controller }) => { }; SearchFlyout.propTypes = { state: PropTypes.object.isRequired, - controller: PropTypes.func.isRequired + controller: PropTypes.object.isRequired }; export const showSearchFlyout = (state, indicators = [], searchController, stateController, onClose) => { From 5bde193f1123e9e134a307c1dcd894623ec8e5cd Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 16 Oct 2024 00:15:24 +0300 Subject: [PATCH 039/181] new selector values fix --- bundles/statistics/statsgrid/handler/SearchHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index f858b56154..409576d72f 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -197,8 +197,8 @@ class SearchController extends AsyncStateHandler { if (!existing) { combinedSelectors.push(selector); } else { - const existingIds = existing.values.map(s => s.id); - const newValues = selector.values.filter(v => !existingIds.includes(v.id)); + const values = existing.values.map(s => s.value); + const newValues = selector.values.filter(v => !values.includes(v.value)); if (newValues.length) { existing.values = [...existing.values, ...newValues].sort((a, b) => b.value - a.value); } From 309f2104b6a833efe48cad13ce55447cc616af4a Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 16 Oct 2024 01:30:31 +0300 Subject: [PATCH 040/181] add props validation --- .../statsgrid/view/search/IndicatorParams.jsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx index 18c0493514..8d90cfd831 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx @@ -51,8 +51,14 @@ TimeSeriesParams.propTypes = { controller: PropTypes.object.isRequired }; -export const IndicatorParams = ({ state, allRegionsets, controller }) => { - const { searchTimeseries, regionsetFilter, indicatorParams, selectedRegionset } = state; +export const IndicatorParams = ({ + searchTimeseries, + regionsetFilter, + indicatorParams, + selectedRegionset, + allRegionsets, + controller +}) => { const { selectors = [], regionsets = [], selections = {} } = indicatorParams; if (!selectors.length) { return ( @@ -108,7 +114,10 @@ export const IndicatorParams = ({ state, allRegionsets, controller }) => { }; IndicatorParams.propTypes = { - state: PropTypes.object.isRequired, + searchTimeseries: PropTypes.bool.isRequired, + regionsetFilter: PropTypes.array.isRequired, + indicatorParams: PropTypes.object.isRequired, + selectedRegionset: PropTypes.number, allRegionsets: PropTypes.array.isRequired, controller: PropTypes.object.isRequired }; From 019f7cf4111c104d29d6b23ec8b66ffb64c77f9b Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 16 Oct 2024 01:32:21 +0300 Subject: [PATCH 041/181] add complete to state and remove some checks --- .../statistics/statsgrid/handler/SearchHandler.js | 12 ++++++++---- .../statsgrid/view/search/SearchFlyout.jsx | 15 ++++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 409576d72f..bd50d4a139 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -35,7 +35,8 @@ class SearchController extends AsyncStateHandler { indicatorOptions: ds && isReset ? this.getState().indicatorOptions : [], indicatorParams: {}, // selectors, selected, selections isUserDatasource: ds ? ds.type === 'user' : false, - loading: false + loading: false, // flyout is disabled while loading + complete: true // to show that more indicators will be loaded }; } @@ -77,7 +78,9 @@ class SearchController extends AsyncStateHandler { const { indicators = [], complete = false } = response; this.updateState({ indicatorOptions: this.validateIndicatorList(indicators), - loading: !complete + // set loading false if we got got some indicators or empty list on complete + loading: indicators.length === 0 && !complete, + complete }); if (complete && !isUserDatasource && !indicators.length) { // show notification about empty indicator list for non-myindicators datasource @@ -87,13 +90,14 @@ class SearchController extends AsyncStateHandler { error => { const errorMsg = this.loc('errors')[error] || this.loc('errors.indicatorListError'); Messaging.error(errorMsg); - this.updateState({ loading: false }); + this.updateState({ loading: false, complete: true }); }); } catch (error) { Messaging.error(this.loc('errors.indicatorListError')); this.updateState({ indicatorOptions: [], - loading: false + loading: false, + complete: true }); } } diff --git a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx index a1adbb3d74..b47a4e3a84 100644 --- a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx +++ b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx @@ -68,7 +68,7 @@ const SearchFlyout = ({ state, controller }) => { // Nothing to show -> show generic "data missing" message return (<b><Message messageKey='errors.indicatorListError' /></b>); } - const singleIndicatorSelected = state.selectedIndicators?.length === 1; + const singleIndicatorSelected = state.selectedIndicators.length === 1; const multipleRegionsetsAvailable = regionsets.length > 1; const multipleDatasourcesAvailable = datasources.length > 1; const Component = ( @@ -92,7 +92,7 @@ const SearchFlyout = ({ state, controller }) => { optionFilterProp='label' options={regionsets.map(rs => ({ value: rs.id, label: rs.name }))} placeholder={<Message messageKey='panels.newSearch.selectRegionsetPlaceholder' />} - value={state?.regionsetFilter} + value={state.regionsetFilter} onChange={(value) => controller.setRegionsetFilter(value)} /> </Field> @@ -103,7 +103,7 @@ const SearchFlyout = ({ state, controller }) => { <StyledSelect options={datasources.map(ds => ({ value: ds.id, label: ds.name, disabled: state.disabledDatasources.includes(ds.id) }))} placeholder={<Message messageKey='panels.newSearch.selectDatasourcePlaceholder' />} - value={state?.selectedDatasource} + value={state.selectedDatasource} onChange={(value) => controller.setSelectedDatasource(value)} /> </Field> @@ -114,11 +114,12 @@ const SearchFlyout = ({ state, controller }) => { <b><Message messageKey='panels.newSearch.indicatorTitle' /></b> <StyledSelect mode='multiple' + loading={!state.complete} optionFilterProp='label' - options={state?.indicatorOptions?.map(i => ({ value: i.id, label: i.name, disabled: !!i.disabled }))} + options={state.indicatorOptions.map(i => ({ value: i.id, label: i.name, disabled: !!i.disabled }))} placeholder={<Message messageKey='panels.newSearch.selectIndicatorPlaceholder' />} - disabled={!state?.indicatorOptions || state?.indicatorOptions?.length < 1} - value={state?.selectedIndicators} + disabled={!state.indicatorOptions.length} + value={state.selectedIndicators} onChange={(value) => controller.setSelectedIndicators(value)} /> </IndicatorField> @@ -132,8 +133,8 @@ const SearchFlyout = ({ state, controller }) => { )} <b><Message messageKey='panels.newSearch.refineSearchLabel' /></b> <IndicatorParams + { ...state } allRegionsets={regionsets} - state={state} controller={controller}/> <ButtonContainer> <SecondaryButton From 8ab02f08b2f277ec8badc342374765244a2bf4e2 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 16 Oct 2024 01:50:52 +0300 Subject: [PATCH 042/181] add confirm props --- src/react/components/buttons/IconButton.jsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/react/components/buttons/IconButton.jsx b/src/react/components/buttons/IconButton.jsx index b6d29fe2e9..f39c9aea28 100644 --- a/src/react/components/buttons/IconButton.jsx +++ b/src/react/components/buttons/IconButton.jsx @@ -29,6 +29,7 @@ const BorderlessButton = styled(Button)` color: ${props => props.$active || props.$color}; border: none; background: none; + box-shadow: none; padding: 0px; pointer-events: ${props => props.disabled ? 'none' : 'auto'}; font-size: ${props => props.$iconSize}px; @@ -87,19 +88,19 @@ const getPredefinedIcon = (type) => { return null; }; -const getConfirmProps = (type) => { +const getConfirmProps = (type, title) => { if (type === 'delete') { return { okText: <Message messageKey='buttons.delete' bundleKey='oskariui'/>, cancelText: <Message messageKey='buttons.cancel' bundleKey='oskariui'/>, - title: <Message messageKey='messages.confirmDelete' bundleKey='oskariui'/>, + title: title || <Message messageKey='messages.confirmDelete' bundleKey='oskariui'/>, okType: 'danger' }; } return { okText: <Message messageKey='buttons.yes' bundleKey='oskariui'/>, cancelText: <Message messageKey='buttons.no' bundleKey='oskariui'/>, - title: <Message messageKey='messages.confirm' bundleKey='oskariui'/> + title: title || <Message messageKey='messages.confirm' bundleKey='oskariui'/> }; }; @@ -140,6 +141,7 @@ export const IconButton = ({ title = type ? <Message messageKey={`buttons.${type}`} bundleKey='oskariui'/> : '', disabled = false, onConfirm, + confirm = {}, ...rest }) => { if (onConfirm) { @@ -151,7 +153,7 @@ export const IconButton = ({ cancelButtonProps={{className: 't_button t_cancel'}} disabled={disabled} placement={title ? 'bottom' : 'top'} - { ...getConfirmProps(type) }> + { ...getConfirmProps(type, confirm.title) }> <Tooltip title={title}> <DisabledWrapper $disabled={disabled}> <ThemeButton type={type} disabled={disabled} { ...rest }/> @@ -178,6 +180,7 @@ IconButton.propTypes = { icon: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), onClick: PropTypes.func, onConfirm: PropTypes.func, + confirm: PropTypes.object, disabled: PropTypes.bool, color: PropTypes.string, iconSize: PropTypes.number, From c499f177dc8801137e9f703f49f31b7fd4a2027b Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 16 Oct 2024 02:12:02 +0300 Subject: [PATCH 043/181] eslint --- .../statsgrid/handler/IndicatorFormHandler.js | 18 ++++++++++-------- .../statsgrid/handler/MyIndicatorsHandler.js | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js index 457a5dedc5..d5fadb6741 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js +++ b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js @@ -61,7 +61,7 @@ class IndicatorFormController extends StateHandler { indicator.name = name; indicator.description = description; indicator.source = source; - selectors.forEach(sel => regionsets.forEach(regionset => sel.allowedValues.forEach(val => datasets.push({[sel.id]: val.id, regionset})))); + selectors.forEach(sel => regionsets.forEach(regionset => sel.allowedValues.forEach(val => datasets.push({ [sel.id]: val.id, regionset })))); } catch (error) { Messaging.error(Oskari.getMsg('StatsGrid', 'errors.indicatorMetadataError')); } @@ -70,7 +70,7 @@ class IndicatorFormController extends StateHandler { } updateIndicator (key, value) { - this.updateState({ indicator: {...this.getState().indicator, [key]: value }}); + this.updateState({ indicator: { ...this.getState().indicator, [key]: value } }); } setSelection (selection) { @@ -96,7 +96,7 @@ class IndicatorFormController extends StateHandler { updateRegionValue (key, value) { const dataByRegions = this.getState().dataByRegions - .map(region => region.key === key ? {...region, value } : region); + .map(region => region.key === key ? { ...region, value } : region); this.updateState({ dataByRegions }); } @@ -109,14 +109,14 @@ class IndicatorFormController extends StateHandler { let data = {}; if (indicator.id) { try { - data = await getIndicatorData({...indicator, selections }, regionset); + data = await getIndicatorData({ ...indicator, selections }, regionset); } catch (e) { // no data saved for selections } } // use key to use as datasource for Table const dataByRegions = regions - .map(({name, id}) => ({key: id, name, value: data[id]})) + .map(({ name, id }) => ({ key: id, name, value: data[id] })) .sort((a, b) => a.name.localeCompare(b.name)); this.updateState({ dataByRegions, loading: false }); } catch (error) { @@ -133,12 +133,13 @@ class IndicatorFormController extends StateHandler { dataByRegions: [] }); } + async saveIndicator () { this.updateState({ loading: true }); const { indicator } = this.getState(); try { const id = await saveIndicator(indicator); - const updated = {...indicator, id}; + const updated = { ...indicator, id }; this.updateState({ indicator: updated, loading: false }); this.notifyCacheUpdate(updated); this.log.info(`Saved indicator with id: ${id}`, updated); @@ -148,6 +149,7 @@ class IndicatorFormController extends StateHandler { Messaging.error(this.loc('userIndicators.error.indicatorSave')); } } + async saveData () { this.updateState({ loading: true }); const { dataByRegions, regionset, selection } = this.getState(); @@ -159,7 +161,7 @@ class IndicatorFormController extends StateHandler { return; } const data = {}; - dataByRegions.forEach(({key, value}) => { + dataByRegions.forEach(({ key, value }) => { if (typeof value === 'undefined') { return; } @@ -179,7 +181,7 @@ class IndicatorFormController extends StateHandler { this.log.info('Saved data form values', data, indicatorInfo); Messaging.success(this.loc('userIndicators.success.datasetSave')); // add indicator only when data is saved - const dataset = {...selections, regionset }; + const dataset = { ...selections, regionset }; this.selectIndicator(dataset); this.updateState({ datasets: [...this.getState().datasets, dataset] }); diff --git a/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js b/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js index f62cd09766..a31364466b 100644 --- a/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js +++ b/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js @@ -81,6 +81,7 @@ class IndicatorsHandler extends AsyncStateHandler { editIndicator (id) { this.formHandler.getController().showIndicatorPopup(this.userDsId, id); } + openIndicator (indicator) { const viewHandler = this.instance.getViewHandler(); viewHandler?.openSearchWithSelections({ ds: this.userDsId, ...indicator }); From a1535adc07f561b2a13814f1f294d99836d2db5a Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 16 Oct 2024 02:16:28 +0300 Subject: [PATCH 044/181] add props validation and eslint fixes --- .../statsgrid/view/Form/ClipboardPopup.jsx | 6 +++++- .../statsgrid/view/Form/DatasetsTable.jsx | 5 +++++ .../statsgrid/view/Form/IndicatorCollapse.jsx | 7 ++++++- .../statsgrid/view/Form/IndicatorDatasets.jsx | 6 +++++- .../statsgrid/view/Form/IndicatorForm.jsx | 14 +++++++++++++- .../statsgrid/view/Form/IndicatorInfo.jsx | 5 +++++ .../statsgrid/view/Form/StatisticalData.jsx | 9 +++++++-- .../statsgrid/view/Form/StatisticalInfo.jsx | 8 ++++++-- 8 files changed, 52 insertions(+), 8 deletions(-) diff --git a/bundles/statistics/statsgrid/view/Form/ClipboardPopup.jsx b/bundles/statistics/statsgrid/view/Form/ClipboardPopup.jsx index 0665524b2d..e2dc3f766b 100644 --- a/bundles/statistics/statsgrid/view/Form/ClipboardPopup.jsx +++ b/bundles/statistics/statsgrid/view/Form/ClipboardPopup.jsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import PropTypes from 'prop-types'; import { Message, TextAreaInput } from 'oskari-ui'; import { LocaleProvider } from 'oskari-ui/util'; import { PrimaryButton, ButtonContainer, SecondaryButton } from 'oskari-ui/components/buttons'; @@ -41,9 +42,12 @@ const ClipboardPopup = ({ controller, onClose }) => { </Content> ); }; +ClipboardPopup.propTypes = { + controller: PropTypes.object.isRequired, + onClose: PropTypes.func.isRequired +}; export const showClipboardPopup = (controller, onClose) => { - const title = <Message messageKey='userIndicators.flyoutTitle' bundleKey={BUNDLE_KEY} />; return showPopup( title, diff --git a/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx b/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx index be23b5b087..3561563c02 100644 --- a/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx +++ b/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Message } from 'oskari-ui'; import { Table } from 'oskari-ui/components/Table'; import { IconButton } from 'oskari-ui/components/buttons'; @@ -67,3 +68,7 @@ export const DatasetsTable = ({ state, controller }) => { }))} pagination={false}/>; }; +DatasetsTable.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx index c210da305b..12be28c2b9 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Message, Collapse, CollapsePanel } from 'oskari-ui'; import { IndicatorInfo } from './IndicatorInfo'; import { IndicatorDatasets } from './IndicatorDatasets'; @@ -17,9 +18,13 @@ export const IndicatorCollapse = ({ state, controller }) => { // return <Collapse items={items} defaultActiveKey={defaultActiveKey}/> return ( <Collapse defaultActiveKey={defaultActiveKey}> - {items.map(({key, label, children}) => ( + {items.map(({ key, label, children }) => ( <CollapsePanel key={key} header={label}>{children}</CollapsePanel> ))} </Collapse> ); }; +IndicatorCollapse.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx index 4d0b8b3d1d..581bdb1a95 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorDatasets.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Message } from 'oskari-ui'; import { DatasetsTable } from './DatasetsTable'; import { StatisticalInfo } from './StatisticalInfo'; @@ -7,7 +8,6 @@ export const IndicatorDatasets = ({ state, controller }) => { if (!state.indicator.id) { return <Message messageKey='userIndicators.datasets.noIndicator'/>; } - return ( <div> <DatasetsTable state={state} controller={controller}/> @@ -15,3 +15,7 @@ export const IndicatorDatasets = ({ state, controller }) => { </div> ); }; +IndicatorDatasets.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx index 1c27e53ad8..5712c887b2 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Message, Button } from 'oskari-ui'; import { LocaleProvider } from 'oskari-ui/util'; import { PrimaryButton, SecondaryButton, ButtonContainer } from 'oskari-ui/components/buttons'; @@ -35,7 +36,13 @@ const Title = ({ indicator, showDataTable, regionset, selection }) => { ); } const key = !indicator.id ? 'userIndicators.add' : 'userIndicators.edit'; - return <Message messageKey={key} bundleKey={BUNDLE_KEY} /> + return <Message messageKey={key} bundleKey={BUNDLE_KEY} />; +}; +Title.propTypes = { + indicator: PropTypes.object.isRequired, + regionset: PropTypes.number.isRequired, + showDataTable: PropTypes.func.isRequired, + selection: PropTypes.any.isRequired }; const IndicatorForm = ({ state, controller, onClose }) => { @@ -60,6 +67,11 @@ const IndicatorForm = ({ state, controller, onClose }) => { </Content> ); }; +IndicatorForm.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired, + onClose: PropTypes.func.isRequired +}; export const showIndicatorForm = (state, controller, onClose) => { const controls = showPopup( diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx index 32a5ae88af..5f63e53c6a 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorInfo.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { TextInput, TextAreaInput } from 'oskari-ui'; import styled from 'styled-components'; import { BUNDLE_KEY } from '../../constants'; @@ -38,3 +39,7 @@ export const IndicatorInfo = ({ state, controller }) => { </Content> ); }; +IndicatorInfo.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx b/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx index e3c363c943..fd1de3c903 100644 --- a/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx +++ b/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { TextInput, Message } from 'oskari-ui'; import { Table } from 'oskari-ui/components/Table'; import styled from 'styled-components'; @@ -11,7 +12,7 @@ const StyledTable = styled(Table)` export const StatisticalData = ({ state, controller }) => { const { regionset, dataByRegions } = state; if (!regionset || !dataByRegions.length) { - return <Message messageKey='errors.regionsetsIsEmpty' /> + return <Message messageKey='errors.regionsetsIsEmpty' />; } const columnSettings = [ { @@ -39,5 +40,9 @@ export const StatisticalData = ({ state, controller }) => { return <StyledTable columns={columnSettings} dataSource={dataByRegions} - pagination={false} /> + pagination={false} />; +}; +StatisticalData.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired }; diff --git a/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx b/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx index 1accb28f65..c77597d3c9 100644 --- a/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx +++ b/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx @@ -1,10 +1,10 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { TextInput, Select, Message } from 'oskari-ui'; import styled from 'styled-components'; import { PrimaryButton } from 'oskari-ui/components/buttons'; import { getDatasources, getRegionsets } from '../../helper/ConfigHelper'; - const Content = styled('div')` display: flex; flex-direction: row; @@ -24,7 +24,7 @@ export const StatisticalInfo = ({ state, controller }) => { const { regionsets = [] } = getDatasources().find(({ id }) => id === indicator.ds) || {}; const options = getRegionsets() .filter(rs => regionsets.includes(rs.id)) - .map(rs => ({value: rs.id, label: rs.name})); + .map(rs => ({ value: rs.id, label: rs.name })); return ( <Content> <YearField @@ -41,3 +41,7 @@ export const StatisticalInfo = ({ state, controller }) => { </Content> ); }; +StatisticalInfo.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired +}; From 553be72387a61ac14b427008fbba9f5ac228b934 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 16 Oct 2024 02:19:47 +0300 Subject: [PATCH 045/181] use items --- .../statsgrid/view/Form/IndicatorCollapse.jsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx index 12be28c2b9..718fcdb4e4 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorCollapse.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Message, Collapse, CollapsePanel } from 'oskari-ui'; +import { Message, Collapse } from 'oskari-ui'; import { IndicatorInfo } from './IndicatorInfo'; import { IndicatorDatasets } from './IndicatorDatasets'; @@ -15,14 +15,7 @@ export const IndicatorCollapse = ({ state, controller }) => { label: <Message messageKey='userIndicators.datasets.title' />, children: <IndicatorDatasets state={state} controller={controller}/> }]; - // return <Collapse items={items} defaultActiveKey={defaultActiveKey}/> - return ( - <Collapse defaultActiveKey={defaultActiveKey}> - {items.map(({ key, label, children }) => ( - <CollapsePanel key={key} header={label}>{children}</CollapsePanel> - ))} - </Collapse> - ); + return <Collapse items={items} defaultActiveKey={defaultActiveKey}/>; }; IndicatorCollapse.propTypes = { state: PropTypes.object.isRequired, From eb20604d8cbe88b64e7deae86b6994abb510b1cd Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 10 Oct 2024 01:53:59 +0300 Subject: [PATCH 046/181] add themed slider --- src/react/components/Slider.jsx | 58 +++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/react/components/Slider.jsx b/src/react/components/Slider.jsx index 037823d2f9..f88ed5d26e 100644 --- a/src/react/components/Slider.jsx +++ b/src/react/components/Slider.jsx @@ -1,6 +1,11 @@ import React from 'react'; import { Slider as AntSlider } from 'antd'; import styled from 'styled-components'; +import { getNavigationTheme } from 'oskari-ui/theme'; + +const normal = 4; +const thick = 8; +const handler = 12; // TODO colors should come from theme-variables const StyledAntSlider = styled(AntSlider)` @@ -13,7 +18,54 @@ const StyledAntSlider = styled(AntSlider)` } } `; +const ThemedAntSlider = styled(StyledAntSlider)` + .ant-slider-mark { + top: -21px; + } + .ant-slider-mark-text { + color: ${props => props.theme.getTextColor()}; + } + .ant-slider-handle::after { + display: none; + } + .ant-slider-dot { + background: #3c3c3c; + border: none; + width: 2px; + top: 0px; + height: ${props => props.useThick ? thick : normal}px; + } +`; + +const getThemedStyle = (theme, vertical, useThick) => { + const width = vertical ? 'height' : 'width'; + const height = vertical ? 'width' : 'height'; + const size = useThick ? thick : normal; + return { + track: { + [height]: `${size}px`, + background: theme.getButtonHoverColor() + }, + rail: { + [height]: `${size}px`, + background: theme.getTextColor() + }, + handle: { + backgroundColor: theme.getButtonHoverColor(), + [width]: '8px', + [height]: `${size + handler}px`, + top: '-2px', + borderRadius: '6px', + border: 'solid 1px #3c3c3c' + } + }; +}; -export const Slider = (props) => ( - <StyledAntSlider {...props} /> -); +export const Slider = ({ theme, useThick, vertical, ...props }) => { + if (theme) { + const helper = getNavigationTheme(theme); + const styles = getThemedStyle(helper, vertical, useThick); + return <ThemedAntSlider styles={styles} theme={helper} useThick={useThick} {...props} /> + } + return <StyledAntSlider {...props} /> +}; From ef5feb90f51a660d19dd4b39b0c51f032e330ddb Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 24 Oct 2024 09:50:51 +0300 Subject: [PATCH 047/181] use Antd theme to color slider --- src/react/theme/ThemeHelper.js | 5 +++++ src/react/theme/constants.js | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/react/theme/ThemeHelper.js b/src/react/theme/ThemeHelper.js index 6ff327180f..5215558c31 100644 --- a/src/react/theme/ThemeHelper.js +++ b/src/react/theme/ThemeHelper.js @@ -30,6 +30,11 @@ export const getAntdTheme = (theme) => { // colorFillContentHover: accentColor // fixes an issue where close icon has white bg while hovering colorBgContainer: 'inherit' + }, + Slider: { + handleColor: DEFAULT_COLORS.SLIDER_BG, + trackBg: DEFAULT_COLORS.SLIDER_BG, + trackHoverBg: DEFAULT_COLORS.SLIDER_HOVER, } }, token: { diff --git a/src/react/theme/constants.js b/src/react/theme/constants.js index b1fde9b1fd..08387ad776 100644 --- a/src/react/theme/constants.js +++ b/src/react/theme/constants.js @@ -3,6 +3,8 @@ export const DEFAULT_COLORS = { HEADER_BG: '#fdf8d9', ACCENT: '#ffd400', // 141414 -> rgb(20,20,20) - DARK_BUTTON_BG: '#141414' + DARK_BUTTON_BG: '#141414', + SLIDER_BG: '#0091ff', + SLIDER_HOVER: '#003fc3' }; export const DEFAULT_FONT = 'arial'; From ecbfa0fdbf8a379acfe9e2601186b05dd3c1cb34 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 24 Oct 2024 09:54:00 +0300 Subject: [PATCH 048/181] remove antd slider styling from components --- .../VisualizationTabPane/Scale.jsx | 16 +----------- .../TimeSeriesMetadataToggleLevel.jsx | 4 --- .../LayerBox/Footer/OpacitySlider.jsx | 3 +-- .../framework/publisher2/view/PanelLayout.js | 12 +++++---- .../publisher2/view/PanelToolStyles.jsx | 26 ++++++++----------- .../editclassification/EditClassification.jsx | 6 +---- .../editclassification/SizeSlider.jsx | 2 +- .../editclassification/EditClassification.jsx | 6 +---- .../editclassification/SizeSlider.jsx | 24 +---------------- 9 files changed, 24 insertions(+), 75 deletions(-) diff --git a/bundles/admin/admin-layereditor/view/AdminLayerForm/VisualizationTabPane/Scale.jsx b/bundles/admin/admin-layereditor/view/AdminLayerForm/VisualizationTabPane/Scale.jsx index a01f0e88f0..c3bf5bc916 100644 --- a/bundles/admin/admin-layereditor/view/AdminLayerForm/VisualizationTabPane/Scale.jsx +++ b/bundles/admin/admin-layereditor/view/AdminLayerForm/VisualizationTabPane/Scale.jsx @@ -19,20 +19,6 @@ const FieldLabel = styled('div')` padding-bottom: 5px; `; -const ScaleSlider = styled(Slider)` - .ant-slider-track { - background-color: #0091ff; - } - .ant-slider-mark-text { - padding-bottom: 3px; - white-space: nowrap; - font-size: 11px; - } - &:hover .ant-slider-track { - background-color: #003fc3 !important; - } -`; - const SliderContainer = styled('div')` height: 200px; padding-top: 15px; @@ -104,7 +90,7 @@ const Scale = ({ layer, scales = [], controller, getMessage }) => { onChange={value => controller.setMinAndMaxScale([minscale, value])} /> <PlusIcon /> <SliderContainer> - <ScaleSlider + <Slider vertical range reversed diff --git a/bundles/admin/admin-layereditor/view/AdminLayerForm/VisualizationTabPane/TimeSeries/TimeSeriesMetadataToggleLevel.jsx b/bundles/admin/admin-layereditor/view/AdminLayerForm/VisualizationTabPane/TimeSeries/TimeSeriesMetadataToggleLevel.jsx index 5fe6c5fd6a..4439b8eada 100644 --- a/bundles/admin/admin-layereditor/view/AdminLayerForm/VisualizationTabPane/TimeSeries/TimeSeriesMetadataToggleLevel.jsx +++ b/bundles/admin/admin-layereditor/view/AdminLayerForm/VisualizationTabPane/TimeSeries/TimeSeriesMetadataToggleLevel.jsx @@ -9,10 +9,6 @@ const SliderContainer = styled('div')` padding-right: 15px; padding-top: 10px; padding-bottom: 10px; - - .ant-slider-mark-text { - font-size: 11px; - } `; export const TimeSeriesMetadataToggleLevel = ({ layer, disabled, scales, controller }) => { diff --git a/bundles/framework/layerlist/view/LayerViewTabs/SelectedLayers/LayerBox/Footer/OpacitySlider.jsx b/bundles/framework/layerlist/view/LayerViewTabs/SelectedLayers/LayerBox/Footer/OpacitySlider.jsx index 7a74f595c2..ebd1165c1d 100644 --- a/bundles/framework/layerlist/view/LayerViewTabs/SelectedLayers/LayerBox/Footer/OpacitySlider.jsx +++ b/bundles/framework/layerlist/view/LayerViewTabs/SelectedLayers/LayerBox/Footer/OpacitySlider.jsx @@ -1,9 +1,8 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; -import { InputGroup } from 'oskari-ui'; +import { InputGroup, Opacity } from 'oskari-ui'; import { Timeout } from 'oskari-ui/util'; -import { Opacity } from 'oskari-ui'; const OPACITY_EVENT_FIRING_DELAY = 100; diff --git a/bundles/framework/publisher2/view/PanelLayout.js b/bundles/framework/publisher2/view/PanelLayout.js index 8114164853..60df45d8fa 100755 --- a/bundles/framework/publisher2/view/PanelLayout.js +++ b/bundles/framework/publisher2/view/PanelLayout.js @@ -1,6 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { PanelToolStyles } from './PanelToolStyles'; +import { ThemeProvider } from 'oskari-ui/util'; /** * @class Oskari.mapframework.bundle.publisher.view.PanelToolLayout * @@ -130,11 +131,12 @@ Oskari.clazz.define('Oskari.mapframework.bundle.publisher2.view.PanelLayout', contentPanel.append(styleEditor); ReactDOM.render( - <PanelToolStyles - mapTheme={this.mapModule.getMapTheme()} - changeTheme={(theme) => this.updateTheme(theme)} - fonts={this.fonts} - />, + <ThemeProvider> + <PanelToolStyles + mapTheme={this.mapModule.getMapTheme()} + changeTheme={(theme) => this.updateTheme(theme)} + fonts={this.fonts}/> + </ThemeProvider>, styleEditor[0] ); diff --git a/bundles/framework/publisher2/view/PanelToolStyles.jsx b/bundles/framework/publisher2/view/PanelToolStyles.jsx index e91908d1c4..980118bd79 100644 --- a/bundles/framework/publisher2/view/PanelToolStyles.jsx +++ b/bundles/framework/publisher2/view/PanelToolStyles.jsx @@ -21,18 +21,12 @@ const StyledColorPicker = styled('div')` width: 165px; `; -const StyledSlider = styled(Slider)` +const SliderContainer = styled('div')` width: 285px; - margin: 0 15px 0 5px; - .ant-slider-track { - background-color: #0091ff; - } - &:hover .ant-slider-track { - background-color: #003fc3 !important; - } + margin-left: 5px; `; -const SliderContainer = styled('div')` +const ButtonRounding = styled('div')` display: flex; flex-direction: row; align-items: center; @@ -46,6 +40,7 @@ const StyledNumberInput = styled(NumberInput)` const NumberInputContainer = styled('div')` display: flex; flex-direction: row; + margin-left: 15px; `; const NumberSuffix = styled('span')` @@ -186,11 +181,12 @@ export const PanelToolStyles = ({ mapTheme, changeTheme, fonts }) => { </Field> <Field> <Message bundleKey={BUNDLE_KEY} messageKey='BasicView.layout.fields.buttonRounding' /> - <SliderContainer> - <StyledSlider - value={buttonRounding} - onChange={val => setButtonRounding(val)} - /> + <ButtonRounding> + <SliderContainer> + <Slider noMargin + value={buttonRounding} + onChange={val => setButtonRounding(val)}/> + </SliderContainer> <NumberInputContainer> <StyledNumberInput min={0} @@ -202,7 +198,7 @@ export const PanelToolStyles = ({ mapTheme, changeTheme, fonts }) => { % </NumberSuffix> </NumberInputContainer> - </SliderContainer> + </ButtonRounding> </Field> <Field> <Message bundleKey={BUNDLE_KEY} messageKey='BasicView.layout.fields.effect' /> diff --git a/bundles/statistics/statsgrid/components/classification/editclassification/EditClassification.jsx b/bundles/statistics/statsgrid/components/classification/editclassification/EditClassification.jsx index edbe1a8f1f..ae1de99171 100644 --- a/bundles/statistics/statsgrid/components/classification/editclassification/EditClassification.jsx +++ b/bundles/statistics/statsgrid/components/classification/editclassification/EditClassification.jsx @@ -9,13 +9,9 @@ import { EditTwoTone } from '@ant-design/icons'; import { getEditOptions } from '../../../helper/ClassificationHelper'; import { LAYER_ID } from '../../../constants'; -// remove mark dots (.ant-slider-dot) const Container = styled.div` background-color: #fafafa; padding: 6px 12px; - .ant-slider-dot { - display: none; - } `; const MethodContainer = styled.div` @@ -103,7 +99,7 @@ export const EditClassification = ({ <Color values = {values} disabled = {disabled} colorsets = {options.colorsets} controller = {controller}/> <Message messageKey='classify.labels.transparency'/> - <Slider + <Slider hideDots value = {opacity} disabled = {disabled} onChange = {onOpacityChange} diff --git a/bundles/statistics/statsgrid/components/classification/editclassification/SizeSlider.jsx b/bundles/statistics/statsgrid/components/classification/editclassification/SizeSlider.jsx index a78d247b1c..454e158162 100644 --- a/bundles/statistics/statsgrid/components/classification/editclassification/SizeSlider.jsx +++ b/bundles/statistics/statsgrid/components/classification/editclassification/SizeSlider.jsx @@ -43,7 +43,7 @@ export const SizeSlider = ({ return ( <div> <Message messageKey="classify.labels.pointSize"/> - <Slider + <Slider hideDots value = {internalRange} disabled = {disabled} onChange={onChange} diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/EditClassification.jsx b/bundles/statistics/statsgrid2016/components/classification/editclassification/EditClassification.jsx index 9fe8af47e9..04e1ececd5 100644 --- a/bundles/statistics/statsgrid2016/components/classification/editclassification/EditClassification.jsx +++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/EditClassification.jsx @@ -7,13 +7,9 @@ import { Color } from './Color'; import { Checkbox, Message, Slider } from 'oskari-ui'; import { EditTwoTone } from '@ant-design/icons'; -// remove mark dots (.ant-slider-dot) const Container = styled.div` background-color: #fafafa; padding: 6px 12px; - .ant-slider-dot { - display: none; - } `; const MethodContainer = styled.div` @@ -98,7 +94,7 @@ export const EditClassification = ({ <Color values = {values} disabled = {disabled} colorsets = {options.colorsets} controller = {controller}/> <Message messageKey='classify.labels.transparency'/> - <Slider + <Slider hideDots value = {values.transparency} disabled = {disabled} onChange = {value => handleChange('transparency', value)} diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/SizeSlider.jsx b/bundles/statistics/statsgrid2016/components/classification/editclassification/SizeSlider.jsx index 039efa2f34..c7e2ef9e5f 100644 --- a/bundles/statistics/statsgrid2016/components/classification/editclassification/SizeSlider.jsx +++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/SizeSlider.jsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { Slider, Message } from 'oskari-ui'; -import styled from 'styled-components'; // Overrride -50% translateX const SLIDER_PROPS = { @@ -24,27 +23,6 @@ const SLIDER_PROPS = { } }; -const StyledSlider = styled(Slider)` - .ant-slider-track { - background-color: #0091ff; - } - .ant-slider-handle { - border: #0091ff solid 2px; - margin-top: -6px; - } - &:hover .ant-slider-track { - background-color: #003fc3 !important; - } - &:hover .ant-slider-handle { - border: #003fc3 solid 2px !important; - } - .ant-slider-dot { - width: 6px; - height: 6px; - left: 1px; - } -`; - export const SizeSlider = ({ values, controller, @@ -65,7 +43,7 @@ export const SizeSlider = ({ return ( <div> <Message messageKey="classify.labels.pointSize"/> - <StyledSlider + <Slider hideDots value = {range} disabled = {disabled} onChange={onChange} From 3bc56c66b2321168c2db70136c0da338afcae0e3 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 24 Oct 2024 09:55:10 +0300 Subject: [PATCH 049/181] remove slider styling --- src/react/components/Opacity.jsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/react/components/Opacity.jsx b/src/react/components/Opacity.jsx index fbdc352dc9..1b38d934b5 100644 --- a/src/react/components/Opacity.jsx +++ b/src/react/components/Opacity.jsx @@ -8,12 +8,6 @@ const StyledSlider = styled('div')` width: 100%; padding: 0 10px 0 5px; float: left; - .ant-slider-track { - background-color: #0091ff; - } - &:hover .ant-slider-track { - background-color: #003fc3 !important; - } ${props => props.bordered && ( ` border-radius: 4px 0 0 4px; From 7c384b01b6e16c1d20e6da4e0c7448d3bd9940d5 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 24 Oct 2024 09:56:54 +0300 Subject: [PATCH 050/181] move styling to common slider --- src/react/components/Slider.jsx | 34 +++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/react/components/Slider.jsx b/src/react/components/Slider.jsx index f88ed5d26e..47a09b0a87 100644 --- a/src/react/components/Slider.jsx +++ b/src/react/components/Slider.jsx @@ -1,29 +1,35 @@ import React from 'react'; import { Slider as AntSlider } from 'antd'; import styled from 'styled-components'; -import { getNavigationTheme } from 'oskari-ui/theme'; +import { getNavigationTheme, DEFAULT_COLORS } from 'oskari-ui/theme'; const normal = 4; const thick = 8; const handler = 12; +const hover = DEFAULT_COLORS.SLIDER_HOVER; -// TODO colors should come from theme-variables const StyledAntSlider = styled(AntSlider)` - &.ant-slider-vertical { - .ant-slider-handle { - margin-bottom: 0; - } - .ant-slider-mark { - top: 2px; - } + .ant-slider-mark-text { + white-space: nowrap; + font-size: 11px; + } + .ant-slider-dot { + ${props => props.hideDots ? 'display: none;' : ''} + } + &:hover .ant-slider-handle::after { + box-shadow: 0 0 0 2px ${hover}; } + ${props => props.noMargin ? 'margin: 0;' : ''} + `; + const ThemedAntSlider = styled(StyledAntSlider)` .ant-slider-mark { top: -21px; } .ant-slider-mark-text { color: ${props => props.theme.getTextColor()}; + font-size: 11px; } .ant-slider-handle::after { display: none; @@ -44,11 +50,11 @@ const getThemedStyle = (theme, vertical, useThick) => { return { track: { [height]: `${size}px`, - background: theme.getButtonHoverColor() + backgroundColor: theme.getButtonHoverColor() }, rail: { [height]: `${size}px`, - background: theme.getTextColor() + backgroundColor: theme.getTextColor() }, handle: { backgroundColor: theme.getButtonHoverColor(), @@ -64,8 +70,8 @@ const getThemedStyle = (theme, vertical, useThick) => { export const Slider = ({ theme, useThick, vertical, ...props }) => { if (theme) { const helper = getNavigationTheme(theme); - const styles = getThemedStyle(helper, vertical, useThick); - return <ThemedAntSlider styles={styles} theme={helper} useThick={useThick} {...props} /> + const style = getThemedStyle(helper, vertical, useThick); + return <ThemedAntSlider styles={style} theme={helper} useThick={useThick} {...props} /> } - return <StyledAntSlider {...props} /> + return <StyledAntSlider styles={style} vertical= {vertical} {...props} /> }; From 9cbe91529ea2e6689a13598ab0d4b744ba460676 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 24 Oct 2024 10:59:30 +0300 Subject: [PATCH 051/181] pass theme to Slider --- .../time-control-3d/view/TimeControl3d.jsx | 2 + .../view/TimeControl3d/DateControl.jsx | 22 +++--- .../view/TimeControl3d/TimeSlider.jsx | 10 +-- .../view/TimeControl3d/styled.jsx | 71 ++----------------- 4 files changed, 24 insertions(+), 81 deletions(-) diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d.jsx index 073716f39a..55ea1b10c6 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d.jsx @@ -4,6 +4,7 @@ import { Controller } from 'oskari-ui/util'; import styled from 'styled-components'; import { TimeControl } from './TimeControl3d/TimeControl'; import { DateControl } from './TimeControl3d/DateControl'; +import { Divider } from './TimeControl3d/styled'; import { validateDate, validateTime } from '../../mapmodule/util/time'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; @@ -130,6 +131,7 @@ export const TimeControl3d = ({ controller, date, time, isMobile }) => { dateValue={dateValue} currentTimeHandler={changeTimeAndDate} /> + <Divider /> <TimeControl isMobile={isMobile} changeHandler={changeTime} diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d/DateControl.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d/DateControl.jsx index 29a323d549..7f5eec5e95 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d/DateControl.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d/DateControl.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Message } from 'oskari-ui'; -import { Row, Col, ColFixed, StyledButton, StyledDateSlider, CalendarIcon } from './styled'; +import { Message, Slider } from 'oskari-ui'; +import { Row, Col, StyledButton, DateSliderContainer, CalendarIcon, ColFixed } from './styled'; import { ThemeConsumer } from 'oskari-ui/util'; import { getHeaderTheme } from 'oskari-ui/theme'; import { Input } from './Input'; @@ -53,14 +53,16 @@ export const DateControl = ThemeConsumer(({ theme, isMobile, changeHandler, slid </Col> {!isMobile && <ColFixed> - <StyledDateSlider - color={color} - hover={hover} - marks={marksForDate()} - min={1} max={DAYS} step={1} - value={sliderDateValue} - onChange={changeSliderDate} - tooltip={{ open: false }}/> + <DateSliderContainer> + <Slider + noMargin + theme={theme} + marks={marksForDate()} + min={1} max={DAYS} step={1} + value={sliderDateValue} + onChange={changeSliderDate} + tooltip={{ open: false }}/> + </DateSliderContainer> </ColFixed> } <Col> diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d/TimeSlider.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d/TimeSlider.jsx index 950eaf02cf..1d38db6bf8 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d/TimeSlider.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d/TimeSlider.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { InputGroup } from 'oskari-ui'; -import { StyledTimeSlider, TimeBorder, StyledPlayButton } from './styled'; +import { InputGroup, Slider } from 'oskari-ui'; +import { TimeBorder, StyledPlayButton } from './styled'; import { PlayButton } from './PlayButton'; import { ThemeConsumer } from 'oskari-ui/util'; import { getHeaderTheme } from 'oskari-ui/theme'; @@ -34,9 +34,9 @@ export const TimeSlider = ThemeConsumer(({ theme, isMobile, changeHandler, slide <PlayButton initial={playing} /> </StyledPlayButton> <TimeBorder isMobile={isMobile}> - <StyledTimeSlider - color={color} - hover={hover} + <Slider + noMargin + theme={theme} useThick={isMobile} marks={marksForTime} min={0} max={MINUTES} diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d/styled.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d/styled.jsx index c3e29bd86e..9a6936312d 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d/styled.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d/styled.jsx @@ -1,9 +1,7 @@ import styled from 'styled-components'; -import { Button, Slider, Select } from 'oskari-ui'; +import { Button, Select } from 'oskari-ui'; import { CalendarOutlined, ClockCircleOutlined } from '@ant-design/icons'; -const thickSlider = 8; // default is 4 - export const CalendarIcon = styled(CalendarOutlined)` margin-right: 15px; color: #d9d9d9; @@ -66,69 +64,7 @@ export const StyledPlayButton = styled(ThemedButton)` width: 40px; `; -const StyledSlider = styled(Slider)` - &&& { - height: ${props => props.useThick ? 12 + thickSlider : 16}px; - } - .ant-slider-mark { - top: -21px; - } - .ant-slider-mark-text { - color: #ffffff; - } - .ant-slider-dot { - background: #3c3c3c; - border-radius: 0%; - border: 0; - margin-left: 0px; - width: 2px; - top: 0px; - height: ${props => props.useThick ? thickSlider : 4}px; - } - .ant-slider-dot:last-child { - margin-left: 0px; - } - .ant-slider-rail { - ${props => props.useThick ? 'height: ' + thickSlider + 'px;' : ''} - background: #ffffff !important; - } - .ant-slider-track { - ${props => props.useThick ? 'height: ' + thickSlider + 'px;' : ''} - background: ${props => props.color}; - } - .ant-slider-handle { - top: -2px; - width: 8px; - height: ${props => props.useThick ? 12 + thickSlider : 16}px; - border-radius: 6px; - border: solid 1px #3c3c3c; - background-color: ${props => props.color} !important; - - &:focus .ant-slider-track, - &:active .ant-slider-track, - &:hover .ant-slider-track { - background: ${props => props.hover} !important; - } - ::after { - display: none; - } - } - &:hover .ant-slider-track { - background: ${props => props.hover} !important; - } - &:hover .ant-slider-handle { - border: solid 1px #3c3c3c !important; - background-color: ${props => props.color} !important; - } -`; - -export const StyledTimeSlider = styled(StyledSlider)` - &&& { - margin: 0px; - } -`; - -export const StyledDateSlider = styled(StyledSlider)` +export const DateSliderContainer = styled.div` width: 93%; margin-top: 20px; `; @@ -156,6 +92,9 @@ export const ColFixed = styled.div` max-width: 100%; position: relative; `; +export const Divider = styled.div` + margin-bottom: 30px; +`; export const TimeBorder = styled.div(({ isMobile }) => ({ borderRadius: '4px', border: '1px solid #949494', From ee24b3121fbf1200f53664d331446f2c7924afdc Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 24 Oct 2024 11:06:18 +0300 Subject: [PATCH 052/181] order code and increase noMargin specificity --- src/react/components/Slider.jsx | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/react/components/Slider.jsx b/src/react/components/Slider.jsx index 47a09b0a87..8aef031c14 100644 --- a/src/react/components/Slider.jsx +++ b/src/react/components/Slider.jsx @@ -9,6 +9,7 @@ const handler = 12; const hover = DEFAULT_COLORS.SLIDER_HOVER; const StyledAntSlider = styled(AntSlider)` + ${props => props.noMargin ? 'margin: 0;' : ''} .ant-slider-mark-text { white-space: nowrap; font-size: 11px; @@ -19,13 +20,14 @@ const StyledAntSlider = styled(AntSlider)` &:hover .ant-slider-handle::after { box-shadow: 0 0 0 2px ${hover}; } - ${props => props.noMargin ? 'margin: 0;' : ''} - `; -const ThemedAntSlider = styled(StyledAntSlider)` +const ThemedAntSlider = styled(AntSlider)` + &&& { + ${props => props.noMargin ? 'margin: 0;' : ''} + } .ant-slider-mark { - top: -21px; + ${props => props.vertical ? '' : 'top: -18px;'} } .ant-slider-mark-text { color: ${props => props.theme.getTextColor()}; @@ -67,11 +69,11 @@ const getThemedStyle = (theme, vertical, useThick) => { }; }; -export const Slider = ({ theme, useThick, vertical, ...props }) => { - if (theme) { - const helper = getNavigationTheme(theme); - const style = getThemedStyle(helper, vertical, useThick); - return <ThemedAntSlider styles={style} theme={helper} useThick={useThick} {...props} /> +export const Slider = ({ theme, useThick, vertical, ...rest }) => { + if (!theme) { + return <StyledAntSlider vertical= {vertical} {...rest} /> } - return <StyledAntSlider styles={style} vertical= {vertical} {...props} /> + const helper = getNavigationTheme(theme); + const style = getThemedStyle(helper, vertical, useThick); + return <ThemedAntSlider styles={style} theme={helper} useThick={useThick} vertical={vertical} {...rest} /> }; From 377f523752f28b167deae008903c79632e00f162 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 28 Oct 2024 11:36:27 +0200 Subject: [PATCH 053/181] add missing closing tag for code block --- api/ui/toolbar/bundle.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/ui/toolbar/bundle.md b/api/ui/toolbar/bundle.md index d395b026cb..565440f465 100755 --- a/api/ui/toolbar/bundle.md +++ b/api/ui/toolbar/bundle.md @@ -59,11 +59,12 @@ A toolbar can also contain color configs: "background": "#00ff00" } } +``` Hover color is used when hovering icon and Toolbar.ToolbarRequest add operation data not contains color configuration. Background color is now only used to calculate light/dark icon classes. -Configuration is used when Toolbar.ToolbarREquest add operation data not contains color configuration. +Configuration is used when Toolbar.ToolbarREquest add operation data not contains color configuration. ## Bundle state From 7ba3c334bb3c4540d2c766b8a4b6c6a408a1fdc3 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Wed, 30 Oct 2024 12:15:20 +0200 Subject: [PATCH 054/181] remove redundant init override from scalebar --- .../plugin/scalebar/publisher/ScalebarTool.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/bundles/mapping/mapmodule/plugin/scalebar/publisher/ScalebarTool.js b/bundles/mapping/mapmodule/plugin/scalebar/publisher/ScalebarTool.js index 38c2ddd7c7..0933fcefcc 100644 --- a/bundles/mapping/mapmodule/plugin/scalebar/publisher/ScalebarTool.js +++ b/bundles/mapping/mapmodule/plugin/scalebar/publisher/ScalebarTool.js @@ -6,7 +6,6 @@ class ScaleBarTool extends AbstractPublisherTool { super(...args); this.index = 1; this.group = 'additional'; - this.config = null; } getTool () { @@ -17,18 +16,6 @@ class ScaleBarTool extends AbstractPublisherTool { }; } - init (data) { - const plugin = this.findPluginFromInitData(data); - this.setEnabled(!!plugin); - - if (plugin?.config) { - this.storePluginConf(plugin.config); - // when we enter publisher: - // restore saved location for plugin that is not stopped nor started - this.getPlugin().setLocation(plugin.config?.location?.classes); - } - } - getValues () { if (!this.isEnabled()) { return null; @@ -43,10 +30,6 @@ class ScaleBarTool extends AbstractPublisherTool { } }; } - - stop () { - super.stop(); - } }; // Attach protocol to make this discoverable by Oskari publisher From 2438bb0817f8fbe535bed0bc88ad6f547cb52550 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Wed, 30 Oct 2024 14:44:59 +0200 Subject: [PATCH 055/181] mylocation init & alignment --- .../mylocation/publisher/MyLocationHandler.js | 6 ++++-- .../mylocation/publisher/MyLocationTool.js | 17 ++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationHandler.js b/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationHandler.js index 6f6d5785bd..0ecadf452f 100644 --- a/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationHandler.js +++ b/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationHandler.js @@ -4,11 +4,13 @@ class UIHandler extends StateHandler { constructor (tool) { super(); this.tool = tool; - this.sandbox = tool.getSandbox(); - this.setState({ + const defaults = { mode: 'single', centerMapAutomatically: false, mobileOnly: false + }; + this.setState({ + ...defaults }); }; diff --git a/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationTool.js b/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationTool.js index b88745eb06..d47772f0a5 100644 --- a/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationTool.js +++ b/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationTool.js @@ -7,18 +7,13 @@ class MyLocationTool extends AbstractPublisherTool { super(...args); this.index = 1; this.group = 'additional'; - this.config = null; this.handler = new MyLocationToolHandler(this); }; init (data) { - const plugin = this.findPluginFromInitData(data); - // restore state to handler -> passing init data to it - this.setEnabled(!!plugin); - - if (plugin?.config) { - this.handler.init(plugin.config); - } + super.init(data); + const config = this.state?.pluginConfig || {}; + this.handler.init(config); } getComponent () { @@ -38,7 +33,7 @@ class MyLocationTool extends AbstractPublisherTool { return { id: 'Oskari.mapframework.bundle.mapmodule.plugin.MyLocationPlugin', title: Oskari.getMsg('MapModule', 'publisherTools.MyLocationPlugin.toolLabel'), - config: this.handler?.getState() || {} + config: this.state?.pluginConfig || {} }; } @@ -46,8 +41,8 @@ class MyLocationTool extends AbstractPublisherTool { if (!this.isEnabled()) { return null; } - const pluginConfig = this.getPlugin().config || {}; - // add selected extraoptions to conf + const pluginConfig = this.getPlugin().getConfig() || {}; + // update extras const state = this.handler.getState(); for (const key in state) { if (state.hasOwnProperty(key)) { From 8536ee58005646842a3bd0f44a656d3102203c38 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Wed, 30 Oct 2024 15:16:32 +0200 Subject: [PATCH 056/181] panbuttons location issue --- .../plugin/panbuttons/publisher/PanButtonsTool.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsTool.js b/bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsTool.js index 132ece0ebb..5e228320c5 100644 --- a/bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsTool.js +++ b/bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsTool.js @@ -6,18 +6,13 @@ class PanButtonsTool extends AbstractPublisherTool { super(...args); this.index = 1; this.group = 'additional'; - this.config = null; this.handler = new PanButtonsHandler(this); }; init (data) { - const plugin = this.findPluginFromInitData(data); - // restore state to handler -> passing init data to it - this.setEnabled(!!plugin); - - if (plugin?.config) { - this.handler.init(plugin.config); - } + super.init(data); + const config = this.state?.pluginConfig || {}; + this.handler.init(config); } getComponent () { @@ -37,7 +32,7 @@ class PanButtonsTool extends AbstractPublisherTool { return { id: 'Oskari.mapframework.bundle.mapmodule.plugin.PanButtons', title: Oskari.getMsg('MapModule', 'publisherTools.PanButtons.toolLabel'), - config: this.handler?.getState() || {} + config: this.state?.pluginConfig || {} }; } @@ -46,7 +41,7 @@ class PanButtonsTool extends AbstractPublisherTool { return null; } - const pluginConfig = this.getPlugin().config || {}; + const pluginConfig = this.getPlugin().getConfig() || {}; // add selected extraoptions to conf const state = this.handler.getState(); for (const key in state) { From 8888266d3ed5f0d02441f714356089690838617f Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Tue, 22 Oct 2024 11:25:51 +0300 Subject: [PATCH 057/181] move indexmap to mapmodule --- .../framework/publisher2/resources/locale/en.js | 1 - .../framework/publisher2/resources/locale/fi.js | 1 - .../framework/publisher2/resources/locale/fr.js | 1 - .../framework/publisher2/resources/locale/is.js | 1 - .../framework/publisher2/resources/locale/ru.js | 1 - .../framework/publisher2/resources/locale/sv.js | 1 - .../plugin/indexmap/publisher}/IndexMapTool.js | 16 +++++++++++++--- bundles/mapping/mapmodule/publisher/tools.js | 3 ++- bundles/mapping/mapmodule/resources/locale/en.js | 3 +++ bundles/mapping/mapmodule/resources/locale/fi.js | 3 +++ bundles/mapping/mapmodule/resources/locale/fr.js | 3 +++ bundles/mapping/mapmodule/resources/locale/is.js | 3 +++ bundles/mapping/mapmodule/resources/locale/ru.js | 3 ++- bundles/mapping/mapmodule/resources/locale/sv.js | 3 +++ packages/framework/bundle/publisher2/bundle.js | 4 ---- 15 files changed, 32 insertions(+), 15 deletions(-) rename bundles/{framework/publisher2/tools => mapping/mapmodule/plugin/indexmap/publisher}/IndexMapTool.js (68%) mode change 100755 => 100644 diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index e571721566..a23ee24e89 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Select available map tools. Check a placement in the map preview.", "AnnouncementsPlugin": "Announcements", "TimeseriesControlPlugin": "Time series player", - "IndexMapPlugin": "Index map", "Portti2Zoombar": "Zoom bar", "ControlsPlugin": "Pan by mouse", "SearchPlugin": "Place search", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 2952f81f60..a094cd76ec 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Valitse kartalla käytettävissä olevat työkalut. Tarkista asettelu esikatselukartasta.", "AnnouncementsPlugin": "Ilmoitukset", "TimeseriesControlPlugin": "Aikasarjatoistin", - "IndexMapPlugin": "Indeksikartta", "Portti2Zoombar": "Mittakaavasäädin", "ControlsPlugin": "Kartan liikuttaminen hiirellä raahaamalla", "SearchPlugin": "Osoite- ja paikannimihaku", diff --git a/bundles/framework/publisher2/resources/locale/fr.js b/bundles/framework/publisher2/resources/locale/fr.js index 5a11ca61be..3b2f009e48 100644 --- a/bundles/framework/publisher2/resources/locale/fr.js +++ b/bundles/framework/publisher2/resources/locale/fr.js @@ -63,7 +63,6 @@ Oskari.registerLocalization( "label": "Outils", "tooltip": "Sélectionner les outils de carte accessibles. Consulter une mise en place dans la prévisualisation de carte.", "TimeseriesControlPlugin": "Lecteur chronologique", - "IndexMapPlugin": "Carte-index", "Portti2Zoombar": "Barre de zoom", "ControlsPlugin": "Panoramiser avec la souris", "SearchPlugin": "Recherche de lieu", diff --git a/bundles/framework/publisher2/resources/locale/is.js b/bundles/framework/publisher2/resources/locale/is.js index dee18bac0f..748b15ae9c 100755 --- a/bundles/framework/publisher2/resources/locale/is.js +++ b/bundles/framework/publisher2/resources/locale/is.js @@ -50,7 +50,6 @@ Oskari.registerLocalization( "maptools": { "label": "Tól", "tooltip": "Velja tiltæk kortatól. Athugaðu staðsetningu við forskoðun korts.", - "IndexMapPlugin": "Index kort", "Portti2Zoombar": "Þysjunarstika", "ControlsPlugin": "Hliðra með mús", "SearchPlugin": "Leit eftir stað", diff --git a/bundles/framework/publisher2/resources/locale/ru.js b/bundles/framework/publisher2/resources/locale/ru.js index 86820f5ae4..6ef8cf7d5b 100644 --- a/bundles/framework/publisher2/resources/locale/ru.js +++ b/bundles/framework/publisher2/resources/locale/ru.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "label": "Инструменты", "tooltip": "Выберите доступные инструменты карты. Проверка размещения в окне предварительного просмотра карты.", "TimeseriesControlPlugin": "Временные ряды", - "IndexMapPlugin": "Номенклатура карты", "Portti2Zoombar": "Панель увеличения", "ControlsPlugin": "Панорамирование мышью", "SearchPlugin": "Поиск места", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 7e7b241a16..5c48697e10 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.", "AnnouncementsPlugin": "Aviseringar", "TimeseriesControlPlugin": "Tidseriespelare", - "IndexMapPlugin": "Indexkarta", "Portti2Zoombar": "Skalans glidreglage", "ControlsPlugin": "Flytta kartvyn med musen", "SearchPlugin": "Adress- och ortnamnssökning", diff --git a/bundles/framework/publisher2/tools/IndexMapTool.js b/bundles/mapping/mapmodule/plugin/indexmap/publisher/IndexMapTool.js old mode 100755 new mode 100644 similarity index 68% rename from bundles/framework/publisher2/tools/IndexMapTool.js rename to bundles/mapping/mapmodule/plugin/indexmap/publisher/IndexMapTool.js index 74aa37706b..2292b6d124 --- a/bundles/framework/publisher2/tools/IndexMapTool.js +++ b/bundles/mapping/mapmodule/plugin/indexmap/publisher/IndexMapTool.js @@ -1,17 +1,25 @@ -import { AbstractPublisherTool } from './AbstractPublisherTool'; +import { AbstractPublisherTool } from '../../../../../framework/publisher2/tools/AbstractPublisherTool'; class IndexMapTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + } + getTool () { return { id: 'Oskari.mapframework.bundle.mapmodule.plugin.IndexMapPlugin', - title: 'IndexMapPlugin', + title: Oskari.getMsg('MapModule', 'publisherTools.IndexMapPlugin.toolLabel'), config: this.state.pluginConfig || {} }; } + isDisplayed () { // not shown on 3d maps return !Oskari.getSandbox().getMap().getSupports3D(); } + getValues () { if (!this.isEnabled()) { return null; @@ -32,6 +40,8 @@ class IndexMapTool extends AbstractPublisherTool { Oskari.clazz.defineES('Oskari.publisher.IndexMapTool', IndexMapTool, { - 'protocol': ['Oskari.mapframework.publisher.Tool'] + protocol: ['Oskari.mapframework.publisher.Tool'] } ); + +export { IndexMapTool }; diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index 39d0fa8398..f099a2e41a 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -5,5 +5,6 @@ import { LogoTool } from '../plugin/logo/publisher/LogoTool'; import { ScaleBarTool } from '../plugin/scalebar/publisher/ScalebarTool'; import { MyLocationTool } from '../plugin/mylocation/publisher/MyLocationTool'; import { PanButtonsTool } from '../plugin/panbuttons/publisher/PanButtonsTool'; +import { IndexMapTool } from '../plugin/indexmap/publisher/IndexMapTool'; -export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool }; +export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool }; diff --git a/bundles/mapping/mapmodule/resources/locale/en.js b/bundles/mapping/mapmodule/resources/locale/en.js index 717c34914e..873c493890 100755 --- a/bundles/mapping/mapmodule/resources/locale/en.js +++ b/bundles/mapping/mapmodule/resources/locale/en.js @@ -177,6 +177,9 @@ Oskari.registerLocalization( "titles": { "showArrows": "Pan tool" } + }, + "IndexMapPlugin": { + "toolLabel": "Index map", } } } diff --git a/bundles/mapping/mapmodule/resources/locale/fi.js b/bundles/mapping/mapmodule/resources/locale/fi.js index 39e738ad96..d0944b86e7 100755 --- a/bundles/mapping/mapmodule/resources/locale/fi.js +++ b/bundles/mapping/mapmodule/resources/locale/fi.js @@ -177,6 +177,9 @@ Oskari.registerLocalization( "titles": { "showArrows": "Kartan liikuttaminen nuolipainikkeilla" } + }, + "IndexMapPlugin": { + "toolLabel": "Indeksikartta" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/fr.js b/bundles/mapping/mapmodule/resources/locale/fr.js index 6d76f40b12..9d27f9aacb 100755 --- a/bundles/mapping/mapmodule/resources/locale/fr.js +++ b/bundles/mapping/mapmodule/resources/locale/fr.js @@ -145,6 +145,9 @@ Oskari.registerLocalization( }, "PanButtons": { "toolLabel": "Outil pour panoramiser" + }, + "IndexMapPlugin": { + "toolLabel": "Carte-index" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/is.js b/bundles/mapping/mapmodule/resources/locale/is.js index f892bc8769..7642578f68 100755 --- a/bundles/mapping/mapmodule/resources/locale/is.js +++ b/bundles/mapping/mapmodule/resources/locale/is.js @@ -139,6 +139,9 @@ Oskari.registerLocalization( }, "PanButtons": { "toolLabel": "Hliðrunartól" + }, + "IndexMapPlugin": { + "toolLabel": "Index kort" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/ru.js b/bundles/mapping/mapmodule/resources/locale/ru.js index 67975684bd..64dd62c234 100644 --- a/bundles/mapping/mapmodule/resources/locale/ru.js +++ b/bundles/mapping/mapmodule/resources/locale/ru.js @@ -140,7 +140,8 @@ Oskari.registerLocalization( }, "PanButtons": { "toolLabel": "Инструмент панорамирования" - } + }, + "IndexMapPlugin": "Номенклатура карты", } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/sv.js b/bundles/mapping/mapmodule/resources/locale/sv.js index f053091c91..d1ec24d9bc 100755 --- a/bundles/mapping/mapmodule/resources/locale/sv.js +++ b/bundles/mapping/mapmodule/resources/locale/sv.js @@ -172,6 +172,9 @@ Oskari.registerLocalization( "titles": { "showArrows": "Panoreringsverktyg" } + }, + "IndexMapPlugin": { + "toolLabel": "Indexkarta" } } } diff --git a/packages/framework/bundle/publisher2/bundle.js b/packages/framework/bundle/publisher2/bundle.js index 0a88d5a0d6..e52d98dc60 100755 --- a/packages/framework/bundle/publisher2/bundle.js +++ b/packages/framework/bundle/publisher2/bundle.js @@ -97,10 +97,6 @@ Oskari.clazz.define("Oskari.mapframework.bundle.publisher2.PublisherBundle", fun "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/ToolbarTool.js" }, - { - "type": "text/javascript", - "src": "../../../../bundles/framework/publisher2/tools/IndexMapTool.js" - }, { "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/ZoombarTool.js" From 71144ecf2837059be55ba31f497ab92bb33b0049 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Thu, 31 Oct 2024 12:03:17 +0200 Subject: [PATCH 058/181] move zoombar tool under mapmodule --- .../publisher2/resources/locale/en.js | 1 - .../publisher2/resources/locale/fi.js | 1 - .../publisher2/resources/locale/fr.js | 1 - .../publisher2/resources/locale/is.js | 1 - .../publisher2/resources/locale/ru.js | 1 - .../publisher2/resources/locale/sv.js | 1 - .../framework/publisher2/tools/ZoombarTool.js | 43 --------------- .../plugin/zoombar/publisher/ZoombarTool.js | 55 +++++++++++++++++++ bundles/mapping/mapmodule/publisher/tools.js | 3 +- .../mapping/mapmodule/resources/locale/en.js | 5 +- .../mapping/mapmodule/resources/locale/fi.js | 3 + .../mapping/mapmodule/resources/locale/fr.js | 5 +- .../mapping/mapmodule/resources/locale/is.js | 5 +- .../mapping/mapmodule/resources/locale/ru.js | 7 ++- .../mapping/mapmodule/resources/locale/sv.js | 5 +- .../framework/bundle/publisher2/bundle.js | 4 -- 16 files changed, 82 insertions(+), 59 deletions(-) delete mode 100755 bundles/framework/publisher2/tools/ZoombarTool.js create mode 100644 bundles/mapping/mapmodule/plugin/zoombar/publisher/ZoombarTool.js diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index a23ee24e89..9c0f1d945f 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Select available map tools. Check a placement in the map preview.", "AnnouncementsPlugin": "Announcements", "TimeseriesControlPlugin": "Time series player", - "Portti2Zoombar": "Zoom bar", "ControlsPlugin": "Pan by mouse", "SearchPlugin": "Place search", "FeaturedataPlugin": "Feature data", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index a094cd76ec..263d3a630d 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Valitse kartalla käytettävissä olevat työkalut. Tarkista asettelu esikatselukartasta.", "AnnouncementsPlugin": "Ilmoitukset", "TimeseriesControlPlugin": "Aikasarjatoistin", - "Portti2Zoombar": "Mittakaavasäädin", "ControlsPlugin": "Kartan liikuttaminen hiirellä raahaamalla", "SearchPlugin": "Osoite- ja paikannimihaku", "FeaturedataPlugin": "Kohdetietotaulukko", diff --git a/bundles/framework/publisher2/resources/locale/fr.js b/bundles/framework/publisher2/resources/locale/fr.js index 3b2f009e48..01b613a827 100644 --- a/bundles/framework/publisher2/resources/locale/fr.js +++ b/bundles/framework/publisher2/resources/locale/fr.js @@ -63,7 +63,6 @@ Oskari.registerLocalization( "label": "Outils", "tooltip": "Sélectionner les outils de carte accessibles. Consulter une mise en place dans la prévisualisation de carte.", "TimeseriesControlPlugin": "Lecteur chronologique", - "Portti2Zoombar": "Barre de zoom", "ControlsPlugin": "Panoramiser avec la souris", "SearchPlugin": "Recherche de lieu", "FeaturedataPlugin": "Données de fonctionnalité", diff --git a/bundles/framework/publisher2/resources/locale/is.js b/bundles/framework/publisher2/resources/locale/is.js index 748b15ae9c..669bd89381 100755 --- a/bundles/framework/publisher2/resources/locale/is.js +++ b/bundles/framework/publisher2/resources/locale/is.js @@ -50,7 +50,6 @@ Oskari.registerLocalization( "maptools": { "label": "Tól", "tooltip": "Velja tiltæk kortatól. Athugaðu staðsetningu við forskoðun korts.", - "Portti2Zoombar": "Þysjunarstika", "ControlsPlugin": "Hliðra með mús", "SearchPlugin": "Leit eftir stað", "FeaturedataPlugin": "Fitjugögn", diff --git a/bundles/framework/publisher2/resources/locale/ru.js b/bundles/framework/publisher2/resources/locale/ru.js index 6ef8cf7d5b..07a5cca9db 100644 --- a/bundles/framework/publisher2/resources/locale/ru.js +++ b/bundles/framework/publisher2/resources/locale/ru.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "label": "Инструменты", "tooltip": "Выберите доступные инструменты карты. Проверка размещения в окне предварительного просмотра карты.", "TimeseriesControlPlugin": "Временные ряды", - "Portti2Zoombar": "Панель увеличения", "ControlsPlugin": "Панорамирование мышью", "SearchPlugin": "Поиск места", "FeaturedataPlugin": "Данные объекта", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 5c48697e10..4eeb46093a 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.", "AnnouncementsPlugin": "Aviseringar", "TimeseriesControlPlugin": "Tidseriespelare", - "Portti2Zoombar": "Skalans glidreglage", "ControlsPlugin": "Flytta kartvyn med musen", "SearchPlugin": "Adress- och ortnamnssökning", "FeaturedataPlugin": "Objektuppgifter", diff --git a/bundles/framework/publisher2/tools/ZoombarTool.js b/bundles/framework/publisher2/tools/ZoombarTool.js deleted file mode 100755 index 9da571004d..0000000000 --- a/bundles/framework/publisher2/tools/ZoombarTool.js +++ /dev/null @@ -1,43 +0,0 @@ -Oskari.clazz.define('Oskari.mapframework.publisher.tool.ZoombarTool', - function () { - }, { - index: 3, - - /** - * Get tool object. - * @method getTool - * - * @returns {Object} tool description - */ - getTool: function () { - return { - id: 'Oskari.mapframework.bundle.mapmodule.plugin.Portti2Zoombar', - title: 'Portti2Zoombar', - config: this.state.pluginConfig || {} - }; - }, - /** - * Get values. - * @method getValues - * @public - * - * @returns {Object} tool value object - */ - getValues: function () { - if (!this.isEnabled()) { - return null; - } - return { - configuration: { - mapfull: { - conf: { - plugins: [{ id: this.getTool().id, config: this.getPlugin().getConfig() }] - } - } - } - }; - } - }, { - 'extend': ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], - 'protocol': ['Oskari.mapframework.publisher.Tool'] - }); diff --git a/bundles/mapping/mapmodule/plugin/zoombar/publisher/ZoombarTool.js b/bundles/mapping/mapmodule/plugin/zoombar/publisher/ZoombarTool.js new file mode 100644 index 0000000000..6c37003c1d --- /dev/null +++ b/bundles/mapping/mapmodule/plugin/zoombar/publisher/ZoombarTool.js @@ -0,0 +1,55 @@ +import { AbstractPublisherTool } from '../../../../../framework/publisher2/tools/AbstractPublisherTool'; + +class ZoombarTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + }; + + /** + * Get tool object. + * @method getTool + * + * @returns {Object} tool description + */ + getTool () { + return { + id: 'Oskari.mapframework.bundle.mapmodule.plugin.Portti2Zoombar', + title: Oskari.getMsg('MapModule', 'publisherTools.Zoombar.toolLabel'), + config: this.state.pluginConfig || {} + }; + }; + + /** + * Get values. + * @method getValues + * @public + * + * @returns {Object} tool value object + */ + getValues () { + if (!this.isEnabled()) { + return null; + } + return { + configuration: { + mapfull: { + conf: { + plugins: [{ id: this.getTool().id, config: this.getPlugin().getConfig() }] + } + } + } + }; + }; +} + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.ZoombarTool', + ZoombarTool, + { + protocol: ['Oskari.mapframework.publisher.Tool'] + } +); + +export { ZoombarTool }; diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index f099a2e41a..3cb7517c52 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -6,5 +6,6 @@ import { ScaleBarTool } from '../plugin/scalebar/publisher/ScalebarTool'; import { MyLocationTool } from '../plugin/mylocation/publisher/MyLocationTool'; import { PanButtonsTool } from '../plugin/panbuttons/publisher/PanButtonsTool'; import { IndexMapTool } from '../plugin/indexmap/publisher/IndexMapTool'; +import { ZoombarTool } from '../plugin/zoombar/publisher/ZoombarTool'; -export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool }; +export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool }; diff --git a/bundles/mapping/mapmodule/resources/locale/en.js b/bundles/mapping/mapmodule/resources/locale/en.js index 873c493890..9cac9fbf87 100755 --- a/bundles/mapping/mapmodule/resources/locale/en.js +++ b/bundles/mapping/mapmodule/resources/locale/en.js @@ -180,7 +180,10 @@ Oskari.registerLocalization( }, "IndexMapPlugin": { "toolLabel": "Index map", - } + }, + "Zoombar": { + "toolLabel": "Zoom bar" + }, } } }); \ No newline at end of file diff --git a/bundles/mapping/mapmodule/resources/locale/fi.js b/bundles/mapping/mapmodule/resources/locale/fi.js index d0944b86e7..cc0238f0ef 100755 --- a/bundles/mapping/mapmodule/resources/locale/fi.js +++ b/bundles/mapping/mapmodule/resources/locale/fi.js @@ -180,6 +180,9 @@ Oskari.registerLocalization( }, "IndexMapPlugin": { "toolLabel": "Indeksikartta" + }, + "Zoombar": { + "toolLabel": "Mittakaavasäädin" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/fr.js b/bundles/mapping/mapmodule/resources/locale/fr.js index 9d27f9aacb..8d32d0b5e3 100755 --- a/bundles/mapping/mapmodule/resources/locale/fr.js +++ b/bundles/mapping/mapmodule/resources/locale/fr.js @@ -148,7 +148,10 @@ Oskari.registerLocalization( }, "IndexMapPlugin": { "toolLabel": "Carte-index" - } + }, + "Zoombar": { + "toolLabel": "Barre de zoom" + }, } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/is.js b/bundles/mapping/mapmodule/resources/locale/is.js index 7642578f68..d2e485e655 100755 --- a/bundles/mapping/mapmodule/resources/locale/is.js +++ b/bundles/mapping/mapmodule/resources/locale/is.js @@ -142,7 +142,10 @@ Oskari.registerLocalization( }, "IndexMapPlugin": { "toolLabel": "Index kort" - } + }, + "Zoombar": { + "toolLabel": "Þysjunarstika" + }, } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/ru.js b/bundles/mapping/mapmodule/resources/locale/ru.js index 64dd62c234..102f3226a7 100644 --- a/bundles/mapping/mapmodule/resources/locale/ru.js +++ b/bundles/mapping/mapmodule/resources/locale/ru.js @@ -141,7 +141,12 @@ Oskari.registerLocalization( "PanButtons": { "toolLabel": "Инструмент панорамирования" }, - "IndexMapPlugin": "Номенклатура карты", + "IndexMapPlugin": { + "toolLabel": "Номенклатура карты" + }, + "Zoombar": { + "toolLabel": "Панель увеличения" + }, } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/sv.js b/bundles/mapping/mapmodule/resources/locale/sv.js index d1ec24d9bc..23f12e7324 100755 --- a/bundles/mapping/mapmodule/resources/locale/sv.js +++ b/bundles/mapping/mapmodule/resources/locale/sv.js @@ -175,7 +175,10 @@ Oskari.registerLocalization( }, "IndexMapPlugin": { "toolLabel": "Indexkarta" - } + }, + "Zoombar": { + "toolLabel": "Skalans glidreglage" + }, } } }); \ No newline at end of file diff --git a/packages/framework/bundle/publisher2/bundle.js b/packages/framework/bundle/publisher2/bundle.js index e52d98dc60..63a70ad648 100755 --- a/packages/framework/bundle/publisher2/bundle.js +++ b/packages/framework/bundle/publisher2/bundle.js @@ -97,10 +97,6 @@ Oskari.clazz.define("Oskari.mapframework.bundle.publisher2.PublisherBundle", fun "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/ToolbarTool.js" }, - { - "type": "text/javascript", - "src": "../../../../bundles/framework/publisher2/tools/ZoombarTool.js" - }, { "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/SearchTool.js" From 1b73745a2a266ff7b8e65e8827dc6ca1ea3e3d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Thu, 31 Oct 2024 15:24:17 +0200 Subject: [PATCH 059/181] Clear drawcontrols only when stopping current draw --- .../mapping/drawtools/plugin/DrawPlugin.ol.js | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/bundles/mapping/drawtools/plugin/DrawPlugin.ol.js b/bundles/mapping/drawtools/plugin/DrawPlugin.ol.js index 4357c0efc1..d915ef0bd1 100644 --- a/bundles/mapping/drawtools/plugin/DrawPlugin.ol.js +++ b/bundles/mapping/drawtools/plugin/DrawPlugin.ol.js @@ -364,13 +364,21 @@ Oskari.clazz.define( // try to finish unfinished (currently drawn) feature this.forceFinishDrawing(); } - if (id && !supressEvent) { - this.sendDrawingEvent(true); + // only clear state if the stopped id is the current id + // otherwise sending startDraw with id 2 and stop with id 1 will + // remove the draw control from the draw id 2 when the requested one was 1 + const clearingAllOrCurrentDrawing = typeof id === 'undefined' || id === this.getRequestId(); + if (clearingAllOrCurrentDrawing) { + if (id && !supressEvent) { + this.sendDrawingEvent(true); + } + // remove draw and modify controls + this._cleanupInternalState(); + // enable gfi + this._gfiTimeout = setTimeout(() => this.getMapModule().setDrawingMode(false), 500); + } else { + Oskari.log('DrawPlugin').info(`Called stop with id '${id}' and draw is currently active with id '${this.getRequestId()}'.`); } - // remove draw and modify controls - this._cleanupInternalState(); - // enable gfi - this._gfiTimeout = setTimeout(() => this.getMapModule().setDrawingMode(false), 500); }, /** * @method forceFinishDrawing From 2d9979fba432360399c68412a1fc73c5be289bf7 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Thu, 31 Oct 2024 16:43:12 +0200 Subject: [PATCH 060/181] move mapmodule plugin publisher tools to one place under mapmodule --- .../publisher => publisher/indexmap}/IndexMapTool.js | 2 +- .../logo/publisher => publisher/logo}/LogoTool.js | 2 +- .../mylocation}/MyLocationComponent.jsx | 0 .../mylocation}/MyLocationHandler.js | 0 .../mylocation}/MyLocationTool.js | 2 +- .../panbuttons}/PanButtonsComponent.jsx | 0 .../panbuttons}/PanButtonsHandler.js | 0 .../panbuttons}/PanButtonsTool.js | 2 +- .../publisher => publisher/scalebar}/ScalebarTool.js | 2 +- bundles/mapping/mapmodule/publisher/tools.js | 12 ++++++------ .../publisher => publisher/zoombar}/ZoombarTool.js | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) rename bundles/mapping/mapmodule/{plugin/indexmap/publisher => publisher/indexmap}/IndexMapTool.js (91%) rename bundles/mapping/mapmodule/{plugin/logo/publisher => publisher/logo}/LogoTool.js (95%) rename bundles/mapping/mapmodule/{plugin/mylocation/publisher => publisher/mylocation}/MyLocationComponent.jsx (100%) rename bundles/mapping/mapmodule/{plugin/mylocation/publisher => publisher/mylocation}/MyLocationHandler.js (100%) rename bundles/mapping/mapmodule/{plugin/mylocation/publisher => publisher/mylocation}/MyLocationTool.js (94%) rename bundles/mapping/mapmodule/{plugin/panbuttons/publisher => publisher/panbuttons}/PanButtonsComponent.jsx (100%) rename bundles/mapping/mapmodule/{plugin/panbuttons/publisher => publisher/panbuttons}/PanButtonsHandler.js (100%) rename bundles/mapping/mapmodule/{plugin/panbuttons/publisher => publisher/panbuttons}/PanButtonsTool.js (94%) rename bundles/mapping/mapmodule/{plugin/scalebar/publisher => publisher/scalebar}/ScalebarTool.js (91%) rename bundles/mapping/mapmodule/{plugin/zoombar/publisher => publisher/zoombar}/ZoombarTool.js (92%) diff --git a/bundles/mapping/mapmodule/plugin/indexmap/publisher/IndexMapTool.js b/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js similarity index 91% rename from bundles/mapping/mapmodule/plugin/indexmap/publisher/IndexMapTool.js rename to bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js index 2292b6d124..a677687bc5 100644 --- a/bundles/mapping/mapmodule/plugin/indexmap/publisher/IndexMapTool.js +++ b/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js @@ -1,4 +1,4 @@ -import { AbstractPublisherTool } from '../../../../../framework/publisher2/tools/AbstractPublisherTool'; +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; class IndexMapTool extends AbstractPublisherTool { constructor (...args) { diff --git a/bundles/mapping/mapmodule/plugin/logo/publisher/LogoTool.js b/bundles/mapping/mapmodule/publisher/logo/LogoTool.js similarity index 95% rename from bundles/mapping/mapmodule/plugin/logo/publisher/LogoTool.js rename to bundles/mapping/mapmodule/publisher/logo/LogoTool.js index 2954900fd0..11cdb63f71 100644 --- a/bundles/mapping/mapmodule/plugin/logo/publisher/LogoTool.js +++ b/bundles/mapping/mapmodule/publisher/logo/LogoTool.js @@ -1,4 +1,4 @@ -import { AbstractPublisherTool } from '../../../../../framework/publisher2/tools/AbstractPublisherTool'; +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; class LogoTool extends AbstractPublisherTool { constructor (...args) { diff --git a/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationComponent.jsx b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationComponent.jsx similarity index 100% rename from bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationComponent.jsx rename to bundles/mapping/mapmodule/publisher/mylocation/MyLocationComponent.jsx diff --git a/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationHandler.js b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationHandler.js similarity index 100% rename from bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationHandler.js rename to bundles/mapping/mapmodule/publisher/mylocation/MyLocationHandler.js diff --git a/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationTool.js b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js similarity index 94% rename from bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationTool.js rename to bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js index d47772f0a5..23f101bdbf 100644 --- a/bundles/mapping/mapmodule/plugin/mylocation/publisher/MyLocationTool.js +++ b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js @@ -1,4 +1,4 @@ -import { AbstractPublisherTool } from '../../../../../framework/publisher2/tools/AbstractPublisherTool'; +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; import { MyLocationComponent } from './MyLocationComponent'; import { MyLocationToolHandler } from './MyLocationHandler'; diff --git a/bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsComponent.jsx b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsComponent.jsx similarity index 100% rename from bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsComponent.jsx rename to bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsComponent.jsx diff --git a/bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsHandler.js b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsHandler.js similarity index 100% rename from bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsHandler.js rename to bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsHandler.js diff --git a/bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsTool.js b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js similarity index 94% rename from bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsTool.js rename to bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js index 5e228320c5..9a4b1e1d26 100644 --- a/bundles/mapping/mapmodule/plugin/panbuttons/publisher/PanButtonsTool.js +++ b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js @@ -1,4 +1,4 @@ -import { AbstractPublisherTool } from '../../../../../framework/publisher2/tools/AbstractPublisherTool'; +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; import { PanButtonsComponent } from './PanButtonsComponent'; import { PanButtonsHandler } from './PanButtonsHandler'; class PanButtonsTool extends AbstractPublisherTool { diff --git a/bundles/mapping/mapmodule/plugin/scalebar/publisher/ScalebarTool.js b/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js similarity index 91% rename from bundles/mapping/mapmodule/plugin/scalebar/publisher/ScalebarTool.js rename to bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js index 0933fcefcc..a390c44623 100644 --- a/bundles/mapping/mapmodule/plugin/scalebar/publisher/ScalebarTool.js +++ b/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js @@ -1,4 +1,4 @@ -import { AbstractPublisherTool } from '../../../../../framework/publisher2/tools/AbstractPublisherTool'; +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; const SCALEBAR_TOOL_ID = 'Oskari.mapframework.bundle.mapmodule.plugin.ScaleBarPlugin'; class ScaleBarTool extends AbstractPublisherTool { diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index 3cb7517c52..736d6ac020 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -1,11 +1,11 @@ /** * Import plugins to be exposed to publisher */ -import { LogoTool } from '../plugin/logo/publisher/LogoTool'; -import { ScaleBarTool } from '../plugin/scalebar/publisher/ScalebarTool'; -import { MyLocationTool } from '../plugin/mylocation/publisher/MyLocationTool'; -import { PanButtonsTool } from '../plugin/panbuttons/publisher/PanButtonsTool'; -import { IndexMapTool } from '../plugin/indexmap/publisher/IndexMapTool'; -import { ZoombarTool } from '../plugin/zoombar/publisher/ZoombarTool'; +import { LogoTool } from './logo/LogoTool'; +import { ScaleBarTool } from './scalebar/ScalebarTool'; +import { MyLocationTool } from './mylocation/MyLocationTool'; +import { PanButtonsTool } from './panbuttons/PanButtonsTool'; +import { IndexMapTool } from './indexmap/IndexMapTool'; +import { ZoombarTool } from './zoombar/ZoombarTool'; export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool }; diff --git a/bundles/mapping/mapmodule/plugin/zoombar/publisher/ZoombarTool.js b/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js similarity index 92% rename from bundles/mapping/mapmodule/plugin/zoombar/publisher/ZoombarTool.js rename to bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js index 6c37003c1d..7ab0e3fbbc 100644 --- a/bundles/mapping/mapmodule/plugin/zoombar/publisher/ZoombarTool.js +++ b/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js @@ -1,4 +1,4 @@ -import { AbstractPublisherTool } from '../../../../../framework/publisher2/tools/AbstractPublisherTool'; +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; class ZoombarTool extends AbstractPublisherTool { constructor (...args) { From 026c57dcb813c9882b7287702ce43655cf284455 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Fri, 1 Nov 2024 10:21:31 +0200 Subject: [PATCH 061/181] move crosshair tool under mapmodule and reactify further --- .../framework/publisher2/resources/locale/en.js | 1 - .../framework/publisher2/resources/locale/fi.js | 1 - .../framework/publisher2/resources/locale/fr.js | 1 - .../framework/publisher2/resources/locale/is.js | 1 - .../framework/publisher2/resources/locale/ru.js | 1 - .../framework/publisher2/resources/locale/sv.js | 1 - .../publisher/crosshair}/CrosshairTool.js | 16 +++++++++++++--- bundles/mapping/mapmodule/publisher/tools.js | 4 ++-- bundles/mapping/mapmodule/resources/locale/en.js | 3 +++ bundles/mapping/mapmodule/resources/locale/fi.js | 3 +++ bundles/mapping/mapmodule/resources/locale/fr.js | 3 +++ bundles/mapping/mapmodule/resources/locale/is.js | 3 +++ bundles/mapping/mapmodule/resources/locale/ru.js | 3 +++ bundles/mapping/mapmodule/resources/locale/sv.js | 3 +++ packages/framework/bundle/publisher2/bundle.js | 4 ---- 15 files changed, 33 insertions(+), 15 deletions(-) rename bundles/{framework/publisher2/tools => mapping/mapmodule/publisher/crosshair}/CrosshairTool.js (80%) mode change 100755 => 100644 diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 9c0f1d945f..2a2949c322 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -76,7 +76,6 @@ Oskari.registerLocalization( "CoordinateToolPlugin": "Coordinate tool", "MapLegend": "Show map legend", "MapRotator": "Enable map rotation", - "CrosshairTool": "Show map focal point", "CameraControls3d": "Camera tools", "TimeControl3d": "Time control", "toolbarToolNames": { diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 263d3a630d..61f712b092 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -76,7 +76,6 @@ Oskari.registerLocalization( "CoordinateToolPlugin": "Koordinaattityökalu", "MapLegend": "Näytä karttaselitteet", "MapRotator": "Salli kartan pyörittäminen", - "CrosshairTool": "Näytä kartan keskipiste", "CameraControls3d": "Kameratyökalut", "TimeControl3d": "Ajanhetken säädin", "toolbarToolNames": { diff --git a/bundles/framework/publisher2/resources/locale/fr.js b/bundles/framework/publisher2/resources/locale/fr.js index 01b613a827..d264324612 100644 --- a/bundles/framework/publisher2/resources/locale/fr.js +++ b/bundles/framework/publisher2/resources/locale/fr.js @@ -74,7 +74,6 @@ Oskari.registerLocalization( "FeedbackServiceTool": "Service de rétroaction (Open311)", "MapLegend": "Afficher la légende de la carte", "MapRotator": "Activer la rotation de la carte", - "CrosshairTool": "Afficher le point d'intérêt de la carte", "toolbarToolNames": { "history": "Passer à la vue précédente ou suivante", "history_back": "Passer à la vue précédente", diff --git a/bundles/framework/publisher2/resources/locale/is.js b/bundles/framework/publisher2/resources/locale/is.js index 669bd89381..f29bc339b1 100755 --- a/bundles/framework/publisher2/resources/locale/is.js +++ b/bundles/framework/publisher2/resources/locale/is.js @@ -58,7 +58,6 @@ Oskari.registerLocalization( "selectDrawLayer": "Velja kortalag", "LayerSelectionPlugin": "Valmynd fyrir kortalög", "CoordinateToolPlugin": "Hnitatól", - "CrosshairTool": "Sýna aðalatriði korts", "FeedbackServiceTool": "", "toolbarToolNames": { "history_back": "Færa í fyrri sýn", diff --git a/bundles/framework/publisher2/resources/locale/ru.js b/bundles/framework/publisher2/resources/locale/ru.js index 07a5cca9db..0c17ff66c9 100644 --- a/bundles/framework/publisher2/resources/locale/ru.js +++ b/bundles/framework/publisher2/resources/locale/ru.js @@ -75,7 +75,6 @@ Oskari.registerLocalization( "FeedbackServiceTool": "Обратная связь (Open311) ", "MapLegend": "Показать условные знаки", "MapRotator": "Включить поворот карты", - "CrosshairTool": "Показать фокус карты", "toolbarToolNames": { "history": "Перейти к предыдущему или следующему виду", "history_back": "Перейти к предыдущему виду", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 4eeb46093a..3155fe13da 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -76,7 +76,6 @@ Oskari.registerLocalization( "CoordinateToolPlugin": "Koordinatverktyg", "MapLegend": "Visa kartförklaringen", "MapRotator": "Tillåt kartrotation", - "CrosshairTool": "Visa kartans mittpunkt", "CameraControls3d": "Kameraverktyg", "TimeControl3d": "Tidskontroll", "toolbarToolNames": { diff --git a/bundles/framework/publisher2/tools/CrosshairTool.js b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js old mode 100755 new mode 100644 similarity index 80% rename from bundles/framework/publisher2/tools/CrosshairTool.js rename to bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js index 9609698a28..cd9e865516 --- a/bundles/framework/publisher2/tools/CrosshairTool.js +++ b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js @@ -1,19 +1,27 @@ -import { AbstractPublisherTool } from './AbstractPublisherTool'; +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; class CrosshairTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + } + getTool () { return { id: 'Oskari.mapframework.publisher.tool.CrosshairTool', - title: 'CrosshairTool', + title: Oskari.getMsg('MapModule', 'publisherTools.CrosshairTool.toolLabel'), config: this.state.pluginConfig || {}, hasNoPlugin: true }; } + init (data) { if (Oskari.util.keyExists(data, 'configuration.mapfull.conf.mapOptions.crosshair')) { this.setEnabled(!!data?.configuration?.mapfull?.conf?.mapOptions?.crosshair); } } + // override since we want to use the instance we currently have, not create a new one setEnabled (enabled) { const changed = super.setEnabled(enabled); @@ -25,6 +33,7 @@ class CrosshairTool extends AbstractPublisherTool { mapModule.toggleCrosshair(enabled); } } + getValues () { if (!this.isEnabled()) { return null; @@ -41,6 +50,7 @@ class CrosshairTool extends AbstractPublisherTool { } }; } + stop () { super.stop(); // remove crosshair from map @@ -55,6 +65,6 @@ class CrosshairTool extends AbstractPublisherTool { Oskari.clazz.defineES('Oskari.publisher.CrosshairTool', CrosshairTool, { - 'protocol': ['Oskari.mapframework.publisher.Tool'] + protocol: ['Oskari.mapframework.publisher.Tool'] } ); diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index 736d6ac020..cf89faa9df 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -7,5 +7,5 @@ import { MyLocationTool } from './mylocation/MyLocationTool'; import { PanButtonsTool } from './panbuttons/PanButtonsTool'; import { IndexMapTool } from './indexmap/IndexMapTool'; import { ZoombarTool } from './zoombar/ZoombarTool'; - -export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool }; +import { CrossHairTool } from './crosshair/CrosshairTool'; +export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool, CrossHairTool }; diff --git a/bundles/mapping/mapmodule/resources/locale/en.js b/bundles/mapping/mapmodule/resources/locale/en.js index 9cac9fbf87..8f0bbaf18c 100755 --- a/bundles/mapping/mapmodule/resources/locale/en.js +++ b/bundles/mapping/mapmodule/resources/locale/en.js @@ -184,6 +184,9 @@ Oskari.registerLocalization( "Zoombar": { "toolLabel": "Zoom bar" }, + "CrosshairTool": { + "toolLabel": "Show map focal point" + } } } }); \ No newline at end of file diff --git a/bundles/mapping/mapmodule/resources/locale/fi.js b/bundles/mapping/mapmodule/resources/locale/fi.js index cc0238f0ef..f4e3091c09 100755 --- a/bundles/mapping/mapmodule/resources/locale/fi.js +++ b/bundles/mapping/mapmodule/resources/locale/fi.js @@ -183,6 +183,9 @@ Oskari.registerLocalization( }, "Zoombar": { "toolLabel": "Mittakaavasäädin" + }, + "CrosshairTool": { + "toolLabel": "Näytä kartan keskipiste" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/fr.js b/bundles/mapping/mapmodule/resources/locale/fr.js index 8d32d0b5e3..7aaaf20a79 100755 --- a/bundles/mapping/mapmodule/resources/locale/fr.js +++ b/bundles/mapping/mapmodule/resources/locale/fr.js @@ -152,6 +152,9 @@ Oskari.registerLocalization( "Zoombar": { "toolLabel": "Barre de zoom" }, + "CrosshairTool": { + "toolLabel": "Afficher le point d'intérêt de la carte" + } } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/is.js b/bundles/mapping/mapmodule/resources/locale/is.js index d2e485e655..e9f6a8a8c0 100755 --- a/bundles/mapping/mapmodule/resources/locale/is.js +++ b/bundles/mapping/mapmodule/resources/locale/is.js @@ -146,6 +146,9 @@ Oskari.registerLocalization( "Zoombar": { "toolLabel": "Þysjunarstika" }, + "CrosshairTool": { + "toolLabel": "Sýna aðalatriði korts" + } } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/ru.js b/bundles/mapping/mapmodule/resources/locale/ru.js index 102f3226a7..8ef6ec8ffd 100644 --- a/bundles/mapping/mapmodule/resources/locale/ru.js +++ b/bundles/mapping/mapmodule/resources/locale/ru.js @@ -147,6 +147,9 @@ Oskari.registerLocalization( "Zoombar": { "toolLabel": "Панель увеличения" }, + "CrosshairTool": { + "toolLabel": "Показать фокус карты" + } } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/sv.js b/bundles/mapping/mapmodule/resources/locale/sv.js index 23f12e7324..5af3b0f801 100755 --- a/bundles/mapping/mapmodule/resources/locale/sv.js +++ b/bundles/mapping/mapmodule/resources/locale/sv.js @@ -179,6 +179,9 @@ Oskari.registerLocalization( "Zoombar": { "toolLabel": "Skalans glidreglage" }, + "CrosshairTool": { + "toolLabel": "Visa kartans mittpunkt" + } } } }); \ No newline at end of file diff --git a/packages/framework/bundle/publisher2/bundle.js b/packages/framework/bundle/publisher2/bundle.js index 63a70ad648..dbdacc0342 100755 --- a/packages/framework/bundle/publisher2/bundle.js +++ b/packages/framework/bundle/publisher2/bundle.js @@ -117,10 +117,6 @@ Oskari.clazz.define("Oskari.mapframework.bundle.publisher2.PublisherBundle", fun "type": "text/javascript", "src": "../../../../bundles/mapping/mapmodule/publisher/tools.js" }, - { - "type": "text/javascript", - "src": "../../../../bundles/framework/publisher2/tools/CrosshairTool.js" - }, { "type": "text/css", "src": "../../../../bundles/framework/publisher2/resources/scss/style.scss" From 3bf6159b77e6552ed5d2378f356d98eac9ed7c0d Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 1 Nov 2024 10:38:43 +0200 Subject: [PATCH 062/181] fix typo --- bundles/admin/admin-permissions/handler/layerRightsHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/admin/admin-permissions/handler/layerRightsHandler.js b/bundles/admin/admin-permissions/handler/layerRightsHandler.js index 11359ed8c7..26c8b61e40 100644 --- a/bundles/admin/admin-permissions/handler/layerRightsHandler.js +++ b/bundles/admin/admin-permissions/handler/layerRightsHandler.js @@ -138,7 +138,7 @@ class UIHandler extends StateHandler { }; } - _mapPerimssions (permissions) { + _mapPermissions (permissions) { // move the recognized permissionTypes to the front with additional styles in random order const orderedTypes = DEFAULT_PERMISSIONS.filter(type => permissions[type]); const additionalTypes = Object.keys(permissions).filter(type => !orderedTypes.includes(type)); @@ -169,7 +169,7 @@ class UIHandler extends StateHandler { } const { names = {}, layers = [] } = await response.json(); this.updateState({ - permissions: this._mapPerimssions(names), + permissions: this._mapPermissions(names), resources: layers.map(l => this._mapLayerToResource(l)) }); this.setLoading(false); From 6a0fc74d20be544cf004f2219e14b0756e16c04f Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Fri, 1 Nov 2024 10:58:49 +0200 Subject: [PATCH 063/181] export/import fix --- .../mapping/mapmodule/publisher/crosshair/CrosshairTool.js | 2 ++ bundles/mapping/mapmodule/publisher/tools.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js index cd9e865516..484ae1f079 100644 --- a/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js +++ b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js @@ -68,3 +68,5 @@ Oskari.clazz.defineES('Oskari.publisher.CrosshairTool', protocol: ['Oskari.mapframework.publisher.Tool'] } ); + +export { CrosshairTool }; diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index cf89faa9df..6637da9c99 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -7,5 +7,5 @@ import { MyLocationTool } from './mylocation/MyLocationTool'; import { PanButtonsTool } from './panbuttons/PanButtonsTool'; import { IndexMapTool } from './indexmap/IndexMapTool'; import { ZoombarTool } from './zoombar/ZoombarTool'; -import { CrossHairTool } from './crosshair/CrosshairTool'; -export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool, CrossHairTool }; +import { CrosshairTool } from './crosshair/CrosshairTool'; +export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool, CrosshairTool }; From c312e77c2da92890e2c39ebfe48120c3d796d29c Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 1 Nov 2024 18:25:10 +0200 Subject: [PATCH 064/181] add default ok and cancel localizations for Confirm --- src/react/components/Confirm.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/react/components/Confirm.jsx b/src/react/components/Confirm.jsx index 4cebed1ebf..07f3c3c2b5 100644 --- a/src/react/components/Confirm.jsx +++ b/src/react/components/Confirm.jsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Popconfirm } from 'antd'; +import { Message } from 'oskari-ui'; // NOTE! z-index is overridden in resources/css/portal.css // Without the override the confirm is shown behind flyouts (for example in admin) @@ -28,9 +29,18 @@ Check the render method of `styled__IconButton`. There seems to be a problem with tooltips with styled-components as direct children: https://stackoverflow.com/questions/61450739/understanding-warning-function-components-cannot-be-given-refs */ -export const Confirm = ({ children, cancelButtonProps = {}, okButtonProps = {}, ...other }) => ( +export const Confirm = ({ + children, + cancelButtonProps = {}, + okButtonProps = {}, + okText = <Message bundleKey='oskariui' messageKey='buttons.yes'/>, + cancelText = <Message bundleKey='oskariui' messageKey='buttons.cancel'/>, + ...other +}) => ( <Popconfirm overlayClassName='t_confirm' + okText={okText} + cancelText={cancelText} okButtonProps={{ className: 't_button t_ok', ...okButtonProps }} From bd0bb964fc711b00d4e9efa39f1a8d6b9ae9399a Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 1 Nov 2024 18:39:54 +0200 Subject: [PATCH 065/181] remove props --- bundles/admin/admin-permissions/view/LayerRights.jsx | 6 +----- .../LayerList/LayerCollapse/AllLayersSwitch.jsx | 6 ------ 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/bundles/admin/admin-permissions/view/LayerRights.jsx b/bundles/admin/admin-permissions/view/LayerRights.jsx index eff832fe06..992bffc287 100644 --- a/bundles/admin/admin-permissions/view/LayerRights.jsx +++ b/bundles/admin/admin-permissions/view/LayerRights.jsx @@ -59,11 +59,7 @@ export const LayerRights = ({ controller, state }) => { title={<Message messageKey='flyout.unsavedChangesConfirm'/>} open={roleConfirmOpen} onConfirm={() => onRoleConfirm(true) } - onCancel={() => onRoleConfirm(false) } - okText={<Message bundleKey='oskariui' messageKey='buttons.yes'/>} - cancelText={<Message bundleKey='oskariui' messageKey='buttons.cancel'/>} - placement='top' - popupStyle={{ zIndex: '999999' }}> + onCancel={() => onRoleConfirm(false) }> <StyledSelect placeholder={<Message messageKey='roles.placeholder'/>} value={state.selectedRole} diff --git a/bundles/framework/layerlist/view/LayerViewTabs/LayerList/LayerCollapse/AllLayersSwitch.jsx b/bundles/framework/layerlist/view/LayerViewTabs/LayerList/LayerCollapse/AllLayersSwitch.jsx index 210d45058e..acc280cade 100644 --- a/bundles/framework/layerlist/view/LayerViewTabs/LayerList/LayerCollapse/AllLayersSwitch.jsx +++ b/bundles/framework/layerlist/view/LayerViewTabs/LayerList/LayerCollapse/AllLayersSwitch.jsx @@ -45,15 +45,11 @@ export const AllLayersSwitch = ({ checked, onToggle, layerCount = 0 }) => { showConfirm(false); event.stopPropagation(); }} - okText={<Message messageKey='yes'/>} // TODO: try to link tooltip to the flyout so it's removed if the flyout is closed. // These didn't solve the problem but might be helpful // div.oskari-flyout.layerlist //getPopupContainer={(triggerNode) => document.querySelector('div.oskari-flyout.layerlist')} //destroyTooltipOnHide={true} - cancelText={<Message messageKey='cancel'/>} - placement='top' - popupStyle={{zIndex: '999999'}} > <StyledSwitch size="small" @@ -69,5 +65,3 @@ AllLayersSwitch.propTypes = { layerCount: PropTypes.number, onToggle: PropTypes.func.isRequired }; - - \ No newline at end of file From 0e9f3af575d1569b88a015941928ffb43c869377 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 2 Nov 2024 01:28:11 +0200 Subject: [PATCH 066/181] split to ThemedSlider, rename prop, add hover --- src/react/components/Slider.jsx | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/react/components/Slider.jsx b/src/react/components/Slider.jsx index 8aef031c14..c7bab71c0b 100644 --- a/src/react/components/Slider.jsx +++ b/src/react/components/Slider.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { Slider as AntSlider } from 'antd'; import styled from 'styled-components'; +import { ThemeConsumer } from 'oskari-ui/util'; import { getNavigationTheme, DEFAULT_COLORS } from 'oskari-ui/theme'; const normal = 4; @@ -43,26 +44,31 @@ const ThemedAntSlider = styled(AntSlider)` top: 0px; height: ${props => props.useThick ? thick : normal}px; } + &:hover .ant-slider-track, + &:hover .ant-slider-handle { + background-color: ${props => props.theme.getAccentHover()} !important; + } `; const getThemedStyle = (theme, vertical, useThick) => { - const width = vertical ? 'height' : 'width'; - const height = vertical ? 'width' : 'height'; + const handleSize = vertical ? 'height' : 'width'; + const thickness = vertical ? 'width' : 'height'; + const alignment = vertical ? 'left' : 'top'; const size = useThick ? thick : normal; return { track: { - [height]: `${size}px`, - backgroundColor: theme.getButtonHoverColor() + [thickness]: `${size}px`, + backgroundColor: theme.getAccent() }, rail: { - [height]: `${size}px`, + [thickness]: `${size}px`, backgroundColor: theme.getTextColor() }, handle: { - backgroundColor: theme.getButtonHoverColor(), - [width]: '8px', - [height]: `${size + handler}px`, - top: '-2px', + backgroundColor: theme.getAccent(), + [handleSize]: '8px', + [thickness]: `${size + handler}px`, + [alignment]: '-2px', borderRadius: '6px', border: 'solid 1px #3c3c3c' } @@ -70,10 +76,11 @@ const getThemedStyle = (theme, vertical, useThick) => { }; export const Slider = ({ theme, useThick, vertical, ...rest }) => { - if (!theme) { - return <StyledAntSlider vertical= {vertical} {...rest} /> - } + return <StyledAntSlider vertical= {vertical} {...rest} /> +}; + +export const ThemedSlider = ThemeConsumer(({ theme, useThick, vertical, ...rest }) => { const helper = getNavigationTheme(theme); const style = getThemedStyle(helper, vertical, useThick); return <ThemedAntSlider styles={style} theme={helper} useThick={useThick} vertical={vertical} {...rest} /> -}; +}); From 1088b366719966007547585ddd8551c7b8d89874 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 2 Nov 2024 01:28:51 +0200 Subject: [PATCH 067/181] add ThemedButton --- src/react/components/Button.jsx | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/react/components/Button.jsx b/src/react/components/Button.jsx index 9b9762047e..f01c49c0e4 100644 --- a/src/react/components/Button.jsx +++ b/src/react/components/Button.jsx @@ -1,6 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; +import styled from 'styled-components'; import { Button as AntButton } from 'antd'; +import { ThemeConsumer } from 'oskari-ui/util'; +import { getNavigationTheme } from 'oskari-ui/theme'; export const Button = ({ children, className, loading = false, ...other }) => { let modifiedClass = className || ''; @@ -13,3 +16,27 @@ export const Button = ({ children, className, loading = false, ...other }) => { Button.propTypes = { children: PropTypes.any }; + +const StyledButton = styled(Button)` + background: ${props => props.theme.getAccent()}; + border: none; + color: inherit; + fill: currentColor; + + &:focus, + &:active, + &&&:hover { + background: ${props => props.theme.getAccentHover()}; + color: inherit; + border: none; + } +`; + +export const ThemedButton = ThemeConsumer(({ theme, children, ...other }) => { + return <StyledButton theme={getNavigationTheme(theme)} {...other}>{children}</StyledButton>; +}); + +ThemedButton.propTypes = { + theme: PropTypes.object.isRequired, + children: PropTypes.any +}; From 16bd1c461ad7655727d75fa2631f9473115065e7 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 2 Nov 2024 01:29:53 +0200 Subject: [PATCH 068/181] export themed button and slider --- src/react/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/react/index.js b/src/react/index.js index e8c2457254..95f8c966df 100644 --- a/src/react/index.js +++ b/src/react/index.js @@ -3,7 +3,7 @@ import 'antd/dist/reset.css'; export { Alert } from './components/Alert'; export { Badge } from './components/Badge'; -export { Button } from './components/Button'; +export { Button, ThemedButton } from './components/Button'; export { Checkbox } from './components/Checkbox'; export { Collapse, Panel as CollapsePanel } from './components/Collapse'; export { Confirm } from './components/Confirm'; @@ -18,7 +18,7 @@ export { Popover } from './components/Popover'; export { Radio } from './components/Radio'; export { SearchInput } from './components/SearchInput'; export { Select, Option } from './components/Select'; -export { Slider } from './components/Slider'; +export { Slider, ThemedSlider } from './components/Slider'; export { Space } from './components/Space'; export { Spin } from './components/Spin'; export { Steps } from './components/Steps'; From ec80906fceb18438f4ee9877cc589168b57c7dce Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 2 Nov 2024 01:30:56 +0200 Subject: [PATCH 069/181] add getter for accent and hover --- src/react/theme/ThemeHelper.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/react/theme/ThemeHelper.js b/src/react/theme/ThemeHelper.js index 5215558c31..780ea93cbc 100644 --- a/src/react/theme/ThemeHelper.js +++ b/src/react/theme/ThemeHelper.js @@ -54,6 +54,7 @@ export const getAntdTheme = (theme) => { export const getNavigationTheme = (theme) => { const primary = theme.navigation?.color?.primary || DEFAULT_COLORS.DARK_BUTTON_BG; + const accent = theme.navigation?.color?.accent || theme.color.accent || DEFAULT_COLORS.ACCENT; const textColor = getTextColor(primary); let borderRadius = 0; if (theme?.navigation?.roundness) { @@ -68,9 +69,11 @@ export const getNavigationTheme = (theme) => { } const funcs = { getPrimary: () => primary, + getAccent: () => accent, + getAccentHover: () => Oskari.util.getColorEffect(accent, 30), getTextColor: () => theme.navigation?.color?.text || textColor, getButtonColor: () => buttonColor, - getButtonHoverColor: () => theme.navigation?.color?.accent || theme.color.accent || DEFAULT_COLORS.ACCENT, + getButtonHoverColor: () => accent, // like 50% getButtonRoundness: () => `${borderRadius}%`, // like 0.5 for calc() usage From 4ab20d47f6f69d00c3d635fef29b1f92520fac19 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 2 Nov 2024 01:38:05 +0200 Subject: [PATCH 070/181] use themed slider and button from oskari-ui --- .../view/TimeControl3d/DateControl.jsx | 16 +++++----------- .../view/TimeControl3d/Input.jsx | 19 ++++++------------- .../view/TimeControl3d/TimeSlider.jsx | 16 +++++----------- .../view/TimeControl3d/styled.jsx | 19 +++---------------- 4 files changed, 19 insertions(+), 51 deletions(-) diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d/DateControl.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d/DateControl.jsx index 7f5eec5e95..ba6c91468a 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d/DateControl.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d/DateControl.jsx @@ -1,9 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Message, Slider } from 'oskari-ui'; +import { Message, ThemedSlider } from 'oskari-ui'; import { Row, Col, StyledButton, DateSliderContainer, CalendarIcon, ColFixed } from './styled'; -import { ThemeConsumer } from 'oskari-ui/util'; -import { getHeaderTheme } from 'oskari-ui/theme'; import { Input } from './Input'; import dayjs from 'dayjs'; import customParseFormat from 'dayjs/plugin/customParseFormat'; @@ -14,10 +12,7 @@ dayjs.extend(customParseFormat); */ const DAYS = 365; -export const DateControl = ThemeConsumer(({ theme, isMobile, changeHandler, sliderDateValue, dateValue, currentTimeHandler }) => { - const helper = getHeaderTheme(theme); - const color = helper.getAccentColor(); - const hover = Oskari.util.getColorEffect(color, 30); +export const DateControl = ({ isMobile, changeHandler, sliderDateValue, dateValue, currentTimeHandler }) => { const changeSliderDate = (val) => { const d = new Date(2019, 0, val); const timeToSet = dayjs(d).format('D/M'); @@ -54,9 +49,8 @@ export const DateControl = ThemeConsumer(({ theme, isMobile, changeHandler, slid {!isMobile && <ColFixed> <DateSliderContainer> - <Slider + <ThemedSlider noMargin - theme={theme} marks={marksForDate()} min={1} max={DAYS} step={1} value={sliderDateValue} @@ -66,13 +60,13 @@ export const DateControl = ThemeConsumer(({ theme, isMobile, changeHandler, slid </ColFixed> } <Col> - <StyledButton hover={hover} color={color} onClick={setCurrentTime}> + <StyledButton onClick={setCurrentTime}> <Message messageKey={'present'} /> </StyledButton> </Col> </Row> ); -}); +}; DateControl.propTypes = { isMobile: PropTypes.bool.isRequired, diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d/Input.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d/Input.jsx index b0de45eb98..db6be689ea 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d/Input.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d/Input.jsx @@ -2,19 +2,12 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { StyledInput } from './styled'; -export const Input = ({ value, changeHandler, children }) => { - const onChange = event => { - const val = event.target.value; - changeHandler(val); - }; - - return ( - <Fragment> - { children } - <StyledInput value={value} onChange={onChange} /> - </Fragment> - ); -}; +export const Input = ({ value, changeHandler, children }) => ( + <Fragment> + { children } + <StyledInput value={value} onChange={(event) => changeHandler(event.target.value)} /> + </Fragment> +); Input.propTypes = { value: PropTypes.string.isRequired, diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d/TimeSlider.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d/TimeSlider.jsx index 1d38db6bf8..12a1d02e9b 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d/TimeSlider.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d/TimeSlider.jsx @@ -1,16 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { InputGroup, Slider } from 'oskari-ui'; +import { InputGroup, ThemedSlider } from 'oskari-ui'; import { TimeBorder, StyledPlayButton } from './styled'; import { PlayButton } from './PlayButton'; -import { ThemeConsumer } from 'oskari-ui/util'; -import { getHeaderTheme } from 'oskari-ui/theme'; const MINUTES = 1439; -export const TimeSlider = ThemeConsumer(({ theme, isMobile, changeHandler, sliderTimeValue, playing, playHandler }) => { - const helper = getHeaderTheme(theme); - const color = helper.getAccentColor(); - const hover = Oskari.util.getColorEffect(color, 30); +export const TimeSlider = ({ isMobile, changeHandler, sliderTimeValue, playing, playHandler }) => { const clickPlayButton = () => { playHandler(!playing); }; @@ -30,13 +25,12 @@ export const TimeSlider = ThemeConsumer(({ theme, isMobile, changeHandler, slide return ( <InputGroup block> - <StyledPlayButton onClick={clickPlayButton} hover={hover} color={color}> + <StyledPlayButton onClick={clickPlayButton}> <PlayButton initial={playing} /> </StyledPlayButton> <TimeBorder isMobile={isMobile}> - <Slider + <ThemedSlider noMargin - theme={theme} useThick={isMobile} marks={marksForTime} min={0} max={MINUTES} @@ -46,7 +40,7 @@ export const TimeSlider = ThemeConsumer(({ theme, isMobile, changeHandler, slide </TimeBorder> </InputGroup> ); -}); +}; TimeSlider.propTypes = { isMobile: PropTypes.bool.isRequired, sliderTimeValue: PropTypes.number.isRequired, diff --git a/bundles/mapping/time-control-3d/view/TimeControl3d/styled.jsx b/bundles/mapping/time-control-3d/view/TimeControl3d/styled.jsx index 9a6936312d..5e0630769b 100644 --- a/bundles/mapping/time-control-3d/view/TimeControl3d/styled.jsx +++ b/bundles/mapping/time-control-3d/view/TimeControl3d/styled.jsx @@ -1,5 +1,5 @@ import styled from 'styled-components'; -import { Button, Select } from 'oskari-ui'; +import { ThemedButton, Select } from 'oskari-ui'; import { CalendarOutlined, ClockCircleOutlined } from '@ant-design/icons'; export const CalendarIcon = styled(CalendarOutlined)` @@ -38,27 +38,14 @@ export const StyledInput = styled.input` text-align: center; `; -const ThemedButton = styled(Button)` - background: ${props => props.color}; - border: none; - font-size: 16px; - fill: currentColor; - - &:focus, - &:active, - &&&:hover { - background: ${props => props.hover}; - color: inherit; - border: none; - } -`; - export const StyledButton = styled(ThemedButton)` + font-size: 16px; width: 100%; height: 40px; `; export const StyledPlayButton = styled(ThemedButton)` + font-size: 16px; padding: 0; height: 42px; width: 40px; From 9fd36e716a19123d194f3d5563676d8ece654191 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 2 Nov 2024 10:27:54 +0200 Subject: [PATCH 071/181] use semantic dom to set slider styling --- .../mapmodule/plugin/zoombar/ZoomSlider.jsx | 92 +++++++------------ 1 file changed, 32 insertions(+), 60 deletions(-) diff --git a/bundles/mapping/mapmodule/plugin/zoombar/ZoomSlider.jsx b/bundles/mapping/mapmodule/plugin/zoombar/ZoomSlider.jsx index faecf7193e..4f2c3cad79 100644 --- a/bundles/mapping/mapmodule/plugin/zoombar/ZoomSlider.jsx +++ b/bundles/mapping/mapmodule/plugin/zoombar/ZoomSlider.jsx @@ -1,11 +1,13 @@ import React from 'react'; import { MapModuleButton } from '../../MapModuleButton'; import styled from 'styled-components'; -import { Slider } from 'oskari-ui'; +import { Slider } from 'antd'; import { PlusOutlined, MinusOutlined } from '@ant-design/icons'; import { ThemeConsumer, ThemeProvider } from 'oskari-ui/util'; import { getNavigationTheme } from 'oskari-ui/theme'; +const width = '8px'; + const Container = styled('div')` display: flex; flex-direction: column; @@ -23,65 +25,26 @@ const StyledPlus = styled(PlusOutlined)` const StyledSlider = styled(Slider)` height: 150px; opacity: ${props => props.opacity}; - margin: 6px 10px; + margin: 6px 0; left: -1px; + + .ant-slider-handle::after { + display: none; + } + .ant-slider-mark-text { color: #ffffff; } + .ant-slider-dot { height: 1px; background: ${props => props.dotColor}; border: none; - width: 7px; + width: ${width}; left: 0; opacity: 50%; } - .ant-slider-rail, - .ant-slider-track, - .ant-slider-step { - width: 7px; - background: ${props => props.railBackground}; - border-radius: 5px; - box-shadow: 0px 1px 2px 1px rgb(0 0 0 / 60%); - &:hover { - background: ${props => props.railBackground}; - } - &:focus { - background: ${props => props.railBackground}; - } - &:active { - background: ${props => props.railBackground}; - } - } - .ant-slider-handle { - background: ${props => props.handleBackground}; - border: 4px solid ${props => props.handleBorder}; - box-shadow: 0px 1px 2px 1px rgb(0 0 0 / 60%); - border-radius: ${props => props.rounding || '0%'}; - width: 14px; - height: 14px; - left: 0; - &:hover { - background: ${props => props.handleBackground}; - border: 4px solid ${props => props.handleBorder}; - } - &:focus { - background: ${props => props.handleBackground}; - border: 4px solid ${props => props.handleBorder}; - } - &:active { - background: ${props => props.handleBackground}; - border: 4px solid ${props => props.handleBorder}; - } - } - .ant-slider-handle::after, .ant-slider-handle:hover::after { - background: none; - box-shadow: none !important; - } - .ant-slider:hover .ant-slider-handle::after { - box-shadow: none!important; - } - `; +`; const MobileContainer = styled('div')` display: flex; @@ -89,20 +52,29 @@ const MobileContainer = styled('div')` align-items: flex-end; `; -const ThemedSlider = ThemeConsumer(({theme = {}, ...rest}) => { +const ThemedSlider = ThemeConsumer(({ theme = {}, ...rest }) => { const helper = getNavigationTheme(theme); - const bgColor = helper.getButtonColor(); - const icon = helper.getTextColor(); - const rounding = helper.getButtonRoundness(); - const opacity = helper.getButtonOpacity(); + + const backgroundColor = helper.getButtonColor(); + const boxShadow = '0px 1px 2px 1px rgb(0 0 0 / 60%)'; + const slider = { width, backgroundColor, boxShadow, borderRadius: '5px' }; + const styles = { + track: slider, + rail: slider, + handle: { + width: '14px', + height: '14px', + backgroundColor, + boxShadow, + border: `4px solid ${helper.getTextColor()}`, + borderRadius: helper.getButtonRoundness() || '0%' + } + }; return ( <StyledSlider - railBackground={bgColor} - handleBackground={bgColor} - dotColor={icon} - rounding={rounding} - handleBorder={icon} - opacity={opacity} + styles={styles} + dotColor={helper.getTextColor()} + opacity={helper.getButtonOpacity()} {...rest} /> ); From da896aadb85042a7c8ff36387accf18279116e73 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 2 Nov 2024 10:44:14 +0200 Subject: [PATCH 072/181] fix vertical dot style --- src/react/components/Slider.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/react/components/Slider.jsx b/src/react/components/Slider.jsx index c7bab71c0b..827a5fc214 100644 --- a/src/react/components/Slider.jsx +++ b/src/react/components/Slider.jsx @@ -40,9 +40,9 @@ const ThemedAntSlider = styled(AntSlider)` .ant-slider-dot { background: #3c3c3c; border: none; - width: 2px; top: 0px; - height: ${props => props.useThick ? thick : normal}px; + ${props => props.vertical ? 'height': 'width'}: 2px; + ${props => props.vertical ? 'width': 'height'}: ${props => props.useThick ? thick : normal}px; } &:hover .ant-slider-track, &:hover .ant-slider-handle { From 3928b6d6d17592b644981bf3eddde77e5215398d Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 2 Nov 2024 11:43:17 +0200 Subject: [PATCH 073/181] remove useEffect --- .../TimeSeries/TimeSeriesSlider.jsx | 49 ++++++------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/src/react/components/TimeSeries/TimeSeriesSlider.jsx b/src/react/components/TimeSeries/TimeSeriesSlider.jsx index 3b855e4d98..e7de0a8ba7 100644 --- a/src/react/components/TimeSeries/TimeSeriesSlider.jsx +++ b/src/react/components/TimeSeries/TimeSeriesSlider.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, Fragment } from 'react'; +import React, { useState, Fragment } from 'react'; import PropTypes from 'prop-types'; import { sliderTypes, timeUnits } from './util/constants'; import { getDifferenceCalculator, calculateSvgX } from './util/calculation'; @@ -85,49 +85,28 @@ export const TimeSeriesSlider = ThemeConsumer(({ const widthUnit = lineWidth / calculator(max, min); const [state, setState] = useState({ - sliderPoints: [], - handleX: range ? calcHandlePosition(value[0], min, widthUnit, HANDLE_WIDTH, timeUnits.YEAR) : calcHandlePosition(value, min, widthUnit, 8, timeUnits.YEAR), - secondHandleX: range ? calcHandlePosition(value[1], min, widthUnit, HANDLE_WIDTH, timeUnits.YEAR) : 0, dragElement: null, dragOffsetX: null }); + const xValue = range ? value[0] : value; + const handleX = calcHandlePosition(xValue, min, widthUnit, HANDLE_WIDTH, timeUnits.YEAR); + const secondHandleX = range ? calcHandlePosition(value[1], min, widthUnit, HANDLE_WIDTH, timeUnits.YEAR) : 0; - useEffect(() => { - if (range) { - setState({ - ...state, - handleX: calcHandlePosition(value[0], min, widthUnit, HANDLE_WIDTH, timeUnits.YEAR), - secondHandleX: calcHandlePosition(value[1], min, widthUnit, HANDLE_WIDTH, timeUnits.YEAR) - }); - } else { - setState({ - ...state, - handleX: calcHandlePosition(value, min, widthUnit, HANDLE_WIDTH, timeUnits.YEAR) - }); - } - }, [value, value[0], value[1]]); - - useEffect(() => { - let points = dataPoints.map((data) => ({ - data, - x: calcDataPointX(data, widthUnit, min, (POINT_RADIUS * 2), calculator) - })); - setState({ - ...state, - sliderPoints: points - }); - }, [dataPoints]); + const sliderPoints = dataPoints.map((data) => ({ + data, + x: calcDataPointX(data, widthUnit, min, (POINT_RADIUS * 2), calculator) + })); const onHandlePositionChange = (e, target) => { const svgX = calculateSvgX(e.clientX, target); - const snap = findSnapPoint(svgX, state.sliderPoints); + const snap = findSnapPoint(svgX, sliderPoints); target.setAttributeNS(null, 'x', snap.x - state.dragOffsetX); handleChange(snap.data, target.id); }; const onRailClick = (e) => { const svgX = calculateSvgX(e.clientX, e.target); - const snap = findSnapPoint(svgX, state.sliderPoints); + const snap = findSnapPoint(svgX, sliderPoints); handleChange(snap.data); }; @@ -222,7 +201,7 @@ export const TimeSeriesSlider = ThemeConsumer(({ <g onClick={(e) => onRailClick(e)}> <Rail className='slider-rail' width={lineWidth} height={3} $theme={navigationTheme} /> {range && ( - <ActiveRail x1={state.handleX} x2={state.secondHandleX} y1={1.5} y2={1.5} stroke={navigationTheme.getButtonHoverColor()} strokeWidth={3} /> + <ActiveRail x1={handleX} x2={secondHandleX} y1={1.5} y2={1.5} stroke={navigationTheme.getButtonHoverColor()} strokeWidth={3} /> )} {markers.map((mark, index) => { return ( @@ -240,7 +219,7 @@ export const TimeSeriesSlider = ThemeConsumer(({ ) })} </g> - {state.sliderPoints.map((point, index) => ( + {sliderPoints.map((point, index) => ( <g key={index} className='slider-data-point' @@ -276,7 +255,7 @@ export const TimeSeriesSlider = ThemeConsumer(({ strokeWidth={1} width={HANDLE_WIDTH} height={HANDLE_WIDTH * 2} - x={state.handleX} + x={handleX} y={-7} onMouseDown={(e) => startDrag(e)} $theme={navigationTheme} @@ -290,7 +269,7 @@ export const TimeSeriesSlider = ThemeConsumer(({ strokeWidth={1} width={HANDLE_WIDTH} height={HANDLE_WIDTH * 2} - x={state.secondHandleX} + x={secondHandleX} y={-7} onMouseDown={(e) => startDrag(e)} $theme={navigationTheme} From a720f1eb5782561c6a607bd9556325de8aab3567 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 4 Nov 2024 11:18:36 +0200 Subject: [PATCH 074/181] move maplayerlist - tool under mapmodule --- .../publisher2/handler/MapLayersHandler.js | 3 +- .../publisher/layers}/MapLayerListHandler.js | 1 + .../publisher/layers}/MapLayerListTool.js | 19 +++++++++---- .../layers}/MapLayerListToolComponent.jsx | 28 +++++++++++++++---- bundles/mapping/mapmodule/publisher/tools.js | 3 +- .../framework/bundle/publisher2/bundle.js | 4 --- 6 files changed, 39 insertions(+), 19 deletions(-) rename bundles/{framework/publisher2/handler => mapping/mapmodule/publisher/layers}/MapLayerListHandler.js (99%) rename bundles/{framework/publisher2/tools => mapping/mapmodule/publisher/layers}/MapLayerListTool.js (77%) rename bundles/{framework/publisher2/view/MapLayers => mapping/mapmodule/publisher/layers}/MapLayerListToolComponent.jsx (57%) diff --git a/bundles/framework/publisher2/handler/MapLayersHandler.js b/bundles/framework/publisher2/handler/MapLayersHandler.js index 8bbf70f755..31ab91eba0 100644 --- a/bundles/framework/publisher2/handler/MapLayersHandler.js +++ b/bundles/framework/publisher2/handler/MapLayersHandler.js @@ -1,7 +1,6 @@ import { controllerMixin } from 'oskari-ui/util'; import { ToolPanelHandler } from './ToolPanelHandler'; -import { LAYERLIST_ID } from '../tools/MapLayerListTool'; - +import { LAYERLIST_ID } from '../../../mapping/mapmodule/publisher/layers/MapLayerListTool'; class UIHandler extends ToolPanelHandler { constructor (tools, sandbox, consumer) { // ToolPanelHandler adds tools to state so we can reference it here diff --git a/bundles/framework/publisher2/handler/MapLayerListHandler.js b/bundles/mapping/mapmodule/publisher/layers/MapLayerListHandler.js similarity index 99% rename from bundles/framework/publisher2/handler/MapLayerListHandler.js rename to bundles/mapping/mapmodule/publisher/layers/MapLayerListHandler.js index 9a4fb5fb9f..f566558591 100644 --- a/bundles/framework/publisher2/handler/MapLayerListHandler.js +++ b/bundles/mapping/mapmodule/publisher/layers/MapLayerListHandler.js @@ -18,6 +18,7 @@ class UIHandler extends StateHandler { isDisabledStyleChange: !layersHaveMultipleStyles(layers) }); }; + init (pluginConfig) { this.updateState({ ...pluginConfig diff --git a/bundles/framework/publisher2/tools/MapLayerListTool.js b/bundles/mapping/mapmodule/publisher/layers/MapLayerListTool.js similarity index 77% rename from bundles/framework/publisher2/tools/MapLayerListTool.js rename to bundles/mapping/mapmodule/publisher/layers/MapLayerListTool.js index 6e4eaf0cb3..ac01a1f008 100644 --- a/bundles/framework/publisher2/tools/MapLayerListTool.js +++ b/bundles/mapping/mapmodule/publisher/layers/MapLayerListTool.js @@ -1,6 +1,6 @@ -import { AbstractPublisherTool } from './AbstractPublisherTool'; -import { MapLayerListToolComponent } from '../view/MapLayers/MapLayerListToolComponent'; -import { MapLayerListHandler } from '../handler/MapLayerListHandler'; +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; +import { MapLayerListToolComponent } from './MapLayerListToolComponent'; +import { MapLayerListHandler } from './MapLayerListHandler'; export const LAYERLIST_ID = 'Oskari.mapframework.bundle.mapmodule.plugin.LayerSelectionPlugin'; @@ -8,27 +8,31 @@ class MapLayerListTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 5; - this.group = 'layers'; + this.group = 'additional'; this.handler = new MapLayerListHandler(this); } + getTool () { return { id: LAYERLIST_ID, - title: Oskari.getMsg('Publisher2', 'BasicView.layerselection.label'), + title: Oskari.getMsg('MapModule', 'publisherTools.LayerSelection.toolLabel'), config: this.state.pluginConfig || {} }; } + getComponent () { return { component: MapLayerListToolComponent, handler: this.handler }; } + init (data) { super.init(data); // restore state to handler -> passing init data to it this.handler.init(this.getTool().config); } + getValues () { if (!this.isEnabled()) { return null; @@ -49,6 +53,7 @@ class MapLayerListTool extends AbstractPublisherTool { } return value; } + stop () { super.stop(); this.handler.clearState(); @@ -59,6 +64,8 @@ class MapLayerListTool extends AbstractPublisherTool { Oskari.clazz.defineES('Oskari.publisher.MapLayerListTool', MapLayerListTool, { - 'protocol': ['Oskari.mapframework.publisher.LayerTool'] + protocol: ['Oskari.mapframework.publisher.LayerTool'] } ); + +export { MapLayerListTool }; diff --git a/bundles/framework/publisher2/view/MapLayers/MapLayerListToolComponent.jsx b/bundles/mapping/mapmodule/publisher/layers/MapLayerListToolComponent.jsx similarity index 57% rename from bundles/framework/publisher2/view/MapLayers/MapLayerListToolComponent.jsx rename to bundles/mapping/mapmodule/publisher/layers/MapLayerListToolComponent.jsx index b029013122..3cebabdf7e 100644 --- a/bundles/framework/publisher2/view/MapLayers/MapLayerListToolComponent.jsx +++ b/bundles/mapping/mapmodule/publisher/layers/MapLayerListToolComponent.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { Message, Checkbox, Tooltip } from 'oskari-ui'; import styled from 'styled-components'; +import PropTypes from 'prop-types'; const ExtraOptions = styled('div')` display:flex; @@ -18,8 +19,8 @@ export const MapLayerListToolComponent = ({ state, controller }) => { const StyleSelect = ({ state, controller }) => { if (state.isDisabledStyleChange) { return ( - <Tooltip title={<Message messageKey='BasicView.maptools.layerselection.noMultipleStyles' />}> - <Checkbox className='t_allow_style_select' disabled><Message messageKey='BasicView.maptools.layerselection.allowStyleChange' /></Checkbox> + <Tooltip title={<Message bundleKey='MapModule' messageKey='publisherTools.LayerSelection.noMultipleStyles' />}> + <Checkbox className='t_allow_style_select' disabled><Message bundleKey='MapModule' messageKey='publisherTools.LayerSelection.allowStyleChange' /></Checkbox> </Tooltip>); } return ( @@ -28,15 +29,15 @@ const StyleSelect = ({ state, controller }) => { checked={state.isStyleSelectable} onChange={(e) => controller.setAllowStyleChange(e.target.checked)} > - <Message messageKey='BasicView.maptools.layerselection.allowStyleChange' /> + <Message bundleKey='MapModule' messageKey='publisherTools.LayerSelection.allowStyleChange' /> </Checkbox>); }; const MetadataSelect = ({ state, controller }) => { if (state.isDisabledMetadata) { return ( - <Tooltip title={<Message messageKey='BasicView.maptools.layerselection.noMetadata' />}> - <Checkbox className='t_show_metalinks' disabled><Message messageKey='BasicView.maptools.layerselection.showMetadata' /></Checkbox> + <Tooltip title={<Message bundleKey='MapModule' messageKey='publisherTools.LayerSelection.noMetadata' />}> + <Checkbox className='t_show_metalinks' disabled><Message bundleKey='MapModule' messageKey='publisherTools.LayerSelection.showMetadata' /></Checkbox> </Tooltip>); } return ( @@ -45,6 +46,21 @@ const MetadataSelect = ({ state, controller }) => { checked={state.showMetadata} onChange={(e) => controller.setShowMetadata(e.target.checked)} > - <Message messageKey='BasicView.maptools.layerselection.showMetadata' /> + <Message bundleKey='MapModule' messageKey='publisherTools.LayerSelection.showMetadata' /> </Checkbox>); }; + +MapLayerListToolComponent.propTypes = { + state: PropTypes.object, + controller: PropTypes.object +}; + +StyleSelect.propTypes = { + state: PropTypes.object, + controller: PropTypes.object +}; + +MetadataSelect.propTypes = { + state: PropTypes.object, + controller: PropTypes.object +}; diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index 6637da9c99..02c8d0b00c 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -8,4 +8,5 @@ import { PanButtonsTool } from './panbuttons/PanButtonsTool'; import { IndexMapTool } from './indexmap/IndexMapTool'; import { ZoombarTool } from './zoombar/ZoombarTool'; import { CrosshairTool } from './crosshair/CrosshairTool'; -export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool, CrosshairTool }; +import { MapLayerListTool } from './layers/MapLayerListTool'; +export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool, CrosshairTool, MapLayerListTool }; diff --git a/packages/framework/bundle/publisher2/bundle.js b/packages/framework/bundle/publisher2/bundle.js index dbdacc0342..9affbc31d7 100755 --- a/packages/framework/bundle/publisher2/bundle.js +++ b/packages/framework/bundle/publisher2/bundle.js @@ -101,10 +101,6 @@ Oskari.clazz.define("Oskari.mapframework.bundle.publisher2.PublisherBundle", fun "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/SearchTool.js" }, - { - "type": "text/javascript", - "src": "../../../../bundles/framework/publisher2/tools/MapLayerListTool.js" - }, { "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/ControlsTool.js" From c70520cb817e43b1e3d7ef0a4263b6aaa0867871 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 4 Nov 2024 11:19:05 +0200 Subject: [PATCH 075/181] move maplayerlist - tool under mapmodule: localisations --- bundles/framework/publisher2/resources/locale/en.js | 5 ----- bundles/framework/publisher2/resources/locale/fi.js | 5 ----- bundles/framework/publisher2/resources/locale/sv.js | 5 ----- bundles/mapping/mapmodule/resources/locale/en.js | 8 ++++++++ bundles/mapping/mapmodule/resources/locale/fi.js | 8 ++++++++ bundles/mapping/mapmodule/resources/locale/sv.js | 9 +++++++++ 6 files changed, 25 insertions(+), 15 deletions(-) diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 2a2949c322..4bd7bd0ff4 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -100,12 +100,7 @@ Oskari.registerLocalization( "label": "My map layers" }, "layerselection": { - "info": "Select the background map layer. You can select the default background map layer in the map preview.", "selectAsBaselayer": "Select as baselayer", - "allowStyleChange": "Allow presentation style change", - "showMetadata": "Show metadata links", - "noMultipleStyles": "Only single presentation style available on the selected map layers.", - "noMetadata": "No metadata links availabe on the selected map layers" }, "mylocation": { "modes": { diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 61f712b092..ae5205898e 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -100,12 +100,7 @@ Oskari.registerLocalization( "label": "Omat karttatasot" }, "layerselection": { - "info": "Valitse taustakarttoina näytettävät karttatasot. Oletusvalinnan voit tehdä esikatselukartassa.", "selectAsBaselayer": "Valitse taustakartaksi", - "allowStyleChange": "Salli esitystyylin valinta", - "showMetadata": "Näytä metatietolinkit", - "noMultipleStyles": "Vain yksi esitystyyli saatavilla valituilla karttatasoilla.", - "noMetadata": "Metatietolinkkejä ei saatavilla valituilla karttatasoilla" }, "mylocation": { "modes": { diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 3155fe13da..71ff72647a 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -100,12 +100,7 @@ Oskari.registerLocalization( "label": "Mina kartlager" }, "layerselection": { - "info": "Välj bakgrundskartlager. Du kan göra förval i förhandsgranskningsvyn.", "selectAsBaselayer": "Välj bakgrundskartlager", - "allowStyleChange": "Tillåt val av visningsstil", - "showMetadata": "Visa länkar för metadata", - "noMultipleStyles": "Endast en visningsstil är tillgänglig för valda kartlager.", - "noMetadata": "Metadatalänkar är inte tillgängliga på valda kartlager" }, "mylocation": { "modes": { diff --git a/bundles/mapping/mapmodule/resources/locale/en.js b/bundles/mapping/mapmodule/resources/locale/en.js index 8f0bbaf18c..ac111b6ac9 100755 --- a/bundles/mapping/mapmodule/resources/locale/en.js +++ b/bundles/mapping/mapmodule/resources/locale/en.js @@ -186,6 +186,14 @@ Oskari.registerLocalization( }, "CrosshairTool": { "toolLabel": "Show map focal point" + }, + "LayerSelection": { + "toolLabel": "Map layers menu", + "selectAsBaselayer": "Select as baselayer", + "allowStyleChange": "Allow presentation style change", + "showMetadata": "Show metadata links", + "noMultipleStyles": "Only single presentation style available on the selected map layers.", + "noMetadata": "No metadata links availabe on the selected map layers" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/fi.js b/bundles/mapping/mapmodule/resources/locale/fi.js index f4e3091c09..b31c9e3b43 100755 --- a/bundles/mapping/mapmodule/resources/locale/fi.js +++ b/bundles/mapping/mapmodule/resources/locale/fi.js @@ -186,6 +186,14 @@ Oskari.registerLocalization( }, "CrosshairTool": { "toolLabel": "Näytä kartan keskipiste" + }, + "LayerSelection": { + "toolLabel": "Karttatasovalikko", + "noMultipleStyles": "Vain yksi esitystyyli saatavilla valituilla karttatasoilla.", + "allowStyleChange": "Salli esitystyylin valinta", + "noMetadata": "Metatietolinkkejä ei saatavilla valituilla karttatasoilla", + "showMetadata": "Näytä metatietolinkit", + "selectAsBaselayer": "Valitse taustakartaksi", } } } diff --git a/bundles/mapping/mapmodule/resources/locale/sv.js b/bundles/mapping/mapmodule/resources/locale/sv.js index 5af3b0f801..ed4b8d187e 100755 --- a/bundles/mapping/mapmodule/resources/locale/sv.js +++ b/bundles/mapping/mapmodule/resources/locale/sv.js @@ -181,7 +181,16 @@ Oskari.registerLocalization( }, "CrosshairTool": { "toolLabel": "Visa kartans mittpunkt" + }, + "LayerSelection": { + "toolLabel": "Kartlagermeny", + "selectAsBaselayer": "Välj bakgrundskartlager", + "allowStyleChange": "Tillåt val av visningsstil", + "showMetadata": "Visa länkar för metadata", + "noMultipleStyles": "Endast en visningsstil är tillgänglig för valda kartlager.", + "noMetadata": "Metadatalänkar är inte tillgängliga på valda kartlager" } + } } }); \ No newline at end of file From cc7772c22c2eea6e2e2274dd70686b97bfc08472 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 4 Nov 2024 13:52:30 +0200 Subject: [PATCH 076/181] move SearchTool under mapmodule --- .../mapmodule/publisher/search/SearchTool.js | 41 +++++++++++++++++++ bundles/mapping/mapmodule/publisher/tools.js | 4 +- .../framework/bundle/publisher2/bundle.js | 4 -- 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 bundles/mapping/mapmodule/publisher/search/SearchTool.js diff --git a/bundles/mapping/mapmodule/publisher/search/SearchTool.js b/bundles/mapping/mapmodule/publisher/search/SearchTool.js new file mode 100644 index 0000000000..89db673e58 --- /dev/null +++ b/bundles/mapping/mapmodule/publisher/search/SearchTool.js @@ -0,0 +1,41 @@ +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; +class SearchTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + } + + getTool () { + return { + id: 'Oskari.mapframework.bundle.mapmodule.plugin.SearchPlugin', + title: Oskari.getMsg('MapModule', 'publisherTools.SearchPlugin.toolLabel'), + config: this.state?.pluginConfig || {} + }; + } + + getValues () { + if (!this.isEnabled()) { + return null; + } + return { + configuration: { + mapfull: { + conf: { + plugins: [{ id: this.getTool().id, config: this.getPlugin().getConfig() }] + } + } + } + }; + } +}; + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.SearchTool', + SearchTool, + { + protocol: ['Oskari.mapframework.publisher.Tool'] + } +); + +export { SearchTool }; diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index 02c8d0b00c..0a157a5c26 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -9,4 +9,6 @@ import { IndexMapTool } from './indexmap/IndexMapTool'; import { ZoombarTool } from './zoombar/ZoombarTool'; import { CrosshairTool } from './crosshair/CrosshairTool'; import { MapLayerListTool } from './layers/MapLayerListTool'; -export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool, CrosshairTool, MapLayerListTool }; +import { SearchTool } from './search/SearchTool'; + +export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool, CrosshairTool, MapLayerListTool, SearchTool }; diff --git a/packages/framework/bundle/publisher2/bundle.js b/packages/framework/bundle/publisher2/bundle.js index 9affbc31d7..238c20a081 100755 --- a/packages/framework/bundle/publisher2/bundle.js +++ b/packages/framework/bundle/publisher2/bundle.js @@ -97,10 +97,6 @@ Oskari.clazz.define("Oskari.mapframework.bundle.publisher2.PublisherBundle", fun "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/ToolbarTool.js" }, - { - "type": "text/javascript", - "src": "../../../../bundles/framework/publisher2/tools/SearchTool.js" - }, { "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/ControlsTool.js" From 9d4bd81a1b0a808bd2aae35b8aa75e66948990bf Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 4 Nov 2024 14:13:57 +0200 Subject: [PATCH 077/181] searchtool localisations --- bundles/framework/publisher2/resources/locale/en.js | 1 - bundles/framework/publisher2/resources/locale/fi.js | 1 - bundles/framework/publisher2/resources/locale/fr.js | 1 - bundles/framework/publisher2/resources/locale/is.js | 1 - bundles/framework/publisher2/resources/locale/ru.js | 1 - bundles/framework/publisher2/resources/locale/sv.js | 1 - bundles/mapping/mapmodule/resources/locale/en.js | 5 ++++- bundles/mapping/mapmodule/resources/locale/fi.js | 5 ++++- bundles/mapping/mapmodule/resources/locale/fr.js | 5 ++++- bundles/mapping/mapmodule/resources/locale/is.js | 5 ++++- bundles/mapping/mapmodule/resources/locale/ru.js | 5 ++++- bundles/mapping/mapmodule/resources/locale/sv.js | 6 ++++-- 12 files changed, 24 insertions(+), 13 deletions(-) diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 4bd7bd0ff4..47343932e5 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -67,7 +67,6 @@ Oskari.registerLocalization( "AnnouncementsPlugin": "Announcements", "TimeseriesControlPlugin": "Time series player", "ControlsPlugin": "Pan by mouse", - "SearchPlugin": "Place search", "FeaturedataPlugin": "Feature data", "GetInfoPlugin": "Feature query tool", "PublisherToolbarPlugin": "Map tools", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index ae5205898e..0a7b7b5ccf 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -67,7 +67,6 @@ Oskari.registerLocalization( "AnnouncementsPlugin": "Ilmoitukset", "TimeseriesControlPlugin": "Aikasarjatoistin", "ControlsPlugin": "Kartan liikuttaminen hiirellä raahaamalla", - "SearchPlugin": "Osoite- ja paikannimihaku", "FeaturedataPlugin": "Kohdetietotaulukko", "GetInfoPlugin": "Kohdetietojen kyselytyökalu", "PublisherToolbarPlugin": "Karttatyökalut", diff --git a/bundles/framework/publisher2/resources/locale/fr.js b/bundles/framework/publisher2/resources/locale/fr.js index d264324612..45dc5bf3bc 100644 --- a/bundles/framework/publisher2/resources/locale/fr.js +++ b/bundles/framework/publisher2/resources/locale/fr.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "tooltip": "Sélectionner les outils de carte accessibles. Consulter une mise en place dans la prévisualisation de carte.", "TimeseriesControlPlugin": "Lecteur chronologique", "ControlsPlugin": "Panoramiser avec la souris", - "SearchPlugin": "Recherche de lieu", "FeaturedataPlugin": "Données de fonctionnalité", "GetInfoPlugin": "Outil d'interrogation de fonctionnalité", "PublisherToolbarPlugin": "Outils cartographiques", diff --git a/bundles/framework/publisher2/resources/locale/is.js b/bundles/framework/publisher2/resources/locale/is.js index f29bc339b1..4970bf2dbc 100755 --- a/bundles/framework/publisher2/resources/locale/is.js +++ b/bundles/framework/publisher2/resources/locale/is.js @@ -51,7 +51,6 @@ Oskari.registerLocalization( "label": "Tól", "tooltip": "Velja tiltæk kortatól. Athugaðu staðsetningu við forskoðun korts.", "ControlsPlugin": "Hliðra með mús", - "SearchPlugin": "Leit eftir stað", "FeaturedataPlugin": "Fitjugögn", "GetInfoPlugin": "Fyrirspurnatól fyrir fitjur", "PublisherToolbarPlugin": "Kortatól", diff --git a/bundles/framework/publisher2/resources/locale/ru.js b/bundles/framework/publisher2/resources/locale/ru.js index 0c17ff66c9..9b1f590868 100644 --- a/bundles/framework/publisher2/resources/locale/ru.js +++ b/bundles/framework/publisher2/resources/locale/ru.js @@ -65,7 +65,6 @@ Oskari.registerLocalization( "tooltip": "Выберите доступные инструменты карты. Проверка размещения в окне предварительного просмотра карты.", "TimeseriesControlPlugin": "Временные ряды", "ControlsPlugin": "Панорамирование мышью", - "SearchPlugin": "Поиск места", "FeaturedataPlugin": "Данные объекта", "GetInfoPlugin": "Инструмент запроса объектов", "PublisherToolbarPlugin": "Инструмент карты", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 71ff72647a..ee315c1037 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -67,7 +67,6 @@ Oskari.registerLocalization( "AnnouncementsPlugin": "Aviseringar", "TimeseriesControlPlugin": "Tidseriespelare", "ControlsPlugin": "Flytta kartvyn med musen", - "SearchPlugin": "Adress- och ortnamnssökning", "FeaturedataPlugin": "Objektuppgifter", "GetInfoPlugin": "Frågverktyg för visande av objektuppgifter", "PublisherToolbarPlugin": "Kartverktyg", diff --git a/bundles/mapping/mapmodule/resources/locale/en.js b/bundles/mapping/mapmodule/resources/locale/en.js index ac111b6ac9..cc305e8dc9 100755 --- a/bundles/mapping/mapmodule/resources/locale/en.js +++ b/bundles/mapping/mapmodule/resources/locale/en.js @@ -194,7 +194,10 @@ Oskari.registerLocalization( "showMetadata": "Show metadata links", "noMultipleStyles": "Only single presentation style available on the selected map layers.", "noMetadata": "No metadata links availabe on the selected map layers" - } + }, + "SearchPlugin": { + "toolLabel": "Place search" + }, } } }); \ No newline at end of file diff --git a/bundles/mapping/mapmodule/resources/locale/fi.js b/bundles/mapping/mapmodule/resources/locale/fi.js index b31c9e3b43..e0d68bb6ae 100755 --- a/bundles/mapping/mapmodule/resources/locale/fi.js +++ b/bundles/mapping/mapmodule/resources/locale/fi.js @@ -194,7 +194,10 @@ Oskari.registerLocalization( "noMetadata": "Metatietolinkkejä ei saatavilla valituilla karttatasoilla", "showMetadata": "Näytä metatietolinkit", "selectAsBaselayer": "Valitse taustakartaksi", - } + }, + "SearchPlugin": { + "toolLabel": "Osoite- ja paikannimihaku" + }, } } }); \ No newline at end of file diff --git a/bundles/mapping/mapmodule/resources/locale/fr.js b/bundles/mapping/mapmodule/resources/locale/fr.js index 7aaaf20a79..5dc1f8ec28 100755 --- a/bundles/mapping/mapmodule/resources/locale/fr.js +++ b/bundles/mapping/mapmodule/resources/locale/fr.js @@ -154,7 +154,10 @@ Oskari.registerLocalization( }, "CrosshairTool": { "toolLabel": "Afficher le point d'intérêt de la carte" - } + }, + "SearchPlugin": { + "toolLabel": "Recherche de lieu" + }, } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/is.js b/bundles/mapping/mapmodule/resources/locale/is.js index e9f6a8a8c0..53b2b20fa5 100755 --- a/bundles/mapping/mapmodule/resources/locale/is.js +++ b/bundles/mapping/mapmodule/resources/locale/is.js @@ -148,7 +148,10 @@ Oskari.registerLocalization( }, "CrosshairTool": { "toolLabel": "Sýna aðalatriði korts" - } + }, + "SearchPlugin": { + "toolLabel": "Leit eftir stað" + }, } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/ru.js b/bundles/mapping/mapmodule/resources/locale/ru.js index 8ef6ec8ffd..8b9423c45f 100644 --- a/bundles/mapping/mapmodule/resources/locale/ru.js +++ b/bundles/mapping/mapmodule/resources/locale/ru.js @@ -149,7 +149,10 @@ Oskari.registerLocalization( }, "CrosshairTool": { "toolLabel": "Показать фокус карты" - } + }, + "SearchPlugin": { + "toolLabel": "Поиск места" + }, } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/sv.js b/bundles/mapping/mapmodule/resources/locale/sv.js index ed4b8d187e..5d183b9906 100755 --- a/bundles/mapping/mapmodule/resources/locale/sv.js +++ b/bundles/mapping/mapmodule/resources/locale/sv.js @@ -189,8 +189,10 @@ Oskari.registerLocalization( "showMetadata": "Visa länkar för metadata", "noMultipleStyles": "Endast en visningsstil är tillgänglig för valda kartlager.", "noMetadata": "Metadatalänkar är inte tillgängliga på valda kartlager" - } - + }, + "SearchPlugin": { + "toolLabel": "Adress- och ortnamnssökning" + }, } } }); \ No newline at end of file From 5409081c1464bfbf7784f4dad1097f0b71775ea6 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 4 Nov 2024 14:20:16 +0200 Subject: [PATCH 078/181] remove searchtool from under publisher --- .../framework/publisher2/tools/SearchTool.js | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100755 bundles/framework/publisher2/tools/SearchTool.js diff --git a/bundles/framework/publisher2/tools/SearchTool.js b/bundles/framework/publisher2/tools/SearchTool.js deleted file mode 100755 index 4ca4856d3c..0000000000 --- a/bundles/framework/publisher2/tools/SearchTool.js +++ /dev/null @@ -1,43 +0,0 @@ -Oskari.clazz.define('Oskari.mapframework.publisher.tool.SearchTool', - function () { - }, { - index: 4, - - /** - * Get tool object. - * @method getTool - * - * @returns {Object} tool description - */ - getTool: function () { - return { - id: 'Oskari.mapframework.bundle.mapmodule.plugin.SearchPlugin', - title: 'SearchPlugin', - config: this.state.pluginConfig || {} - }; - }, - /** - * Get values. - * @method getValues - * @public - * - * @returns {Object} tool value object - */ - getValues: function () { - if (!this.isEnabled()) { - return null; - } - return { - configuration: { - mapfull: { - conf: { - plugins: [{ id: this.getTool().id, config: this.getPlugin().getConfig() }] - } - } - } - }; - } - }, { - 'extend': ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], - 'protocol': ['Oskari.mapframework.publisher.Tool'] - }); From b246d6266428bc5272a964854dbb0c5dca8532ca Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 4 Nov 2024 15:13:29 +0200 Subject: [PATCH 079/181] controls tool from publisher to mapmodule --- .../publisher/controls}/ControlsTool.js | 30 ++++++++++++------- bundles/mapping/mapmodule/publisher/tools.js | 14 ++++++++- .../framework/bundle/publisher2/bundle.js | 4 --- 3 files changed, 32 insertions(+), 16 deletions(-) rename bundles/{framework/publisher2/tools => mapping/mapmodule/publisher/controls}/ControlsTool.js (82%) mode change 100755 => 100644 diff --git a/bundles/framework/publisher2/tools/ControlsTool.js b/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js old mode 100755 new mode 100644 similarity index 82% rename from bundles/framework/publisher2/tools/ControlsTool.js rename to bundles/mapping/mapmodule/publisher/controls/ControlsTool.js index 48f9d2ab80..484d194f1c --- a/bundles/framework/publisher2/tools/ControlsTool.js +++ b/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js @@ -1,21 +1,24 @@ -import { AbstractPublisherTool } from './AbstractPublisherTool'; - +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; class ControlsTool extends AbstractPublisherTool { - getIndex () { - return 5; + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; } + getTool () { return { id: 'Oskari.mapframework.mapmodule.ControlsPlugin', - title: 'ControlsPlugin', + title: Oskari.getMsg('MapModule', 'publisherTools.ControlsPlugin.toolLabel'), config: this.state.pluginConfig || {}, hasNoPlugin: true }; } + init (data) { const plugin = this.findPluginFromInitData(data); if (plugin) { - var hasConfig = typeof plugin.config === 'object'; + const hasConfig = typeof plugin?.config === 'object'; if (hasConfig) { this.storePluginConf(plugin.config); } @@ -23,18 +26,18 @@ class ControlsTool extends AbstractPublisherTool { this.setEnabled(!hasConfig || (hasConfig && plugin.config.keyboardControls !== false)); } } + // override since we want to use the instance we currently have, not create a new one setEnabled (enabled) { - const changed = super.setEnabled(enabled); - if (!changed) { - return; - } + super.setEnabled(enabled); this.allowPanning(!!enabled); } + getPlugin () { // always use the instance on map, not a new copy return this.getMapmodule().getPluginInstances('ControlsPlugin'); } + allowPanning (enabled) { if (!enabled) { this.getSandbox().postRequestByName('DisableMapKeyboardMovementRequest', []); @@ -44,6 +47,7 @@ class ControlsTool extends AbstractPublisherTool { this.getSandbox().postRequestByName('EnableMapMouseMovementRequest', []); } } + getValues () { return { configuration: { @@ -55,6 +59,7 @@ class ControlsTool extends AbstractPublisherTool { } }; } + getConfig () { // NOTE! returning null when isEnabled() is ON PURPOSE! // Usually this is reversed @@ -67,6 +72,7 @@ class ControlsTool extends AbstractPublisherTool { mouseControls: false }; } + stop () { super.stop(); // resume panning on map @@ -78,6 +84,8 @@ class ControlsTool extends AbstractPublisherTool { Oskari.clazz.defineES('Oskari.publisher.ControlsTool', ControlsTool, { - 'protocol': ['Oskari.mapframework.publisher.Tool'] + protocol: ['Oskari.mapframework.publisher.Tool'] } ); + +export { ControlsTool }; diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index 0a157a5c26..4c99d4ee05 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -10,5 +10,17 @@ import { ZoombarTool } from './zoombar/ZoombarTool'; import { CrosshairTool } from './crosshair/CrosshairTool'; import { MapLayerListTool } from './layers/MapLayerListTool'; import { SearchTool } from './search/SearchTool'; +import { ControlsTool } from './controls/ControlsTool'; -export { LogoTool, ScaleBarTool, MyLocationTool, PanButtonsTool, IndexMapTool, ZoombarTool, CrosshairTool, MapLayerListTool, SearchTool }; +export { + LogoTool, + ScaleBarTool, + MyLocationTool, + PanButtonsTool, + IndexMapTool, + ZoombarTool, + CrosshairTool, + MapLayerListTool, + SearchTool, + ControlsTool +}; diff --git a/packages/framework/bundle/publisher2/bundle.js b/packages/framework/bundle/publisher2/bundle.js index 238c20a081..418ef4a9f1 100755 --- a/packages/framework/bundle/publisher2/bundle.js +++ b/packages/framework/bundle/publisher2/bundle.js @@ -97,10 +97,6 @@ Oskari.clazz.define("Oskari.mapframework.bundle.publisher2.PublisherBundle", fun "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/ToolbarTool.js" }, - { - "type": "text/javascript", - "src": "../../../../bundles/framework/publisher2/tools/ControlsTool.js" - }, { "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/GetInfoTool.js" From 54c1189fbef1cd526c8d6f58d9a8cde0d485e0e6 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 4 Nov 2024 15:13:42 +0200 Subject: [PATCH 080/181] controls tool from publisher to mapmodule: localisations --- bundles/framework/publisher2/resources/locale/en.js | 1 - bundles/framework/publisher2/resources/locale/fi.js | 1 - bundles/framework/publisher2/resources/locale/fr.js | 1 - bundles/framework/publisher2/resources/locale/is.js | 1 - bundles/framework/publisher2/resources/locale/ru.js | 1 - bundles/framework/publisher2/resources/locale/sv.js | 1 - bundles/mapping/mapmodule/resources/locale/en.js | 3 +++ bundles/mapping/mapmodule/resources/locale/fi.js | 3 +++ bundles/mapping/mapmodule/resources/locale/fr.js | 3 +++ bundles/mapping/mapmodule/resources/locale/is.js | 3 +++ bundles/mapping/mapmodule/resources/locale/ru.js | 3 +++ bundles/mapping/mapmodule/resources/locale/sv.js | 3 +++ 12 files changed, 18 insertions(+), 6 deletions(-) diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 47343932e5..5c223868b6 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Select available map tools. Check a placement in the map preview.", "AnnouncementsPlugin": "Announcements", "TimeseriesControlPlugin": "Time series player", - "ControlsPlugin": "Pan by mouse", "FeaturedataPlugin": "Feature data", "GetInfoPlugin": "Feature query tool", "PublisherToolbarPlugin": "Map tools", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 0a7b7b5ccf..f8d499e86f 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Valitse kartalla käytettävissä olevat työkalut. Tarkista asettelu esikatselukartasta.", "AnnouncementsPlugin": "Ilmoitukset", "TimeseriesControlPlugin": "Aikasarjatoistin", - "ControlsPlugin": "Kartan liikuttaminen hiirellä raahaamalla", "FeaturedataPlugin": "Kohdetietotaulukko", "GetInfoPlugin": "Kohdetietojen kyselytyökalu", "PublisherToolbarPlugin": "Karttatyökalut", diff --git a/bundles/framework/publisher2/resources/locale/fr.js b/bundles/framework/publisher2/resources/locale/fr.js index 45dc5bf3bc..966c5cecec 100644 --- a/bundles/framework/publisher2/resources/locale/fr.js +++ b/bundles/framework/publisher2/resources/locale/fr.js @@ -63,7 +63,6 @@ Oskari.registerLocalization( "label": "Outils", "tooltip": "Sélectionner les outils de carte accessibles. Consulter une mise en place dans la prévisualisation de carte.", "TimeseriesControlPlugin": "Lecteur chronologique", - "ControlsPlugin": "Panoramiser avec la souris", "FeaturedataPlugin": "Données de fonctionnalité", "GetInfoPlugin": "Outil d'interrogation de fonctionnalité", "PublisherToolbarPlugin": "Outils cartographiques", diff --git a/bundles/framework/publisher2/resources/locale/is.js b/bundles/framework/publisher2/resources/locale/is.js index 4970bf2dbc..f993103606 100755 --- a/bundles/framework/publisher2/resources/locale/is.js +++ b/bundles/framework/publisher2/resources/locale/is.js @@ -50,7 +50,6 @@ Oskari.registerLocalization( "maptools": { "label": "Tól", "tooltip": "Velja tiltæk kortatól. Athugaðu staðsetningu við forskoðun korts.", - "ControlsPlugin": "Hliðra með mús", "FeaturedataPlugin": "Fitjugögn", "GetInfoPlugin": "Fyrirspurnatól fyrir fitjur", "PublisherToolbarPlugin": "Kortatól", diff --git a/bundles/framework/publisher2/resources/locale/ru.js b/bundles/framework/publisher2/resources/locale/ru.js index 9b1f590868..ab2109beec 100644 --- a/bundles/framework/publisher2/resources/locale/ru.js +++ b/bundles/framework/publisher2/resources/locale/ru.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "label": "Инструменты", "tooltip": "Выберите доступные инструменты карты. Проверка размещения в окне предварительного просмотра карты.", "TimeseriesControlPlugin": "Временные ряды", - "ControlsPlugin": "Панорамирование мышью", "FeaturedataPlugin": "Данные объекта", "GetInfoPlugin": "Инструмент запроса объектов", "PublisherToolbarPlugin": "Инструмент карты", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index ee315c1037..73b7396fb4 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.", "AnnouncementsPlugin": "Aviseringar", "TimeseriesControlPlugin": "Tidseriespelare", - "ControlsPlugin": "Flytta kartvyn med musen", "FeaturedataPlugin": "Objektuppgifter", "GetInfoPlugin": "Frågverktyg för visande av objektuppgifter", "PublisherToolbarPlugin": "Kartverktyg", diff --git a/bundles/mapping/mapmodule/resources/locale/en.js b/bundles/mapping/mapmodule/resources/locale/en.js index cc305e8dc9..7c40266c40 100755 --- a/bundles/mapping/mapmodule/resources/locale/en.js +++ b/bundles/mapping/mapmodule/resources/locale/en.js @@ -198,6 +198,9 @@ Oskari.registerLocalization( "SearchPlugin": { "toolLabel": "Place search" }, + "ControlsPlugin": { + "toolLabel": "Pan by mouse" + } } } }); \ No newline at end of file diff --git a/bundles/mapping/mapmodule/resources/locale/fi.js b/bundles/mapping/mapmodule/resources/locale/fi.js index e0d68bb6ae..0e319c388c 100755 --- a/bundles/mapping/mapmodule/resources/locale/fi.js +++ b/bundles/mapping/mapmodule/resources/locale/fi.js @@ -198,6 +198,9 @@ Oskari.registerLocalization( "SearchPlugin": { "toolLabel": "Osoite- ja paikannimihaku" }, + "ControlsPlugin": { + "toolLabel": "Kartan liikuttaminen hiirellä raahaamalla", + } } } }); \ No newline at end of file diff --git a/bundles/mapping/mapmodule/resources/locale/fr.js b/bundles/mapping/mapmodule/resources/locale/fr.js index 5dc1f8ec28..a59988b19c 100755 --- a/bundles/mapping/mapmodule/resources/locale/fr.js +++ b/bundles/mapping/mapmodule/resources/locale/fr.js @@ -158,6 +158,9 @@ Oskari.registerLocalization( "SearchPlugin": { "toolLabel": "Recherche de lieu" }, + "ControlsPlugin": { + "toolLabel": "Panoramiser avec la souris" + } } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/is.js b/bundles/mapping/mapmodule/resources/locale/is.js index 53b2b20fa5..8bd1294644 100755 --- a/bundles/mapping/mapmodule/resources/locale/is.js +++ b/bundles/mapping/mapmodule/resources/locale/is.js @@ -152,6 +152,9 @@ Oskari.registerLocalization( "SearchPlugin": { "toolLabel": "Leit eftir stað" }, + "ControlsPlugin": { + "toolLabel": "Hliðra með mús" + } } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/ru.js b/bundles/mapping/mapmodule/resources/locale/ru.js index 8b9423c45f..baf00222a7 100644 --- a/bundles/mapping/mapmodule/resources/locale/ru.js +++ b/bundles/mapping/mapmodule/resources/locale/ru.js @@ -153,6 +153,9 @@ Oskari.registerLocalization( "SearchPlugin": { "toolLabel": "Поиск места" }, + "ControlsPlugin": { + "toolLabel": "Панорамирование мышью" + } } } }); diff --git a/bundles/mapping/mapmodule/resources/locale/sv.js b/bundles/mapping/mapmodule/resources/locale/sv.js index 5d183b9906..0af54e6f1e 100755 --- a/bundles/mapping/mapmodule/resources/locale/sv.js +++ b/bundles/mapping/mapmodule/resources/locale/sv.js @@ -193,6 +193,9 @@ Oskari.registerLocalization( "SearchPlugin": { "toolLabel": "Adress- och ortnamnssökning" }, + "ControlsPlugin": { + "toolLabel": "Flytta kartvyn med musen" + } } } }); \ No newline at end of file From 657189d94f2a19749a57d34d298b65b3bd67d331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Wed, 6 Nov 2024 11:56:24 +0200 Subject: [PATCH 081/181] Make ESLint happier --- bundles/mapping/mapmodule/mapmodule.olcs.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bundles/mapping/mapmodule/mapmodule.olcs.js b/bundles/mapping/mapmodule/mapmodule.olcs.js index ef95ccfdaf..619f614880 100644 --- a/bundles/mapping/mapmodule/mapmodule.olcs.js +++ b/bundles/mapping/mapmodule/mapmodule.olcs.js @@ -62,12 +62,12 @@ class MapModuleOlCesium extends MapModuleOl { * @return {ol/Map} */ createMap () { - var me = this; + const me = this; olProjProj4.register(window.proj4); // this is done BEFORE enhancement writes the values to map domain // object... so we will move the map to correct location // by making a MapMoveRequest in application startup - var controls = olControlDefaults({ + const controls = olControlDefaults({ zoom: false, attribution: true, attributionOptions: { @@ -76,17 +76,17 @@ class MapModuleOlCesium extends MapModuleOl { rotate: false }); - var map = new OLMap({ + const map = new OLMap({ keyboardEventTarget: document, target: this.getMapDOMEl(), - controls: controls, + controls, interactions: me._olInteractionDefaults, moveTolerance: 2 }); - var projection = olProj.get(me.getProjection()); + const projection = olProj.get(me.getProjection()); map.setView(new OLView({ - projection: projection, + projection, // actual startup location is set with MapMoveRequest later on // still these need to be set to prevent errors center: [0, 0], @@ -96,7 +96,7 @@ class MapModuleOlCesium extends MapModuleOl { const creditContainer = document.createElement('div'); creditContainer.className = 'cesium-credit-container'; this._map3D = new OLCesium({ - map: map, + map, time: () => this.getJulianTime(), sceneOptions: { showCredit: true, @@ -109,7 +109,7 @@ class MapModuleOlCesium extends MapModuleOl { }); this._map3D.container_.appendChild(creditContainer); - var scene = this._map3D.getCesiumScene(); + const scene = this._map3D.getCesiumScene(); // Vector features sink in the ground. This allows sunken features to be visible through the ground. // Setting olcs property 'altitudeMode': 'clampToGround' to vector layer had some effect but wasn't good enough. // DepthTestAgainstTerrain should be enabled when 3D-tiles (buildings) are visible. @@ -131,7 +131,7 @@ class MapModuleOlCesium extends MapModuleOl { this._setupMapEvents(map); this._fixDuplicateOverlays(true); - var updateReadyStatus = function () { + const updateReadyStatus = function () { scene.postRender.removeEventListener(updateReadyStatus); me._mapReady = true; me._notifyMapReadySubscribers(); From a459b62379738ba19855602aa4fab81cca99b4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Wed, 6 Nov 2024 11:59:11 +0200 Subject: [PATCH 082/181] Use tileset.fromUrl() instead of constructor/cesium API change --- bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js b/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js index 3b34f1c114..65cbe94b86 100644 --- a/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js +++ b/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js @@ -186,18 +186,21 @@ Oskari.clazz.define('Oskari.mapframework.mapmodule.Tiles3DLayerPlugin', const url = ionAssetId ? Cesium.IonResource.fromAssetId(ionAssetId, { server: ionAssetServer, accessToken: ionAccessToken }) : layer.getLayerUrl(); + + this.__addTileset(layer, url, options); + }, + __addTileset: async function (layer, url, options = {}) { // Common settings for the dynamicScreenSpaceError optimization // copied from Cesium.Cesium3DTileset api doc: // https://cesium.com/docs/cesiumjs-ref-doc/Cesium3DTileset.html - var tileset = new Cesium.Cesium3DTileset({ - url, + // https://cesium.com/learn/cesiumjs/ref-doc/Cesium3DTileset.html + const tileset = await Cesium.Cesium3DTileset.fromUrl(url, { dynamicScreenSpaceError: true, dynamicScreenSpaceErrorDensity: 0.00278, dynamicScreenSpaceErrorFactor: 4.0, dynamicScreenSpaceErrorHeightFalloff: 0.25, ...options }); - this._disablePointCloudShadows(tileset); this._applyOskariStyle(tileset, layer); this.getMapModule().addLayer(tileset); From 910e71258e466b84b52992d3033b8f8e10d072d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Wed, 6 Nov 2024 12:10:30 +0200 Subject: [PATCH 083/181] Make ESLint happier --- bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js b/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js index 65cbe94b86..90e0072fd1 100644 --- a/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js +++ b/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js @@ -51,7 +51,7 @@ Oskari.clazz.define('Oskari.mapframework.mapmodule.Tiles3DLayerPlugin', */ _initImpl: function () { // register domain builder - var mapLayerService = this.getSandbox().getService('Oskari.mapframework.service.MapLayerService'); + const mapLayerService = this.getSandbox().getService('Oskari.mapframework.service.MapLayerService'); if (mapLayerService) { const registerType = this.layertype + 'layer'; const clazz = 'Oskari.mapframework.mapmodule.Tiles3DLayer'; @@ -144,7 +144,7 @@ Oskari.clazz.define('Oskari.mapframework.mapmodule.Tiles3DLayerPlugin', if (tableContent.length === 0) { return; } - let content = [{ html: `<table>${tableContent}</table>` }]; + const content = [{ html: `<table>${tableContent}</table>` }]; const location = me.getMapModule().getMouseLocation(movement.position); // Request info box const infoRequestBuilder = Oskari.requestBuilder('InfoBox.ShowInfoBoxRequest'); @@ -216,11 +216,11 @@ Oskari.clazz.define('Oskari.mapframework.mapmodule.Tiles3DLayerPlugin', // no-op - 3DTiles scale limits or runtime changes to config not supported at this time } }, { - 'extend': ['Oskari.mapping.mapmodule.AbstractMapLayerPlugin'], + extend: ['Oskari.mapping.mapmodule.AbstractMapLayerPlugin'], /** * @static @property {string[]} protocol array of superclasses */ - 'protocol': [ + protocol: [ 'Oskari.mapframework.module.Module', 'Oskari.mapframework.ui.module.common.mapmodule.Plugin' ] From cd27426ad2529bf943e9ba4a51033a80dc3f0b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Wed, 6 Nov 2024 14:58:55 +0200 Subject: [PATCH 084/181] Shovels in some cesium wasm and uses await for assetId URL --- .../tiles3d/plugin/Tiles3DLayerPlugin.js | 29 +++++++++++++------ webpack.config.js | 3 ++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js b/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js index 90e0072fd1..addfbb15d8 100644 --- a/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js +++ b/bundles/mapping/tiles3d/plugin/Tiles3DLayerPlugin.js @@ -180,16 +180,12 @@ Oskari.clazz.define('Oskari.mapframework.mapmodule.Tiles3DLayerPlugin', if (!this.getMapModule().getSupports3D()) { return; } - const options = layer.getOptions() || {}; - const { ionAssetId, ionAssetServer, ionAccessToken } = options; - - const url = ionAssetId - ? Cesium.IonResource.fromAssetId(ionAssetId, { server: ionAssetServer, accessToken: ionAccessToken }) - : layer.getLayerUrl(); - - this.__addTileset(layer, url, options); + // moved to own func because of async/await requirement + this.__addTileset(layer); }, - __addTileset: async function (layer, url, options = {}) { + __addTileset: async function (layer) { + const options = layer.getOptions() || {}; + const url = await this.__getURL(layer, options); // Common settings for the dynamicScreenSpaceError optimization // copied from Cesium.Cesium3DTileset api doc: // https://cesium.com/docs/cesiumjs-ref-doc/Cesium3DTileset.html @@ -208,6 +204,21 @@ Oskari.clazz.define('Oskari.mapframework.mapmodule.Tiles3DLayerPlugin', // store reference to layers this.setOLMapLayers(layer.getId(), tileset); }, + __getURL: async function (layer, options) { + const { ionAssetId, ionAssetServer, ionAccessToken } = options; + if (!ionAssetId) { + return layer.getLayerUrl(); + } + const ionResourceOpts = {}; + if (ionAssetServer) { + // check truthy, we might have empty string defined and Cesium only checks for null/undefined for defaulting. + ionResourceOpts.server = ionAssetServer; + } + if (ionAccessToken) { + ionResourceOpts.accessToken = ionAccessToken; + } + return Cesium.IonResource.fromAssetId(ionAssetId, ionResourceOpts); + }, /** * Called when layer details are updated (for example by the admin functionality) * @param {Oskari.mapframework.domain.AbstractLayer} layer new layer details diff --git a/webpack.config.js b/webpack.config.js index 11cbd5516e..8423716185 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -37,6 +37,9 @@ module.exports = (env, argv) => { .forEach(possibleSrcPath => { plugins.push(new CopywebpackPlugin([ { from: path.join(__dirname, possibleSrcPath, '../Source/Assets'), to: cesiumTarget + '/Assets' }, + // This is not available under Build folder: + // - oskari-frontend/node_modules/@cesium/engine/Source/ThirdParty/draco_decoder.wasm + { from: path.join(__dirname, possibleSrcPath, '../Source/ThirdParty'), to: cesiumTarget + '/ThirdParty' }, { from: path.join(__dirname, possibleSrcPath, 'Workers'), to: cesiumTarget + '/Workers' }, // { from: path.join(__dirname, possibleSrcPath, 'Widgets'), to: cesiumTarget + '/Widgets' }, // copy Cesium's minified third-party scripts From 983aeddb5c8b1c7e88e24b1a1e1dac54749c88af Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 7 Nov 2024 12:06:09 +0200 Subject: [PATCH 085/181] select default button on setActive --- bundles/mapping/layerswipe/instance.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/bundles/mapping/layerswipe/instance.js b/bundles/mapping/layerswipe/instance.js index e02fda1133..4bd8460fb4 100644 --- a/bundles/mapping/layerswipe/instance.js +++ b/bundles/mapping/layerswipe/instance.js @@ -28,13 +28,7 @@ Oskari.clazz.define( iconCls: 'tool-layer-swipe', tooltip: this.loc('toolLayerSwipe'), sticky: true, - callback: () => { - if (this.getState().active) { - this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); - } else { - this.setActive(true); - } - } + callback: () => this.setActive(!this.getState().active) }; sandbox.request(this, addToolButtonBuilder('LayerSwipe', 'basictools', buttonConf)); } @@ -58,6 +52,10 @@ Oskari.clazz.define( return this.handler?.getState() || {}; }, setActive: function (active) { + if (!this.plugin && !active) { + this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); + return; + } this.handler?.setActive(active); }, getStateParameters: function () { From 7b9a43e794430302a717e6a5d85f2123490a4773 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 7 Nov 2024 12:14:08 +0200 Subject: [PATCH 086/181] remove layerId on stop --- bundles/mapping/layerswipe/handler/SwipeHandler.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bundles/mapping/layerswipe/handler/SwipeHandler.js b/bundles/mapping/layerswipe/handler/SwipeHandler.js index 17ab7237b6..245212166b 100644 --- a/bundles/mapping/layerswipe/handler/SwipeHandler.js +++ b/bundles/mapping/layerswipe/handler/SwipeHandler.js @@ -93,6 +93,7 @@ class UIHandler extends StateHandler { if (this.getState().active === active) { return; } + let { layerId } = this.getState(); const mapModule = this.instance.getMapModule(); const root = mapModule.getMapDOMEl(); if (active) { @@ -106,10 +107,11 @@ class UIHandler extends StateHandler { unmountComponentAtNode(this.element); root.removeChild(this.element); this.element = null; + layerId = null; } mapModule.getMap().render(); this.instance.getSandbox().getService('Oskari.mapframework.service.VectorFeatureService')?.setHoverEnabled(!active); - this.updateState({ active }); + this.updateState({ active, layerId }); } toggleTool () { From d86d244fcdc149395ee77b29dd7eb06c88122cf8 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 7 Nov 2024 12:18:51 +0200 Subject: [PATCH 087/181] select default tool on stop --- bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js b/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js index fd028878f8..21da33463f 100644 --- a/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js +++ b/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js @@ -2,7 +2,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { SwipeIcon } from '../resources/icons/Swipe'; import { MapModuleButton } from '../../mapmodule/MapModuleButton'; -import { refresh } from 'less'; /** * @class Oskari.mapframework.bundle.mapmodule.plugin.LayerSelectionPlugin @@ -33,7 +32,7 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug return true; }, _stopPluginImpl: function () { - this.getInstance()?.handler.setActive(false); + this.getInstance()?.setActive(false); this.teardownUI(); }, setHandler: function (handler) { From d911554d343bc6f1fe1091b6323589822c9f38ab Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Thu, 7 Nov 2024 12:19:17 +0200 Subject: [PATCH 088/181] add props validation --- .../publisher/SwipeToolComponent.jsx | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/bundles/mapping/layerswipe/publisher/SwipeToolComponent.jsx b/bundles/mapping/layerswipe/publisher/SwipeToolComponent.jsx index 0bb5d19796..3aa2400f02 100644 --- a/bundles/mapping/layerswipe/publisher/SwipeToolComponent.jsx +++ b/bundles/mapping/layerswipe/publisher/SwipeToolComponent.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Message, Checkbox } from 'oskari-ui'; import styled from 'styled-components'; @@ -10,29 +11,23 @@ const ExtraOptions = styled('div')` export const SwipeToolComponent = ({ state, controller }) => { return ( <ExtraOptions> - <AutoStartSelect state={state} controller={controller} /> - <HideUISelect state={state} controller={controller} /> - </ExtraOptions>); + <Checkbox + className='t_swipe_auto_start' + checked={state.autoStart} + onChange={(e) => controller.setAutoStart(e.target.checked)}> + <Message bundleKey="LayerSwipe" messageKey='tool.autoStart' /> + </Checkbox> + <Checkbox + className='t_swipe_hide_plugin' + checked={state.noUI} + onChange={(e) => controller.setHideUI(e.target.checked)}> + <Message bundleKey="Publisher2" messageKey='BasicView.noUI' /> + </Checkbox> + </ExtraOptions> + ); }; -const AutoStartSelect = ({ state, controller }) => { - return ( - <Checkbox - className='t_swipe_auto_start' - checked={state.autoStart} - onChange={(e) => controller.setAutoStart(e.target.checked)} - > - <Message bundleKey="LayerSwipe" messageKey='tool.autoStart' /> - </Checkbox>); -}; - -const HideUISelect = ({ state, controller }) => { - return ( - <Checkbox - className='t_swipe_hide_plugin' - checked={state.noUI} - onChange={(e) => controller.setHideUI(e.target.checked)} - > - <Message bundleKey="Publisher2" messageKey='BasicView.noUI' /> - </Checkbox>); +SwipeToolComponent.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired }; From 28fd5f5547d145e88df129ace0b42715a4f0ee9f Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 09:27:05 +0200 Subject: [PATCH 089/181] remove tool handler --- .../layerswipe/handler/SwipeHandler.js | 11 +++- .../layerswipe/handler/SwipeToolHandler.js | 65 ------------------- 2 files changed, 9 insertions(+), 67 deletions(-) delete mode 100644 bundles/mapping/layerswipe/handler/SwipeToolHandler.js diff --git a/bundles/mapping/layerswipe/handler/SwipeHandler.js b/bundles/mapping/layerswipe/handler/SwipeHandler.js index 245212166b..8a50e51222 100644 --- a/bundles/mapping/layerswipe/handler/SwipeHandler.js +++ b/bundles/mapping/layerswipe/handler/SwipeHandler.js @@ -16,11 +16,13 @@ class UIHandler extends StateHandler { super(); this.instance = instance; const mapSize = this.getMapSize(); + const { noUI = false } = instance.conf || {}; this.setState({ active: false, layerId: null, position: (mapSize.width - SPLITTER_WIDTH) / 2, - mapSize + mapSize, + noUI }); this.alertPopupControls = null; this.element = null; @@ -119,6 +121,10 @@ class UIHandler extends StateHandler { this.setActive(!active); } + setHideUI (noUI) { + this.updateState({ noUI }); + } + render () { if (!this.element) { return; @@ -197,7 +203,8 @@ class UIHandler extends StateHandler { const wrapped = controllerMixin(UIHandler, [ 'setActive', 'setPosition', - 'toggleTool' + 'toggleTool', + 'setHideUI' ]); export { wrapped as SwipeHandler }; diff --git a/bundles/mapping/layerswipe/handler/SwipeToolHandler.js b/bundles/mapping/layerswipe/handler/SwipeToolHandler.js deleted file mode 100644 index e70d723f1e..0000000000 --- a/bundles/mapping/layerswipe/handler/SwipeToolHandler.js +++ /dev/null @@ -1,65 +0,0 @@ -import { StateHandler, controllerMixin } from 'oskari-ui/util'; - -class UIHandler extends StateHandler { - constructor (tool) { - super(); - this.tool = tool; - this.sandbox = tool.getSandbox(); - this.setState({ - autoStart: false, - noUI: false - }); - }; - - init (pluginConfig) { - this.updateState({ - ...pluginConfig.conf, - autoStart: pluginConfig?.state?.active, - noUI: pluginConfig?.conf?.noUI - }); - } - - syncState () { - // required to sync state to plugin on startup and restore the state of the embedded map - const { autoStart, noUI } = this.getState(); - this.setAutoStart(autoStart); - this.setHideUI(noUI); - } - - clearState () { - // plugin is created again on startup, so it's state doesn't need to be cleared - this.setState({ - autoStart: false, - noUI: false - }); - } - /* - onLayersChanged () { - // we could do setEnabled(false) here if Oskari.getSandbox().getMap().getLayers() is < 2 - } - */ - - setAutoStart (value) { - this.tool.getPlugin().setToolState(!!value); - this.updateConfig2State(); - } - - setHideUI (value) { - this.tool.getPlugin().setHideUI(value); - this.updateConfig2State(); - } - - updateConfig2State () { - this.updateState({ - autoStart: !!this.tool?.getPlugin()?.isActive(), - noUI: !this.tool?.getPlugin()?.hasUI() - }); - } -} - -const wrapped = controllerMixin(UIHandler, [ - 'setAutoStart', - 'setHideUI' -]); - -export { wrapped as SwipeToolhandler }; From d693ece246b7579c3387dfb845f7320ec03938a1 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 09:32:45 +0200 Subject: [PATCH 090/181] use SwipeHandler for publisher --- .../mapping/layerswipe/publisher/SwipeTool.js | 47 +++++++++---------- .../publisher/SwipeToolComponent.jsx | 4 +- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/bundles/mapping/layerswipe/publisher/SwipeTool.js b/bundles/mapping/layerswipe/publisher/SwipeTool.js index 83487c7d80..dfb73dd58e 100644 --- a/bundles/mapping/layerswipe/publisher/SwipeTool.js +++ b/bundles/mapping/layerswipe/publisher/SwipeTool.js @@ -1,5 +1,4 @@ import { AbstractPublisherTool } from '../../../framework/publisher2/tools/AbstractPublisherTool'; -import { SwipeToolhandler } from '../handler/SwipeToolHandler'; import { SwipeToolComponent } from './SwipeToolComponent'; export const SWIPE_ID = 'Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlugin'; @@ -9,7 +8,6 @@ class SwipeTool extends AbstractPublisherTool { super(...args); this.index = 5; this.group = 'additional'; - this.handler = new SwipeToolhandler(this); } getTool () { @@ -20,49 +18,50 @@ class SwipeTool extends AbstractPublisherTool { }; } + getHandler () { + return this.getSandbox().findRegisteredModuleInstance('LayerSwipe')?.getHandler(); + } + getComponent () { + const handler = this.getHandler(); + if (!handler) { + return {}; + } return { component: SwipeToolComponent, - handler: this.handler + handler }; } init (data) { - const configuration = data?.configuration?.layerswipe || {}; - - // restore state to handler -> passing init data to it - this.handler.init(configuration); - if (configuration?.conf || configuration?.state?.active) { + const { state, conf } = data?.configuration?.layerswipe || {}; + const { active = false } = state || {}; + const handler = this.getHandler(); + // Store config before setEnable because it creates plugin which requires config + this.storePluginConf(conf); + handler?.setHideUI(!!conf?.noUI); + handler.setActive(active); + if (conf || active) { this.setEnabled(true); } - - if (configuration.conf) { - this.storePluginConf(configuration.conf); - this.handler.syncState(); - } } getValues () { if (!this.isEnabled()) { return null; } - const { autoStart = false } = this.handler.getState(); - const value = { + const config = this.getPlugin()?.getConfig() || {}; + const { active = false, noUI = false } = this.getHandler()?.getState() || {}; + return { configuration: { layerswipe: { - conf: this.getPlugin()?.getConfig() || {}, + conf: { ...config, noUI }, state: { - active: autoStart + active } } } }; - return value; - } - - stop () { - super.stop(); - this.handler.clearState(); } } @@ -70,6 +69,6 @@ class SwipeTool extends AbstractPublisherTool { Oskari.clazz.defineES('Oskari.publisher.SwipeTool', SwipeTool, { - 'protocol': ['Oskari.mapframework.publisher.Tool'] + protocol: ['Oskari.mapframework.publisher.Tool'] } ); diff --git a/bundles/mapping/layerswipe/publisher/SwipeToolComponent.jsx b/bundles/mapping/layerswipe/publisher/SwipeToolComponent.jsx index 3aa2400f02..7805ac23da 100644 --- a/bundles/mapping/layerswipe/publisher/SwipeToolComponent.jsx +++ b/bundles/mapping/layerswipe/publisher/SwipeToolComponent.jsx @@ -13,8 +13,8 @@ export const SwipeToolComponent = ({ state, controller }) => { <ExtraOptions> <Checkbox className='t_swipe_auto_start' - checked={state.autoStart} - onChange={(e) => controller.setAutoStart(e.target.checked)}> + checked={state.active} + onChange={(e) => controller.setActive(e.target.checked)}> <Message bundleKey="LayerSwipe" messageKey='tool.autoStart' /> </Checkbox> <Checkbox From 938249f19b65c1ea1a9ac66ffc6b46652afa70eb Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 09:52:15 +0200 Subject: [PATCH 091/181] use SwipeHandler --- bundles/mapping/layerswipe/instance.js | 22 +++++---- .../layerswipe/plugin/LayerSwipePlugin.js | 46 ++++++------------- 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/bundles/mapping/layerswipe/instance.js b/bundles/mapping/layerswipe/instance.js index 4bd8460fb4..cadd4b8910 100644 --- a/bundles/mapping/layerswipe/instance.js +++ b/bundles/mapping/layerswipe/instance.js @@ -28,17 +28,23 @@ Oskari.clazz.define( iconCls: 'tool-layer-swipe', tooltip: this.loc('toolLayerSwipe'), sticky: true, - callback: () => this.setActive(!this.getState().active) + callback: () => { + if (this.getState().active) { + this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); + } else { + this.handler?.setActive(true); + } + } }; sandbox.request(this, addToolButtonBuilder('LayerSwipe', 'basictools', buttonConf)); } - if (this.state.active) { + if (this.state?.active) { if (!this.plugin) { // AddToolButtonRequest prefixes group and have to use prefixed group on select tool // It's little confusing that tool can't be selected using group which was used on add this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', ['LayerSwipe', 'default-basictools']); } - Oskari.on('app.start', () => this.setActive(true)); + Oskari.on('app.start', () => this.handler.setActive(true)); } }, @@ -48,16 +54,12 @@ Oskari.clazz.define( getMapModule: function () { return this.mapModule; }, + getHandler: function () { + return this.handler; + }, getState: function () { return this.handler?.getState() || {}; }, - setActive: function (active) { - if (!this.plugin && !active) { - this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); - return; - } - this.handler?.setActive(active); - }, getStateParameters: function () { const { active } = this.getState(); if (active) { diff --git a/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js b/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js index 21da33463f..1e3bbc28cb 100644 --- a/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js +++ b/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js @@ -15,9 +15,7 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug * @method create called automatically on construction * @static */ - function (conf) { - this.instance = null; - this._config = conf || {}; + function () { this._clazz = 'Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlugin'; this._defaultLocation = 'top left'; @@ -32,7 +30,7 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug return true; }, _stopPluginImpl: function () { - this.getInstance()?.setActive(false); + this.getHandler()?.setActive(false); this.teardownUI(); }, setHandler: function (handler) { @@ -43,29 +41,12 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug handler.addStateListener(() => this.refresh()); this.handler = handler; }, - getInstance: function () { - if (!this.instance) { - this.instance = this.getSandbox()?.findRegisteredModuleInstance('LayerSwipe'); - this.setHandler(this.instance.handler); + getHandler: function () { + if (!this.handler) { + const handler = this.getSandbox().findRegisteredModuleInstance('LayerSwipe')?.getHandler(); + this.setHandler(handler); } - return this.instance; - }, - isActive: function () { - const { active = false } = this.getInstance()?.getState() || {}; - return active; - }, - setToolState: function (active) { - this.getInstance()?.setActive(active); - }, - hasUI: function () { - return !this.getConfig()?.noUI; - }, - setHideUI: function (value) { - const old = this.getConfig(); - this.setConfig({ - ...old, - noUI: value - }); + return this.handler; }, /** * @private @method _createControlElement @@ -80,19 +61,20 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug refresh: function () { const el = this.getElement(); - if (!el) { + const handler = this.getHandler(); + if (!el || !handler) { return; } - const active = this.isActive(); - const title = this.getInstance()?.loc('toolLayerSwipe'); + const { active, noUI } = handler.getState(); + const controller = handler.getController(); ReactDOM.render( <MapModuleButton className='t_layerswipe' highlight='stroke' icon={<SwipeIcon />} - visible={this.hasUI()} - title={title} - onClick={() => this.setToolState(!active)} + visible={!noUI} + title={Oskari.getMsg('LayerSwipe', 'toolLayerSwipe')} + onClick={() => controller.toggleTool()} iconActive={active} position={this.getLocation()} iconSize='20px' From 40e711feebec5628414a23fde705ca30fbafb828 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 10:25:26 +0200 Subject: [PATCH 092/181] fix mouse exists --- bundles/mapping/layerswipe/handler/SwipeHandler.js | 2 +- bundles/mapping/layerswipe/view/LayerSwipe.jsx | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/bundles/mapping/layerswipe/handler/SwipeHandler.js b/bundles/mapping/layerswipe/handler/SwipeHandler.js index 8a50e51222..7754f42aab 100644 --- a/bundles/mapping/layerswipe/handler/SwipeHandler.js +++ b/bundles/mapping/layerswipe/handler/SwipeHandler.js @@ -131,7 +131,7 @@ class UIHandler extends StateHandler { } ReactDOM.render( <ThemeProvider> - <LayerSwipe { ...this.getState() } controller={this.getController()} isMobile={Oskari.util.mouseExists()}/> + <LayerSwipe { ...this.getState() } controller={this.getController()} isMobile={!Oskari.util.mouseExists()}/> </ThemeProvider>, this.element); } diff --git a/bundles/mapping/layerswipe/view/LayerSwipe.jsx b/bundles/mapping/layerswipe/view/LayerSwipe.jsx index 2179970535..66d7606589 100644 --- a/bundles/mapping/layerswipe/view/LayerSwipe.jsx +++ b/bundles/mapping/layerswipe/view/LayerSwipe.jsx @@ -33,6 +33,7 @@ const createDraggable = (elementRef, position, limits, setPosition) => { const onTouchMove = (event) => { // prevents text selection from other elements while dragging event.preventDefault(); + event.stopPropagation(); if (!element) { return; } @@ -45,6 +46,7 @@ const createDraggable = (elementRef, position, limits, setPosition) => { const onMouseMove = (event) => { // prevents text selection from other elements while dragging event.preventDefault(); + event.stopPropagation(); if (!element) { return; } @@ -86,13 +88,14 @@ export const LayerSwipe = ThemeConsumer(({ theme, position, mapWidth, isMobile, const margin = isMobile ? MARGIN.mobile : MARGIN.desktop; const width = SPLITTER_WIDTH + 2 * margin; const limits = { min: -margin, max: mapWidth - SPLITTER_WIDTH }; - const style = { transform: `translate(${position - margin}px`, width }; + const style = { transform: `translate(${position - margin}px` }; const onEvent = useCallback(() => createDraggable(elementRef, position, limits, controller.setPosition), [position, limits]); return ( <DragWrapper ref={elementRef} style={style} + $width={width} onMouseDown={() => onEvent()} onTouchStart={() => onEvent()} > <Splitter $color={helper.getAccentColor()} isMobile={isMobile}/> From 0b592b0f2a2fb02c377cb702d4483a6e874a60ad Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 10:26:57 +0200 Subject: [PATCH 093/181] fix error log tool id --- bundles/framework/publisher2/handler/ToolPanelHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/framework/publisher2/handler/ToolPanelHandler.js b/bundles/framework/publisher2/handler/ToolPanelHandler.js index 27809c297f..2da26c7c20 100644 --- a/bundles/framework/publisher2/handler/ToolPanelHandler.js +++ b/bundles/framework/publisher2/handler/ToolPanelHandler.js @@ -61,7 +61,7 @@ class UIHandler extends StateHandler { tool.publisherTool.stop(); } catch (e) { Oskari.log('Publisher.ToolPanelHandler') - .error('Error stopping publisher tool:', tool.getTool().id); + .error('Error stopping publisher tool:', tool.id); } }); } From 56494dde23d37e048900825bc5390c9ac7341616 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 11:18:11 +0200 Subject: [PATCH 094/181] add mask for mouse to prevent text selection --- .../mapping/layerswipe/view/LayerSwipe.jsx | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/bundles/mapping/layerswipe/view/LayerSwipe.jsx b/bundles/mapping/layerswipe/view/LayerSwipe.jsx index 66d7606589..5c8bd13dd8 100644 --- a/bundles/mapping/layerswipe/view/LayerSwipe.jsx +++ b/bundles/mapping/layerswipe/view/LayerSwipe.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef } from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { ThemeConsumer } from 'oskari-ui/util'; @@ -9,6 +9,12 @@ export const MARGIN = { desktop: 2, mobile: 5 }; +const Mask = styled.div` + position: absolute; + top: 0; + width: 100%; + height: 100%; +`; const DragWrapper = styled.div` position: absolute; @@ -25,7 +31,7 @@ const Splitter = styled.div` margin-left: ${props => props.isMobile ? MARGIN.mobile : MARGIN.desktop}px; `; -const createDraggable = (elementRef, position, limits, setPosition) => { +const createDraggable = (elementRef, position, limits, setPosition, setDragging) => { const element = elementRef.current; // previousTouch is assigned in onTouchMove to track change and onMouseUp for reset let previousTouch; @@ -45,6 +51,7 @@ const createDraggable = (elementRef, position, limits, setPosition) => { }; const onMouseMove = (event) => { // prevents text selection from other elements while dragging + setDragging(true); event.preventDefault(); event.stopPropagation(); if (!element) { @@ -73,6 +80,7 @@ const createDraggable = (elementRef, position, limits, setPosition) => { document.removeEventListener('touchend', onMouseUp); document.removeEventListener('touchcancel', onMouseUp); previousTouch = null; + setDragging(false); }; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); @@ -82,6 +90,7 @@ const createDraggable = (elementRef, position, limits, setPosition) => { }; export const LayerSwipe = ThemeConsumer(({ theme, position, mapWidth, isMobile, controller }) => { + const [dragging, setDragging] = useState(false); const elementRef = useRef(); const helper = getHeaderTheme(theme); @@ -90,8 +99,8 @@ export const LayerSwipe = ThemeConsumer(({ theme, position, mapWidth, isMobile, const limits = { min: -margin, max: mapWidth - SPLITTER_WIDTH }; const style = { transform: `translate(${position - margin}px` }; - const onEvent = useCallback(() => createDraggable(elementRef, position, limits, controller.setPosition), [position, limits]); - return ( + const onEvent = useCallback(() => createDraggable(elementRef, position, limits, controller.setPosition, setDragging), [position, limits]); + const swipe = ( <DragWrapper ref={elementRef} style={style} @@ -101,6 +110,14 @@ export const LayerSwipe = ThemeConsumer(({ theme, position, mapWidth, isMobile, <Splitter $color={helper.getAccentColor()} isMobile={isMobile}/> </DragWrapper> ); + if (dragging) { + return ( + <Mask onMouseMove={event => event.preventDefault()}> + {swipe} + </Mask> + ); + } + return swipe; }); LayerSwipe.propTypes = { From 4dd5201efbe1b553c09e30e25d9a9d6458ba3de9 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 11:28:58 +0200 Subject: [PATCH 095/181] fix max limit --- bundles/mapping/layerswipe/view/LayerSwipe.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/mapping/layerswipe/view/LayerSwipe.jsx b/bundles/mapping/layerswipe/view/LayerSwipe.jsx index 5c8bd13dd8..ed67b0d53a 100644 --- a/bundles/mapping/layerswipe/view/LayerSwipe.jsx +++ b/bundles/mapping/layerswipe/view/LayerSwipe.jsx @@ -72,7 +72,7 @@ const createDraggable = (elementRef, position, limits, setPosition, setDragging) setPosition(max); } }; - // window.visualViewport. + const onMouseUp = () => { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); @@ -89,14 +89,14 @@ const createDraggable = (elementRef, position, limits, setPosition, setDragging) document.addEventListener('touchcancel', onMouseUp); }; -export const LayerSwipe = ThemeConsumer(({ theme, position, mapWidth, isMobile, controller }) => { +export const LayerSwipe = ThemeConsumer(({ theme, position, mapSize, isMobile, controller }) => { const [dragging, setDragging] = useState(false); const elementRef = useRef(); const helper = getHeaderTheme(theme); const margin = isMobile ? MARGIN.mobile : MARGIN.desktop; const width = SPLITTER_WIDTH + 2 * margin; - const limits = { min: -margin, max: mapWidth - SPLITTER_WIDTH }; + const limits = { min: -margin, max: mapSize.width - SPLITTER_WIDTH }; const style = { transform: `translate(${position - margin}px` }; const onEvent = useCallback(() => createDraggable(elementRef, position, limits, controller.setPosition, setDragging), [position, limits]); From f6c18a0b7ee63ad612615d845b1c77c5b3714d3e Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 11:58:49 +0200 Subject: [PATCH 096/181] inject localization --- .../catalogue/metadata/resources/locale/en.js | 6 +-- .../catalogue/metadata/resources/locale/fi.js | 4 +- .../catalogue/metadata/resources/locale/sv.js | 41 +++++++++---------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/bundles/catalogue/metadata/resources/locale/en.js b/bundles/catalogue/metadata/resources/locale/en.js index 0ddc9bfdfa..8244cfb656 100644 --- a/bundles/catalogue/metadata/resources/locale/en.js +++ b/bundles/catalogue/metadata/resources/locale/en.js @@ -11,7 +11,7 @@ Oskari.registerLocalization({ "inspire": "Inspire metadata", "jhs": "ISO 19115 metadata", "quality": "Data quality", - "actions": "Actions", + "actions": "Actions" }, "actions": { "xml": "ISO 19139 XML file", @@ -21,7 +21,7 @@ Oskari.registerLocalization({ "showBBOX": "Show coverage area" }, "error": { - "notFound": "Metadata was not found", + "notFound": "Metadata was not found" }, "quality": { "pass": { @@ -452,4 +452,4 @@ Oskari.registerLocalization({ } } } -}); \ No newline at end of file +}); diff --git a/bundles/catalogue/metadata/resources/locale/fi.js b/bundles/catalogue/metadata/resources/locale/fi.js index 8713b07110..bb57d809a2 100644 --- a/bundles/catalogue/metadata/resources/locale/fi.js +++ b/bundles/catalogue/metadata/resources/locale/fi.js @@ -19,7 +19,7 @@ Oskari.registerLocalization({ "hide": "Piilota karttataso", "removeBBOX": "Piilota kattavuusalue", "showBBOX": "Näytä kattavuusalue" - }, + }, "error": { "notFound": "Metatietoja ei löytynyt." }, @@ -452,4 +452,4 @@ Oskari.registerLocalization({ } } } -}); \ No newline at end of file +}); diff --git a/bundles/catalogue/metadata/resources/locale/sv.js b/bundles/catalogue/metadata/resources/locale/sv.js index e67ae80619..9afb137983 100644 --- a/bundles/catalogue/metadata/resources/locale/sv.js +++ b/bundles/catalogue/metadata/resources/locale/sv.js @@ -28,21 +28,21 @@ Oskari.registerLocalization({ "true": "Datamängden uppfyller specifikationen.", "false": "Datamängden uppfyller inte specifikationen." }, - "nameOfMeasure": "", - "citationTitle": "", - "measureIdentificationCode": "", - "measureIdentificationAuthorization": "", - "measureDescription": "", - "evaluationMethodType": "", - "evaluationMethodDescription": "", - "evaluationProcedure": "", - "dateTime": "", - "specification": "", - "explanation": "", - "valueType": "", - "valueUnit": "", - "errorStatistic": "", - "value": "" + "nameOfMeasure": "Kvalitetmåttets namn", + "citationTitle": "Titel", + "measureIdentificationCode": "Kvalitetmåttets identifikationskod", + "measureIdentificationAuthorization": "Identifieringskod för kvalitetsmått", + "measureDescription": "Beskrivning av kvalitetsmått", + "evaluationMethodType": "Typ av evalueringsmetod", + "evaluationMethodDescription": "Beskrivning av evalueringsmetod", + "evaluationProcedure": "Evalueringsprocedur", + "dateTime": "Datum och tid", + "specification": "Spesifikationens titel", + "explanation": "Förklaring", + "valueType": "Typ av datakvalitetsresultat", + "valueUnit": "Enhet av datakvalitetsresultat", + "errorStatistic": "Statistisk metod", + "value": "Värde" }, "label": { "layerList": "Kartlager", @@ -61,18 +61,18 @@ Oskari.registerLocalization({ "metadataLanguage": "Metadata språk", "metadataResponsibleParties": "Organisationsnamn", "metadataStandardName": "Metadata standard namn", - "metadataStandardVersion": "Metadata standard version", + "metadataStandardVersion": "Version av metastandard", "onlineResources": "Online resurser", "operatesOn": "Drivs med", "otherConstraints": "Övriga restriktioner", "reportConformance": "Specifikationsuppfyllelse", - "conformanceResult": "", - "quantitativeResult": "", + "conformanceResult": "Kravöverensstämmelse", + "quantitativeResult": "Mätt kvalitetsresultat", "responsibleParties": "Ansvarig part", "resourceIdentifiers": "Resurs identifierare", "languages": "Resurs språk", "scopeCodes": "Resurs typ", - "serviceType": "Service type", + "serviceType": "Typ av geodatatjänst", "spatialRepresentationTypes": "Rumslig presentationstyp", "spatialResolutions": "Rumslig upplösning", "temporalExtents": "Temporära utsträckningar", @@ -451,6 +451,5 @@ Oskari.registerLocalization({ } } } - } -}); \ No newline at end of file +}); From e6c14663768526a55c66876a36e0849cb4259d79 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 12:15:35 +0200 Subject: [PATCH 097/181] add localization tools --- tools/faulty-bundles.js | 123 +++++++++++++++++++++++++++ tools/faulty-locale.js | 154 ++++++++++++++++++++++++++++++++++ tools/inject-bundle-locale.js | 135 +++++++++++++++++++++++++++++ 3 files changed, 412 insertions(+) create mode 100644 tools/faulty-bundles.js create mode 100644 tools/faulty-locale.js create mode 100644 tools/inject-bundle-locale.js diff --git a/tools/faulty-bundles.js b/tools/faulty-bundles.js new file mode 100644 index 0000000000..5116d5dbe3 --- /dev/null +++ b/tools/faulty-bundles.js @@ -0,0 +1,123 @@ +const path = require('path'); +const fs = require('fs'); +const glob = require('glob'); + +/** -------- CONSTANTS -------- */ +const LANGS = ['fi', 'en', 'sv']; +const DEFAULT = 'en'; +const CSV = ';'; + +/** -------- PARAMS -------- */ +const skipEqual = false; +const skipEmpty = false; +const skipEmptyValues = ['desc']; + +/** -------- HELPERS -------- */ +const isObject = (item) => item && typeof item === 'object' && !Array.isArray(item); + +// returns object with flatten keys {foo: {bar: 1 }} => {foo.bar: 1} +const flattenKeys = (obj) => { + if (!isObject(obj)) return {}; // or throw error + + const getEntries = (o, prefix = '') => + Object.entries(o).flatMap(([k, v]) => + isObject(v) ? getEntries(v, `${prefix}${k}.`) : [ [`${prefix}${k}`, v] ] + ); + return Object.fromEntries(getEntries(obj)); +}; + +const getAllKeys = (obj = {}) => { + const keys = []; + Object.values(obj) + .forEach(langObj => Object.keys(langObj) + .forEach(key => keys.includes(key) || keys.push(key))); + return keys; +}; + +const getFaultyLocales = (flatLocs, allKeys, lang) => { + const loc = flatLocs[lang] || {}; + const response = { + missing: allKeys.filter(key => !allKeys.includes(key)), + additional: allKeys.filter(key => !allKeys.includes(key)), + empty: allKeys.filter(key => typeof loc[key] === 'string' && !loc[key].trim() && !skipEmptyValues.includes(key)), + equal: allKeys.filter(key => locales.some(l => l !== lang && loc[key] && flatLocs[l][key] === loc[key]) && !locales.every(l => flatLocs[l][key] === loc[key])) + }; + if (skipEqual) { + delete response.equal; + } + if (skipEmpty) { + delete response.empty; + } + return response; +}; + +const gatherResultKeys = (results) => { + const allPaths = new Set(); + Object.values(results).forEach(langResult => { + const paths = Object.values(langResult).flat(); + paths.forEach(path => allPaths.add(path)); + }); + return Array.from(allPaths); +}; + +/** Tool for gathering faulty localizations for all bundles +* Usage: +* node faulty-bundles.js +* node faulty-bundles.js csv all fr is ru +*/ + +const [ig, nore, output, base = DEFAULT, ...langs] = process.argv; +const locales = [...LANGS, ...langs]; +console.log(`Prosessing localization for: ${locales.join()}. Gather keys from: ${base}`); +const localeFilePaths = glob.sync('/bundles/**/resources/locale/@(' + locales.join('|') + ').js', {root: path.join(__dirname, '..')}); + +if (!localeFilePaths.length) { + throw new Error('No files found, check bundle name'); +} +console.log(`Found ${localeFilePaths.length} localization files`); + +const flat = {}; +localeFilePaths.forEach(filePath => { + const Oskari = { + registerLocalization: ({ lang, value, key }) => { + if (!flat[key]) { + flat[key] = {}; + } + flat[key][lang] = flattenKeys(value); + } + }; + const source = fs.readFileSync(filePath, 'utf8'); + eval(source); +}); + +const results = {}; +Object.keys(flat).forEach(bundle => { + const values = flat[bundle]; + const allKeys = base === 'all' ? getAllKeys(values) : Object.keys(values[base]); + Object.keys(values).forEach(lang => { + const response = getFaultyLocales(values, allKeys, lang); + const count = Object.values(response).reduce((sum, arr) => sum += arr.length, 0); + if (count === 0) return; + if (!results[bundle]) { + results[bundle] = {}; + } + results[bundle][lang] = output === 'csv' ? response : count; + }); +}); +const outputPath = path.join(__dirname, 'summary'); +if (output === 'csv') { + const csv = []; + Object.keys(results).forEach(bundle => { + csv.push([bundle, ...locales]); + gatherResultKeys(results[bundle]).forEach(key => { + const values = locales.map(lang => flat[bundle][lang][key]); + csv.push([key, ...values]); + }); + csv.push([]); // empty line + }); + fs.writeFileSync(outputPath + '.csv', csv.map(line => line.join(CSV)).join('\n')); +} else { + const json = { locales, base: base, ...results }; + fs.writeFileSync(outputPath + '.json', JSON.stringify(json, null, 2)); +} +console.log('Extracted file succesfully'); diff --git a/tools/faulty-locale.js b/tools/faulty-locale.js new file mode 100644 index 0000000000..66c6a601ae --- /dev/null +++ b/tools/faulty-locale.js @@ -0,0 +1,154 @@ +const path = require('path'); +const fs = require('fs'); +const glob = require('glob'); + +/** -------- CONSTANTS -------- */ +const LANGS = ['fi', 'en', 'sv']; +const KEYS_FROM = 'en'; // lang || 'all' +const CSV = ';'; + +/** -------- PARAMS -------- */ +const skipEmptyValues = ['desc']; + +/** -------- HELPERS -------- */ +const isObject = (item) => item && typeof item === 'object' && !Array.isArray(item); + +// returns object with flatten keys {foo: {bar: 1 }} => {foo.bar: 1} +const flattenKeys = (obj) => { + if (!isObject(obj)) return {}; // or throw error + + const getEntries = (o, prefix = '') => + Object.entries(o).flatMap(([k, v]) => + isObject(v) ? getEntries(v, `${prefix}${k}.`) : [ [`${prefix}${k}`, v] ] + ); + return Object.fromEntries(getEntries(obj)); +}; + +// returns diff object with flatten keys {foo.bar: [o1, o2]} +const diff = (o1, o2) => { + // create flat objects + const flat1 = flattenKeys(o1); + const flat2 = flattenKeys(o2); + + // merge keys and remove duplicates + const keys = [...new Set([...Object.keys(flat1) ,...Object.keys(flat2)])]; + + return keys.reduce((diff, key) => { + if (flat1[key] === flat2[key]) return diff; + return { + ...diff, + [key]: [flat1[key], flat2[key]] + }; + }, {}); +}; + +const getAllKeys = (obj = {}) => { + const keys = []; + Object.values(obj) + .forEach(langObj => Object.keys(langObj) + .forEach(key => keys.includes(key) || keys.push(key))); + return keys; +}; + +const getFaultyLocales = (flatLocs, allKeys, lang) => { + const loc = flatLocs[lang] || {}; + let response = { + missing: allKeys.filter(key => !allKeys.includes(key)), + additional: allKeys.filter(key => !allKeys.includes(key)), + empty: allKeys.filter(key => typeof loc[key] === 'string' && !loc[key].trim() && !skipEmptyValues.includes(key)), + equal: allKeys.filter(key => locales.some(l => l !== lang && loc[key] && flatLocs[l][key] === loc[key]) && !locales.every(l => flatLocs[l][key] === loc[key])) + }; + if (lang === KEYS_FROM) { + response = { empty: response.empty }; + } + const count = Object.values(response).reduce((sum, arr) => sum += arr.length, 0); + const status = count === 0 ? 'OK' : `${count} faulty keys`; + console.log(`${lang} -> ${status}`); + return response; +}; + +const gatherResultKeys = (results) => { + const allPaths = new Set(); + Object.values(results).forEach(langResult => { + const paths = Object.values(langResult).flat(); + paths.forEach(path => allPaths.add(path)); + }); + return Array.from(allPaths); +}; + +/** Tool for gathering faulty localizations for bundle +* Usage: +* node faulty-locale.js myplaces3 +* node faulty-locale.js myplaces3 text +* node faulty-locale.js myplaces3 csv fr is ru +*/ + +const [ig, nore, bundle, output, ...langs] = process.argv; +if (!bundle) { + throw new Error('Must give bundle name as parameter'); +} +const locales = [...LANGS, ...langs]; +console.log(`Prosessing "${bundle}" localization for: ${locales.join()}. Gather keys from: ${KEYS_FROM}`); +const localeFilePaths = glob.sync('/bundles/**/' + bundle +'/resources/locale/@(' + locales.join('|') + ').js', {root: path.join(__dirname, '..')}); + +if (!localeFilePaths.length) { + throw new Error('No files found, check bundle name'); +} + +const flatLocs = {}; +localeFilePaths.forEach(filePath => { + const Oskari = { + registerLocalization: ({ lang, value }) => { + if (!lang) { + throw new Error('Localization file has no "lang"!'); + } + console.log("Found and registered localization for: " + lang); + flatLocs[lang] = flattenKeys(value); + } + }; + const source = fs.readFileSync(filePath, 'utf8'); + eval(source); +}); + +const allKeys = KEYS_FROM === 'all' ? getAllKeys(flatLocs) : Object.keys(flatLocs[KEYS_FROM]); +if (!allKeys.length) { + throw new Error('No keys found for: ' + KEYS_FROM); +} +console.log(`Gathered ${allKeys.length} keys from: ${KEYS_FROM}`); + +const results = {}; +locales.forEach(lang => { + const loc = getFaultyLocales(flatLocs, allKeys, lang); + if (lang === KEYS_FROM) { + results[lang] = { empty: loc.empty }; + } else { + results[lang] = loc; + } +}); + +const outputPath = path.join(__dirname, bundle); + +if (output === 'text') { + const text=[ + `"${bundle}" localization for: ${locales.join()}. Base locale for keys: ${KEYS_FROM}`, + '---------------------------------------------------------------------------------' + ]; + gatherResultKeys(results).forEach(key => { + text.push(`${key}:`); + locales.forEach(lang => text.push(`${lang}: "${flatLocs[lang][key]}"`)); + text.push(''); + }); + fs.writeFileSync(outputPath + '.txt', text.join('\n')); +} else if (output === 'csv') { + const csv = [['path', ...locales]]; + gatherResultKeys(results).forEach(key => { + const values = locales.map(lang => flatLocs[lang][key]); + csv.push([key, ...values]); + }); + fs.writeFileSync(outputPath + '.csv', csv.map(line => line.join(CSV)).join('\n')); +} else { + const json = { bundle, locales, base: KEYS_FROM, ...results }; + fs.writeFileSync(outputPath + '.json', JSON.stringify(json, null, 2)); +} + +console.log('Extracted file succesfully'); diff --git a/tools/inject-bundle-locale.js b/tools/inject-bundle-locale.js new file mode 100644 index 0000000000..f9728b6505 --- /dev/null +++ b/tools/inject-bundle-locale.js @@ -0,0 +1,135 @@ +const fs = require('fs'); +const merge = require('merge'); + +/** -------- CONSTANTS -------- */ +const LANGS = ['fi', 'en', 'sv', 'is', 'fr', 'ru']; +const CSV = ';'; +const KVP = ':'; + +/** -------- HELPERS -------- */ +const count = {}; +LANGS.forEach(lang => count[lang] = 0); +let total = 0; + +const unifyPath = (path) => { + const end = path.endsWith('/') ? path.length - 1 : path.length; + const start = path.startsWith('/') ? 1 : 0; + return path.substring(start, end); +}; + +const getJSON = (fileExt, content) => { + return fileExt === 'csv' ? getJsonFromCSV(content) : getJsonFromText(content); +}; + +const injectJson = (base, parts, value = '') => { + const trimmed = value.trim(); + if (!trimmed) return; + + let temp = base; + parts.map(p => p.trim()).forEach((part, i) => { + if (i === parts.length - 1) { + temp[part] = trimmed; + total++; + return; + } + if (!temp[part]) { + temp[part] = {}; + } + temp = temp[part]; + }); +}; + +const getJsonFromCSV = (content) => { + const [heading, ...lines] = content.split('\n'); + const [ignore, ...langs] = heading.split(CSV); + const json = {}; + langs.forEach(lang => json[lang] = {}); + + lines.filter(line => line.trim()).forEach(line => { + const [key, ...values] = line.split(CSV); + langs.forEach((lang, i) => { + const value = values[i]; + const parts = key.split('.'); + injectJson(json[lang], parts, value); + }); + }); + return json; +}; + +const getJsonFromText = (content) => { + const lines = content.split('\n'); + const json = {}; + let parts = []; + lines.forEach(line => { + if (!line.trim()) return; + const [raw, value] = line.replaceAll('"','').split(KVP); + const key = raw.trim(); + if(!LANGS.includes(key)) { + parts = key.split('.'); + return; + } + const lang = key; + if (!json[lang]) { + json[lang] = {}; + } + injectJson(json[lang], parts, value); + }); + return json; +}; + +const getBaseLocalization = filePath => { + let locale = {}; + const Oskari = { + registerLocalization: (loc) => { + if (!loc.lang) { + throw new Error('Localization file has no "lang"!'); + } + console.log("Found localization for: " + loc.lang); + locale = loc; + } + }; + const source = fs.readFileSync(filePath, 'utf8'); + eval(source); + return locale; +}; + +/** Tool for injecting bundle localizations from file +* Usage: +* node inject-bundle-locale.js locales.csv ../bundles/framework/myplaces3/ +*/ + +const [ig, nore, file, path, lang] = process.argv; + +if (!file) { + throw new Error('Must give file name as parameter'); +} +const fileExt = file.split('.').pop(); +if (fileExt === 'json' && !lang) { + throw new Error('Must give language as parameter with json file'); +} +if (!path) { + throw new Error('Must give bundle path as parameter'); +} +const oskariBasePath = __dirname.replace('/tools', ''); +const bundlePath = unifyPath(path.substring(2)); // removes '..' + +const content = fs.readFileSync(`${__dirname}/${file}`, 'utf8'); +const values = fileExt === 'json' ? { [lang]: content } : getJSON(fileExt, content); +const languages = Object.keys(values).filter(lang => LANGS.includes(lang)); + +let bundle = 'bundle'; + +languages.forEach(function(lang) { + const source = `${oskariBasePath}/${bundlePath}/resources/locale/${lang}.js`; + const locale = getBaseLocalization(source); + bundle = locale.key || bundle; + + const value = values[lang] || {}; + const json = merge.recursive(true, locale, { value }); + + const string = JSON.stringify(json, null, 4); + const content = `Oskari.registerLocalization(${string});\n`; + fs.writeFileSync(source, content, 'utf8'); +}); + +console.log(`Injected ${total} localization strings to "${bundle}" locales. Use git to inspect changes.`); \ No newline at end of file From 0c49737d80d34e0df1cc2a2e84e2372647c5691f Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 12:35:08 +0200 Subject: [PATCH 098/181] new line --- tools/inject-bundle-locale.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/inject-bundle-locale.js b/tools/inject-bundle-locale.js index f9728b6505..28388d30d8 100644 --- a/tools/inject-bundle-locale.js +++ b/tools/inject-bundle-locale.js @@ -132,4 +132,4 @@ languages.forEach(function(lang) { fs.writeFileSync(source, content, 'utf8'); }); -console.log(`Injected ${total} localization strings to "${bundle}" locales. Use git to inspect changes.`); \ No newline at end of file +console.log(`Injected ${total} localization strings to "${bundle}" locales. Use git to inspect changes.`); From b80b2afb25ca8d09279815fc3da6d86eaa32145d Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 13:05:01 +0200 Subject: [PATCH 099/181] sync toolbar on publisher close --- bundles/mapping/layerswipe/instance.js | 16 ++++++++-------- .../layerswipe/plugin/LayerSwipePlugin.js | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/bundles/mapping/layerswipe/instance.js b/bundles/mapping/layerswipe/instance.js index cadd4b8910..edfadc838c 100644 --- a/bundles/mapping/layerswipe/instance.js +++ b/bundles/mapping/layerswipe/instance.js @@ -28,13 +28,7 @@ Oskari.clazz.define( iconCls: 'tool-layer-swipe', tooltip: this.loc('toolLayerSwipe'), sticky: true, - callback: () => { - if (this.getState().active) { - this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); - } else { - this.handler?.setActive(true); - } - } + callback: () => this.setToolActive(!this.getState().active) }; sandbox.request(this, addToolButtonBuilder('LayerSwipe', 'basictools', buttonConf)); } @@ -47,7 +41,13 @@ Oskari.clazz.define( Oskari.on('app.start', () => this.handler.setActive(true)); } }, - + setToolActive: function (active) { + if (!this.plugin && !active) { + this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); + return; + } + this.handler?.setActive(active); + }, getSandbox: function () { return this.sandbox; }, diff --git a/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js b/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js index 1e3bbc28cb..5656ba2854 100644 --- a/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js +++ b/bundles/mapping/layerswipe/plugin/LayerSwipePlugin.js @@ -30,7 +30,8 @@ Oskari.clazz.define('Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipePlug return true; }, _stopPluginImpl: function () { - this.getHandler()?.setActive(false); + // to sync toolbar button state on publisher close + this.getSandbox().findRegisteredModuleInstance('LayerSwipe')?.setToolActive(false); this.teardownUI(); }, setHandler: function (handler) { From a2afedf4e96a7c0366fa0932951a1a273aef7b28 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 15:56:31 +0200 Subject: [PATCH 100/181] hide navigation on mobile set active from tool --- bundles/mapping/layerswipe/instance.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bundles/mapping/layerswipe/instance.js b/bundles/mapping/layerswipe/instance.js index edfadc838c..af2d51640c 100644 --- a/bundles/mapping/layerswipe/instance.js +++ b/bundles/mapping/layerswipe/instance.js @@ -42,9 +42,13 @@ Oskari.clazz.define( } }, setToolActive: function (active) { - if (!this.plugin && !active) { - this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); - return; + if (!this.plugin) { + if (!active) { + this.getSandbox().postRequestByName('Toolbar.SelectToolButtonRequest', []); + return; + } else if (Oskari.util.isMobile()) { + Oskari.dom.showNavigation(false); + } } this.handler?.setActive(active); }, From c5976215bc581daa0e32f2a180ce0c0498211ca3 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Fri, 8 Nov 2024 16:47:30 +0200 Subject: [PATCH 101/181] update antd --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e8dae0db9d..37d4dc43c2 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@testing-library/jest-dom": "5.7.0", "@testing-library/react": "12.1.5", "@testing-library/user-event": "14.4.3", - "antd": "5.21.2", + "antd": "5.21.6", "async": "3.2.6", "babel-loader": "8.3.0", "babel-plugin-styled-components": "2.1.4", From 8df07a8419b06f3589e754222dab8e497483e75b Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 12 Nov 2024 00:33:29 +0200 Subject: [PATCH 102/181] fix group --- bundles/mapping/mapmodule/publisher/layers/MapLayerListTool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/mapping/mapmodule/publisher/layers/MapLayerListTool.js b/bundles/mapping/mapmodule/publisher/layers/MapLayerListTool.js index ac01a1f008..b9c45d212c 100644 --- a/bundles/mapping/mapmodule/publisher/layers/MapLayerListTool.js +++ b/bundles/mapping/mapmodule/publisher/layers/MapLayerListTool.js @@ -8,7 +8,7 @@ class MapLayerListTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 5; - this.group = 'additional'; + this.group = 'layers'; this.handler = new MapLayerListHandler(this); } From f640343d021a85a9ea676080bdcff07c153e3e72 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 12 Nov 2024 00:42:41 +0200 Subject: [PATCH 103/181] clean comments and getBaseLayerOptions function --- .../layers/BackgroundLayerSelectionPlugin.js | 65 +++++++++++-------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/bundles/mapping/mapmodule/plugin/layers/BackgroundLayerSelectionPlugin.js b/bundles/mapping/mapmodule/plugin/layers/BackgroundLayerSelectionPlugin.js index 0b6b3a58c1..e009dfc946 100755 --- a/bundles/mapping/mapmodule/plugin/layers/BackgroundLayerSelectionPlugin.js +++ b/bundles/mapping/mapmodule/plugin/layers/BackgroundLayerSelectionPlugin.js @@ -18,7 +18,7 @@ Oskari.clazz.define( * * */ - function (config) { + function () { this._clazz = 'Oskari.mapframework.bundle.mapmodule.plugin.BackgroundLayerSelectionPlugin'; this._defaultLocation = 'bottom center'; @@ -26,6 +26,7 @@ Oskari.clazz.define( this._name = 'BackgroundLayerSelectionPlugin'; // make sure baseLayers aren't numbers. this._baseLayerIds = this._config?.baseLayers?.map(id => typeof id === 'number' ? id.toString() : id); + this._baseLayerOptions = []; this._template = jQuery('<div class="backgroundLayerSelectionPlugin oskariui mapplugin"/>'); }, { /** @static @property __name module name */ @@ -50,9 +51,8 @@ Oskari.clazz.define( * * Rearranges layers */ - AfterRearrangeSelectedMapLayerEvent: function (event) { + AfterRearrangeSelectedMapLayerEvent: function () { // Update selection, bottom baselayer might've changed - // this._updateUISelection(); this.refresh(); }, @@ -62,12 +62,8 @@ Oskari.clazz.define( * * Removes the layer from selection */ - AfterMapLayerRemoveEvent: function (event) { - // Redo ui, one of our layers might've been deleted - // TODO Check if event.getMapLayer() id is in our layers first... - // if not, still do this._updateUISelection() as the selected - // layer might still have changed - // this._createLayerSelectionElements(); + AfterMapLayerRemoveEvent: function () { + // Update selection, bottom baselayer might've changed this.refresh(); }, @@ -77,12 +73,8 @@ Oskari.clazz.define( * * Adds the layer to selection */ - AfterMapLayerAddEvent: function (event) { - // Redo ui, we might've gotten a previously missing layer - // TODO Check if event.getMapLayer() id is in our layers first... - // if not, still do this._updateUISelection() as the selected - // layer might still have changed - // this._createLayerSelectionElements(); + AfterMapLayerAddEvent: function () { + // Update selection, bottom baselayer might've changed this.refresh(); }, @@ -93,11 +85,18 @@ Oskari.clazz.define( * Adds the layer to selection */ MapLayerEvent: function (event) { - // TODO add check for event.getMapLayer().getId() here? - // this._createLayerSelectionElements(); + const layerId = event.getLayerId()?.toString(); + if (layerId && !this._getBaseLayerIds().includes(layerId)) { + // handle mass events and base layers only + return; + } + if (event.getOperation() === 'update') { + // clear to get updated options on refresh + this._baseLayerOptions = []; + } this.refresh(); }, - MapSizeChangedEvent: function (evt) { + MapSizeChangedEvent: function () { this.refresh(); } }; @@ -121,6 +120,23 @@ Oskari.clazz.define( _getBaseLayerIds: function () { return this._baseLayerIds || []; }, + _getBaseLayerOptions: function () { + if (this._baseLayerOptions.length) { + return this._baseLayerOptions; + } + const ids = this._getBaseLayerIds(); + const sb = this.getSandbox(); + const options = ids.map(id => ({ + id, + title: sb.findMapLayerFromAllAvailable(id)?.getName(), + action: () => this._onSelect(id) + })).filter(opt => opt.title); + if (ids.length === options.length) { + // store if we have full set + this._baseLayerOptions = options; + } + return options; + }, /** * Does the actual layer selection update * @param {String} newId Id of the new base layer @@ -154,23 +170,16 @@ Oskari.clazz.define( */ _createLayerSelectionElements: function () { const element = this.getElement(); - const ids = this._getBaseLayerIds(); - if (!element || !ids.length) { + if (!element) { return; } - const sb = this.getSandbox(); - const baseLayers = ids.map(id => ({ - id, - title: sb.findMapLayerFromAllAvailable(id)?.getName(), - action: () => this._onSelect(id) - })); ReactDOM.render( <ThemeProvider value={this.getMapModule().getMapTheme()}> <BackgroundLayerSelection isMobile={Oskari.util.isMobile()} - baseLayers={baseLayers} + baseLayers={this._getBaseLayerOptions()} selectedId={this._getSelectedId()} - mapWidth={sb.getMap().getWidth()} + mapWidth={this.getSandbox().getMap().getWidth()} /> </ThemeProvider>, element[0] From 9b390b2739900ff87b93473b1736af78e38d1a45 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Tue, 12 Nov 2024 14:07:43 +0200 Subject: [PATCH 104/181] publisher coordinatetool to react --- .../publisher/CoordinateTool.js | 220 +++++++----------- .../publisher/CoordinateToolComponent.jsx | 22 ++ .../publisher/CoordinateToolHandler.js | 48 ++++ .../framework/bundle/coordinatetool/bundle.js | 2 +- 4 files changed, 154 insertions(+), 138 deletions(-) create mode 100644 bundles/framework/coordinatetool/publisher/CoordinateToolComponent.jsx create mode 100644 bundles/framework/coordinatetool/publisher/CoordinateToolHandler.js diff --git a/bundles/framework/coordinatetool/publisher/CoordinateTool.js b/bundles/framework/coordinatetool/publisher/CoordinateTool.js index 8bf0dde59d..d92d99695e 100755 --- a/bundles/framework/coordinatetool/publisher/CoordinateTool.js +++ b/bundles/framework/coordinatetool/publisher/CoordinateTool.js @@ -1,156 +1,102 @@ -Oskari.clazz.define('Oskari.mapframework.publisher.tool.CoordinateTool', - function () { - }, { - index: 4, - lefthanded: 'top left', - righthanded: 'top right', - templates: { - 'toolOptions': '<div class="tool-options"></div>' - }, - supportedProjections: null, - noUI: null, - projectionTrasformationIsCheckedInModifyMode: false, - noUiIsCheckedInModifyMode: false, +import { AbstractPublisherTool } from '../../publisher2/tools/AbstractPublisherTool'; +import { CoordinateToolComponent } from './CoordinateToolComponent'; +import { CoordinateToolHandler } from './CoordinateToolHandler'; +class CoordinateTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + this.bundleName = 'coordinatetool'; + this.handler = new CoordinateToolHandler(this); + } - /** + init (data) { + if (!data || !data.configuration[this.bundleName]) { + // new publication and no saved config but ther is a toolconfig -> init with that. + if (this.toolConfig) { + this.handler.init(this.toolConfig); + } + return; + } + + // saved configuration -> restore. + const conf = data.configuration[this.bundleName].conf || {}; + this.handler.init(conf); + this.storePluginConf(conf); + this.setEnabled(true); + } + + getComponent () { + return { + component: CoordinateToolComponent, + handler: this.handler + }; + } + + /** * Get tool object. * @method getTool * * @returns {Object} tool description */ - getTool: function () { - return { - id: 'Oskari.mapframework.bundle.coordinatetool.plugin.CoordinateToolPlugin', - title: 'CoordinateToolPlugin', - config: { - ...(this.state.pluginConfig || {}) - } - }; - }, - - // Key in view config non-map-module-plugin tools (for returning the state when modifying an existing published map). - bundleName: 'coordinatetool', - - /** - * Initialise tool - * @method init - */ - init: function (data) { - var me = this; - if (!data || !data.configuration[me.bundleName]) { - return; + getTool () { + return { + id: 'Oskari.mapframework.bundle.coordinatetool.plugin.CoordinateToolPlugin', + title: Oskari.getMsg('Publisher2', 'BasicView.maptools.CoordinateToolPlugin'), + config: { + ...(this.state.pluginConfig || {}) } - var conf = data.configuration[me.bundleName].conf || {}; - this.storePluginConf(conf); - me.projectionTrasformationIsCheckedInModifyMode = !!conf.supportedProjections; - me.noUiIsCheckedInModifyMode = !!conf.noUI; - me.setEnabled(true); - }, - /** + }; + } + + /** * Get values. * @method getValues * @public * * @returns {Object} tool value object */ - getValues: function () { - var me = this; - if (!this.isEnabled()) { - return null; - } - - const pluginConfig = this.getPlugin().getConfig(); - - if (me.toolConfig) { - for (var configName in me.toolConfig) { - pluginConfig[configName] = me.toolConfig[configName]; - // Not save supportedProjections if is not checked - if (configName === 'supportedProjections' && !me.supportedProjections) { - pluginConfig[configName] = null; - delete pluginConfig[configName]; - } - // Not save noUI if is not checked - if (configName === 'noUI' && !me.noUI) { - pluginConfig[configName] = null; - delete pluginConfig[configName]; - } - } - } + getValues () { + if (!this.isEnabled()) { + return null; + } - if (me.noUI) { - pluginConfig.noUI = me.noUI; - } - var json = { - configuration: {} + const pluginConfig = this.getPlugin().getConfig(); + const state = this.handler.getState(); + for (const key in state) { + if (key === 'hasSupportedProjections') { + continue; }; - json.configuration[me.bundleName] = { - conf: pluginConfig, - state: {} - }; - return json; - }, - /** - * Get extra options. - * @method @public getExtraOptions - * @param {Object} jQuery element toolContainer - * @return {Object} jQuery element template - */ - getExtraOptions: function (toolContainer) { - var me = this, - template = jQuery(me.templates.toolOptions).clone(), - loc = Oskari.getLocalization('coordinatetool', Oskari.getLang() || Oskari.getDefaultLanguage()), - labelNoUI = loc.display.publisher.noUI; - - var input = Oskari.clazz.create( - 'Oskari.userinterface.component.CheckboxInput' - ); - - if (me.toolConfig && me.toolConfig.supportedProjections) { - var inputTransform = Oskari.clazz.create( - 'Oskari.userinterface.component.CheckboxInput' - ); - inputTransform.setTitle(loc.display.publisher.showTransformationTools); - inputTransform.setHandler(function (checked) { - if (checked === 'on') { - me.supportedProjections = me.toolConfig.supportedProjections; - } else { - me.supportedProjections = null; - } - }); - - if (me.projectionTrasformationIsCheckedInModifyMode) { - inputTransform.setChecked(true); - me.supportedProjections = me.toolConfig.supportedProjections; - } - template.append(inputTransform.getElement()); + pluginConfig[key] = state[key]; + // Not save supportedProjections if is not checked + if (key === 'supportedProjections' && !state[key]) { + pluginConfig[key] = null; + delete pluginConfig[key]; + } + // Not save noUI if is not checked + if (key === 'noUI' && !state[key]) { + pluginConfig[key] = null; + delete pluginConfig[key]; } + } - input.setTitle(labelNoUI); - input.setHandler(function (checked) { - if (checked === 'on') { - me.noUI = true; - me.getPlugin().teardownUI(true); - // me.getPlugin().toggleIconVisibility(false); - } else { - me.noUI = null; - me.getPlugin().redrawUI(Oskari.util.isMobile()); - // me.getPlugin().toggleIconVisibility(true); - } - }); + const json = { + configuration: {} + }; + json.configuration[this.bundleName] = { + conf: pluginConfig, + state: {} + }; + return json; + } +} - if (me.noUiIsCheckedInModifyMode) { - input.setChecked(true); - me.noUI = true; - } - var inputEl = input.getElement(); - if (inputEl.style) { - inputEl.style.width = 'auto'; - } +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.CoordinateTool', + CoordinateTool, + { + protocol: ['Oskari.mapframework.publisher.Tool'] + } +); - template.append(inputEl); - return template; - } - }, { - 'extend': ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], - 'protocol': ['Oskari.mapframework.publisher.Tool'] - }); +export { CoordinateTool }; diff --git a/bundles/framework/coordinatetool/publisher/CoordinateToolComponent.jsx b/bundles/framework/coordinatetool/publisher/CoordinateToolComponent.jsx new file mode 100644 index 0000000000..cf5affe67c --- /dev/null +++ b/bundles/framework/coordinatetool/publisher/CoordinateToolComponent.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Message, Checkbox } from 'oskari-ui'; + +export const CoordinateToolComponent = ({ state, controller }) => { + const { noUI, hasSupportedProjections, supportedProjections } = state; + return <> + <Checkbox checked={noUI} onChange={ evt => controller.setNoUI(evt.target.checked) }> + <Message bundleKey={'coordinatetool'} messageKey={'display.publisher.noUI'}/> + </Checkbox> + { hasSupportedProjections && + <Checkbox checked={supportedProjections} onChange={ evt => controller.setSupportedProjections(evt.target.checked) }> + <Message bundleKey={'coordinatetool'} messageKey={'display.publisher.showTransformationTools'}/> + </Checkbox> + } + </>; +}; + +CoordinateToolComponent.propTypes = { + state: PropTypes.object, + controller: PropTypes.object +}; diff --git a/bundles/framework/coordinatetool/publisher/CoordinateToolHandler.js b/bundles/framework/coordinatetool/publisher/CoordinateToolHandler.js new file mode 100644 index 0000000000..507f354a61 --- /dev/null +++ b/bundles/framework/coordinatetool/publisher/CoordinateToolHandler.js @@ -0,0 +1,48 @@ +import { StateHandler, controllerMixin } from 'oskari-ui/util'; + +class UIHandler extends StateHandler { + constructor (tool) { + super(); + this.tool = tool; + this.setState({ + noUI: false, + hasSupportedProjections: !!this.tool?.toolConfig?.supportedProjections, + supportedProjections: this.tool?.toolConfig?.supportedProjections + }); + }; + + init (config) { + this.updateState({ + ...config, + noUI: config?.noUI, + hasSupportedProjections: !!config?.supportedProjections, + supportedProjections: config?.supportedProjections + }); + } + + setNoUI (value) { + this.updateState({ + noUI: value + }); + + if (value) { + this.tool.getPlugin().teardownUI(true); + } else { + this.tool.getPlugin().redrawUI(Oskari.util.isMobile()); + } + } + + setSupportedProjections (checked) { + const supportedProjections = checked && this.tool?.toolConfig?.supportedProjections ? this.tool?.toolConfig?.supportedProjections : null; + this.updateState({ + supportedProjections + }); + } +} + +const wrapped = controllerMixin(UIHandler, [ + 'setNoUI', + 'setSupportedProjections' +]); + +export { wrapped as CoordinateToolHandler }; diff --git a/packages/framework/bundle/coordinatetool/bundle.js b/packages/framework/bundle/coordinatetool/bundle.js index b960200d36..325ece510e 100755 --- a/packages/framework/bundle/coordinatetool/bundle.js +++ b/packages/framework/bundle/coordinatetool/bundle.js @@ -46,7 +46,7 @@ Oskari.clazz.define("Oskari.mapframework.bundle.coordinatetool.CoordinateToolBun },{ "type": "text/javascript", "src": "../../../../bundles/framework/coordinatetool/service/CoordinateToolService.js" - },{ + }, { "type": "text/javascript", "src": "../../../../bundles/framework/coordinatetool/publisher/CoordinateTool.js" }], From 80b19a82df7a4b3ca3c2363d23ad78b1532574ee Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 13 Nov 2024 11:20:57 +0200 Subject: [PATCH 105/181] increase select style specificity --- .../framework/timeseries/resources/css/timeseriesplayback.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/framework/timeseries/resources/css/timeseriesplayback.css b/bundles/framework/timeseries/resources/css/timeseriesplayback.css index 3797c644c1..c22b286678 100755 --- a/bundles/framework/timeseries/resources/css/timeseriesplayback.css +++ b/bundles/framework/timeseries/resources/css/timeseriesplayback.css @@ -15,7 +15,7 @@ display: inline-block; vertical-align: top; } -.timeseries-aux select { +.timeseries-aux .timeseries-menus select { width: 100%!important; height: 24px; padding-top: 0; From b0bec01eb9269bac2cd351b980bd7f04664135da Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 13 Nov 2024 11:21:58 +0200 Subject: [PATCH 106/181] fix min limit --- bundles/mapping/layerswipe/view/LayerSwipe.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/mapping/layerswipe/view/LayerSwipe.jsx b/bundles/mapping/layerswipe/view/LayerSwipe.jsx index ed67b0d53a..c6fc5ae2a4 100644 --- a/bundles/mapping/layerswipe/view/LayerSwipe.jsx +++ b/bundles/mapping/layerswipe/view/LayerSwipe.jsx @@ -96,7 +96,7 @@ export const LayerSwipe = ThemeConsumer(({ theme, position, mapSize, isMobile, c const margin = isMobile ? MARGIN.mobile : MARGIN.desktop; const width = SPLITTER_WIDTH + 2 * margin; - const limits = { min: -margin, max: mapSize.width - SPLITTER_WIDTH }; + const limits = { min: 0, max: mapSize.width - SPLITTER_WIDTH }; const style = { transform: `translate(${position - margin}px` }; const onEvent = useCallback(() => createDraggable(elementRef, position, limits, controller.setPosition, setDragging), [position, limits]); From ffd524cf484898f35b6b61fc02115518920c69f4 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 13 Nov 2024 11:22:33 +0200 Subject: [PATCH 107/181] increase mobile margin --- bundles/mapping/layerswipe/view/LayerSwipe.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/mapping/layerswipe/view/LayerSwipe.jsx b/bundles/mapping/layerswipe/view/LayerSwipe.jsx index c6fc5ae2a4..3fe8348c55 100644 --- a/bundles/mapping/layerswipe/view/LayerSwipe.jsx +++ b/bundles/mapping/layerswipe/view/LayerSwipe.jsx @@ -7,7 +7,7 @@ import { getHeaderTheme } from 'oskari-ui/theme/ThemeHelper'; export const SPLITTER_WIDTH = 5; export const MARGIN = { desktop: 2, - mobile: 5 + mobile: 10 }; const Mask = styled.div` position: absolute; From 8725daa7efd54d1b937fec0c914fc53157fd54a6 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Thu, 14 Nov 2024 13:15:10 +0200 Subject: [PATCH 108/181] reactify publisher toolbar --- .../publisher2/resources/locale/en.js | 1 - .../publisher2/resources/locale/fi.js | 1 - .../publisher2/resources/locale/fr.js | 1 - .../publisher2/resources/locale/is.js | 1 - .../publisher2/resources/locale/ru.js | 1 - .../publisher2/resources/locale/sv.js | 1 - .../framework/publisher2/tools/ToolbarTool.js | 185 ------------------ .../publisher/toolbar/ToolbarTool.js | 109 +++++++++++ .../toolbar/ToolbarToolComponent.jsx | 32 +++ .../publisher/toolbar/ToolbarToolHandler.js | 57 ++++++ bundles/mapping/mapmodule/publisher/tools.js | 4 +- .../mapping/mapmodule/resources/locale/en.js | 3 + .../mapping/mapmodule/resources/locale/fi.js | 4 + .../mapping/mapmodule/resources/locale/fr.js | 3 + .../mapping/mapmodule/resources/locale/is.js | 3 + .../mapping/mapmodule/resources/locale/ru.js | 3 + .../mapping/mapmodule/resources/locale/sv.js | 3 + .../framework/bundle/publisher2/bundle.js | 4 - 18 files changed, 220 insertions(+), 196 deletions(-) delete mode 100755 bundles/framework/publisher2/tools/ToolbarTool.js create mode 100644 bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js create mode 100644 bundles/mapping/mapmodule/publisher/toolbar/ToolbarToolComponent.jsx create mode 100644 bundles/mapping/mapmodule/publisher/toolbar/ToolbarToolHandler.js diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 5c223868b6..be588ba609 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -68,7 +68,6 @@ Oskari.registerLocalization( "TimeseriesControlPlugin": "Time series player", "FeaturedataPlugin": "Feature data", "GetInfoPlugin": "Feature query tool", - "PublisherToolbarPlugin": "Map tools", "selectDrawLayer": "Select map layer", "LayerSelectionPlugin": "Map layers menu", "CoordinateToolPlugin": "Coordinate tool", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index f8d499e86f..6d70e30b5d 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -68,7 +68,6 @@ Oskari.registerLocalization( "TimeseriesControlPlugin": "Aikasarjatoistin", "FeaturedataPlugin": "Kohdetietotaulukko", "GetInfoPlugin": "Kohdetietojen kyselytyökalu", - "PublisherToolbarPlugin": "Karttatyökalut", "selectDrawLayer": "Valitse tallennustaso", "LayerSelectionPlugin": "Karttatasovalikko", "CoordinateToolPlugin": "Koordinaattityökalu", diff --git a/bundles/framework/publisher2/resources/locale/fr.js b/bundles/framework/publisher2/resources/locale/fr.js index 966c5cecec..e05fb6f997 100644 --- a/bundles/framework/publisher2/resources/locale/fr.js +++ b/bundles/framework/publisher2/resources/locale/fr.js @@ -65,7 +65,6 @@ Oskari.registerLocalization( "TimeseriesControlPlugin": "Lecteur chronologique", "FeaturedataPlugin": "Données de fonctionnalité", "GetInfoPlugin": "Outil d'interrogation de fonctionnalité", - "PublisherToolbarPlugin": "Outils cartographiques", "selectDrawLayer": "Sélectionner la couche cartographique", "LayerSelectionPlugin": "Menu des couches cartographiques", "CoordinateToolPlugin": "Coordonner l'outil", diff --git a/bundles/framework/publisher2/resources/locale/is.js b/bundles/framework/publisher2/resources/locale/is.js index f993103606..f98853d500 100755 --- a/bundles/framework/publisher2/resources/locale/is.js +++ b/bundles/framework/publisher2/resources/locale/is.js @@ -52,7 +52,6 @@ Oskari.registerLocalization( "tooltip": "Velja tiltæk kortatól. Athugaðu staðsetningu við forskoðun korts.", "FeaturedataPlugin": "Fitjugögn", "GetInfoPlugin": "Fyrirspurnatól fyrir fitjur", - "PublisherToolbarPlugin": "Kortatól", "selectDrawLayer": "Velja kortalag", "LayerSelectionPlugin": "Valmynd fyrir kortalög", "CoordinateToolPlugin": "Hnitatól", diff --git a/bundles/framework/publisher2/resources/locale/ru.js b/bundles/framework/publisher2/resources/locale/ru.js index ab2109beec..59b40c5896 100644 --- a/bundles/framework/publisher2/resources/locale/ru.js +++ b/bundles/framework/publisher2/resources/locale/ru.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "TimeseriesControlPlugin": "Временные ряды", "FeaturedataPlugin": "Данные объекта", "GetInfoPlugin": "Инструмент запроса объектов", - "PublisherToolbarPlugin": "Инструмент карты", "selectDrawLayer": "Выбрать слой карты", "LayerSelectionPlugin": "Меню слоев карты", "CoordinateToolPlugin": "Инструмент координат", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 73b7396fb4..aab2272b3b 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -68,7 +68,6 @@ Oskari.registerLocalization( "TimeseriesControlPlugin": "Tidseriespelare", "FeaturedataPlugin": "Objektuppgifter", "GetInfoPlugin": "Frågverktyg för visande av objektuppgifter", - "PublisherToolbarPlugin": "Kartverktyg", "selectDrawLayer": "Välj lager för nya funktioner", "LayerSelectionPlugin": "Kartlagermeny", "CoordinateToolPlugin": "Koordinatverktyg", diff --git a/bundles/framework/publisher2/tools/ToolbarTool.js b/bundles/framework/publisher2/tools/ToolbarTool.js deleted file mode 100755 index 464c2ed9aa..0000000000 --- a/bundles/framework/publisher2/tools/ToolbarTool.js +++ /dev/null @@ -1,185 +0,0 @@ -Oskari.clazz.define('Oskari.mapframework.publisher.tool.ToolbarTool', - function () { - // checkboxes in ui - this.selectedOptionsUi = { - history: true, - measureline: true, - measurearea: true - }; - }, { - index: 3, - - templates: { - 'toolOptions': '<div class="tool-options"></div>', - 'toolOption': '<div class="tool-option"><label><input type="checkbox" /></label></div>' - }, - - /** - * Initialise tool - * @method init - */ - init: function (data) { - this.selectedTools = {}; - const plugin = this.findPluginFromInitData(data); - if (!plugin) { - return; - } - this.storePluginConf(plugin.config); - // tools on map - this.selectedTools = { - history_back: true, - history_forward: true, - measureline: true, - measurearea: true - }; - // preselect tools based on init data - let buttons = plugin.config?.buttons; - if (buttons?.length) { - // restore saved settings... yes it's a messy state for the tool... - Object.keys(this.selectedOptionsUi).forEach(toolName => { - this.selectedOptionsUi[toolName] = false; - }); - Object.keys(this.selectedTools).forEach(toolName => { - this.selectedTools[toolName] = false; - }); - buttons.forEach(toolName => { - if (toolName.startsWith('history')) { - this.selectedOptionsUi.history = true; - this.selectedTools.history_back = true; - this.selectedTools.history_forward = true; - } else { - this.selectedOptionsUi[toolName] = true; - this.selectedTools[toolName] = true; - } - }); - } - - this.setEnabled(this._hasActiveTools()); - }, - _setEnabledImpl: function (enabled) { - if (enabled) { - // if there are no selected tools in configuration, select them all when tools are selected - Object.keys(this.selectedOptionsUi).forEach(toolName => { - this.__changeToolStatus(toolName, !!this.selectedOptionsUi[toolName]); - }); - } - }, - /** - * Get tool object. - * @method getTool - * - * @returns {Object} tool description - */ - getTool: function () { - return { - id: 'Oskari.mapframework.bundle.mapmodule.plugin.PublisherToolbarPlugin', - title: 'PublisherToolbarPlugin', - config: { - ...(this.state.pluginConfig || {}), - 'toolbarId': 'PublisherToolbar', - buttons: this.state.pluginConfig?.buttons || [] - } - }; - }, - /** - * Get values. - * @method getValues - * @public - * - * @returns {Object} tool value object - */ - getValues: function () { - var me = this, - buttons = []; - if (!this.isEnabled()) { - return null; - } - - for (var toolName in me.selectedTools) { - if (me.selectedTools.hasOwnProperty(toolName) && me.selectedTools[toolName]) { - buttons.push(toolName); - } - } - - var pluginConfig = me.getPlugin().getConfig(); - pluginConfig.buttons = buttons; - - var retValue = { - configuration: { - mapfull: { - conf: { - plugins: [{ id: this.getTool().id, config: pluginConfig }] - } - } - } - }; - - // we want toolbar always with no default tools - retValue.configuration.toolbar = { conf: { 'history': false, 'basictools': false, 'viewtools': false } }; - return retValue; - }, - /** - * Get extra options. - * @method getExtraOptions - * @public - * - * @returns {Object} jQuery element - */ - getExtraOptions: function (toolContainer) { - const localization = this.__loc; - const optionsContainer = jQuery(this.templates.toolOptions).clone(); - Object.keys(this.selectedOptionsUi).forEach(toolName => { - var selectTool = jQuery(this.templates.toolOption).clone(); - selectTool.find('label') - .attr('for', 'tool-opt-' + toolName) - .append(localization.toolbarToolNames[toolName]); - optionsContainer.append(selectTool); - // toggle tool - selectTool.find('input') - .attr('id', 'tool-opt-' + toolName) - .prop('checked', !!this.selectedOptionsUi[toolName]) - .on('change', () => { - var toolState = this.selectedOptionsUi[toolName]; - this.__changeToolStatus(toolName, !toolState); - }); - }); - toolContainer.find('.extraOptions').append(optionsContainer); - this.optionsContainer = optionsContainer; - }, - __changeToolStatus: function (toolName, isActive) { - if (typeof this.selectedOptionsUi[toolName] === 'boolean') { - this.selectedOptionsUi[toolName] = isActive; - } - if (!this._hasActiveTools()) { - // last tool removed -> enable last unselected - this.__changeToolStatus(toolName, true); - return; - } - if (this.optionsContainer) { - this.optionsContainer - .find('input#tool-opt-' + toolName) - .prop('checked', isActive); - } - if (toolName === 'history') { - this.__changeToolStatus('history_back', isActive); - this.__changeToolStatus('history_forward', isActive); - return; - } - // toggle checkbox in UI to reflect state - this.selectedTools[toolName] = isActive; - const plugin = this.getPlugin(); - if (isActive) { - plugin.addToolButton(toolName); - } else { - plugin.removeToolButton(toolName); - } - }, - - _hasActiveTools: function () { - return Object.keys(this.selectedOptionsUi) - .some(toolName => this.selectedOptionsUi[toolName] === true); - } - }, { - 'extend': ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], - 'protocol': ['Oskari.mapframework.publisher.Tool'] - }); diff --git a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js new file mode 100644 index 0000000000..3b4f5f271a --- /dev/null +++ b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js @@ -0,0 +1,109 @@ +import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/AbstractPublisherTool'; +import { ToolbarToolComponent } from './ToolbarToolComponent'; +import { ToolbarToolHandler } from './ToolbarToolHandler'; +class ToolbarTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + this.handler = new ToolbarToolHandler(this); + } + + init (data) { + super.init(data); + const buttons = this.state?.pluginConfig?.buttons; + if (!buttons) { + return; + } + + const handlerConfig = { + history_forward: false, + history_back: false, + measureline: false, + measurearea: false + }; + + const plugin = this.getPlugin(); + buttons.forEach((key) => { handlerConfig[key] = true; plugin.addToolButton(key); }); + this.handler.init(handlerConfig); + } + + getTool () { + const state = this.handler.getState(); + const buttons = Object.keys(state).filter((key) => !!state[key]); + return { + id: 'Oskari.mapframework.bundle.mapmodule.plugin.PublisherToolbarPlugin', + title: Oskari.getMsg('MapModule', 'publisherTools.PublisherToolbarPlugin.toolLabel'), + config: { + ...(this.state.pluginConfig || {}), + toolbarId: 'PublisherToolbar', + buttons: buttons || {} + } + }; + } + + getComponent () { + return { + component: ToolbarToolComponent, + handler: this.handler + }; + } + + /** + * Get values. + * @method getValues + * @public + * + * @returns {Object} tool value object + */ + getValues () { + if (!this.isEnabled()) { + return null; + } + + const buttons = []; + const state = this.handler.getState(); + for (const toolName in state) { + if (state[toolName]) { + buttons.push(toolName); + } + } + + const pluginConfig = this.getPlugin().getConfig(); + pluginConfig.buttons = buttons; + + const retValue = { + configuration: { + mapfull: { + conf: { + plugins: [{ id: this.getTool().id, config: pluginConfig }] + } + }, + toolbar: { + conf: { + history: false, + basictools: false, + viewtools: false + } + } + } + }; + return retValue; + } + + hasActiveTools () { + const state = this.handler.getState(); + return Object.keys(state) + .some(toolName => state[toolName] === true); + } +} + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.ToolbarTool', + ToolbarTool, + { + protocol: ['Oskari.mapframework.publisher.Tool'] + } +); + +export { ToolbarTool }; diff --git a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarToolComponent.jsx b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarToolComponent.jsx new file mode 100644 index 0000000000..542d97dc2f --- /dev/null +++ b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarToolComponent.jsx @@ -0,0 +1,32 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Message, Checkbox } from 'oskari-ui'; +import styled from 'styled-components'; + +const Col = styled('div')` + display: flex; + flex-direction: column; +`; + +export const ToolbarToolComponent = ({ state, controller }) => { + // eslint-disable-next-line camelcase + const { history_forward, history_back, measureline, measurearea } = state; + // eslint-disable-next-line camelcase + const history = (history_back || history_forward); + return <Col> + <Checkbox checked={history} onChange={evt => controller.historySelectionChanged(evt.target.checked)}> + <Message bundleKey={'Publisher2'} messageKey={'BasicView.maptools.toolbarToolNames.history'}/> + </Checkbox> + <Checkbox checked={measureline} onChange={evt => controller.selectionChanged('measureline', evt.target.checked)}> + <Message bundleKey={'Publisher2'} messageKey={'BasicView.maptools.toolbarToolNames.measureline'}/> + </Checkbox> + <Checkbox checked={measurearea} onChange={evt => controller.selectionChanged('measurearea', evt.target.checked)}> + <Message bundleKey={'Publisher2'} messageKey={'BasicView.maptools.toolbarToolNames.measurearea'}/> + </Checkbox> + </Col>; +}; + +ToolbarToolComponent.propTypes = { + state: PropTypes.object, + controller: PropTypes.object +}; diff --git a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarToolHandler.js b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarToolHandler.js new file mode 100644 index 0000000000..1ce63fed1f --- /dev/null +++ b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarToolHandler.js @@ -0,0 +1,57 @@ +import { StateHandler, controllerMixin } from 'oskari-ui/util'; + +class UIHandler extends StateHandler { + constructor (tool) { + super(); + this.tool = tool; + this.setState({ + history_back: false, + history_forward: false, + measureline: false, + measurearea: false + }); + }; + + init (config) { + this.setState({ + ...config + }); + } + + historySelectionChanged (checked) { + const newState = { + history_forward: checked, + history_back: checked + }; + this.updateState(newState); + + const plugin = this.tool?.getPlugin(); + if (checked) { + plugin.addToolButton('history_forward'); + plugin.addToolButton('history_back'); + } else { + plugin.removeToolButton('history_forward'); + plugin.removeToolButton('history_back'); + } + } + + selectionChanged (selection, checked) { + const newState = {}; + newState[selection] = checked; + this.updateState(newState); + + const plugin = this.tool?.getPlugin(); + if (checked) { + plugin.addToolButton(selection); + } else { + plugin.removeToolButton(selection); + } + } +}; + +const wrapped = controllerMixin(UIHandler, [ + 'selectionChanged', + 'historySelectionChanged' +]); + +export { wrapped as ToolbarToolHandler }; diff --git a/bundles/mapping/mapmodule/publisher/tools.js b/bundles/mapping/mapmodule/publisher/tools.js index 4c99d4ee05..6bc228f1a2 100644 --- a/bundles/mapping/mapmodule/publisher/tools.js +++ b/bundles/mapping/mapmodule/publisher/tools.js @@ -11,6 +11,7 @@ import { CrosshairTool } from './crosshair/CrosshairTool'; import { MapLayerListTool } from './layers/MapLayerListTool'; import { SearchTool } from './search/SearchTool'; import { ControlsTool } from './controls/ControlsTool'; +import { ToolbarTool } from './toolbar/ToolbarTool'; export { LogoTool, @@ -22,5 +23,6 @@ export { CrosshairTool, MapLayerListTool, SearchTool, - ControlsTool + ControlsTool, + ToolbarTool }; diff --git a/bundles/mapping/mapmodule/resources/locale/en.js b/bundles/mapping/mapmodule/resources/locale/en.js index 7c40266c40..402400f9d2 100755 --- a/bundles/mapping/mapmodule/resources/locale/en.js +++ b/bundles/mapping/mapmodule/resources/locale/en.js @@ -200,6 +200,9 @@ Oskari.registerLocalization( }, "ControlsPlugin": { "toolLabel": "Pan by mouse" + }, + "PublisherToolbarPlugin": { + "toolLabel": "Map tools" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/fi.js b/bundles/mapping/mapmodule/resources/locale/fi.js index 0e319c388c..b35c2ab32f 100755 --- a/bundles/mapping/mapmodule/resources/locale/fi.js +++ b/bundles/mapping/mapmodule/resources/locale/fi.js @@ -200,7 +200,11 @@ Oskari.registerLocalization( }, "ControlsPlugin": { "toolLabel": "Kartan liikuttaminen hiirellä raahaamalla", + }, + "PublisherToolbarPlugin": { + "toolLabel": "Karttatyökalut" } + } } }); \ No newline at end of file diff --git a/bundles/mapping/mapmodule/resources/locale/fr.js b/bundles/mapping/mapmodule/resources/locale/fr.js index a59988b19c..bfafe88bc1 100755 --- a/bundles/mapping/mapmodule/resources/locale/fr.js +++ b/bundles/mapping/mapmodule/resources/locale/fr.js @@ -160,6 +160,9 @@ Oskari.registerLocalization( }, "ControlsPlugin": { "toolLabel": "Panoramiser avec la souris" + }, + "PublisherToolbarPlugin": { + "toolLabel": "Outils cartographiques" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/is.js b/bundles/mapping/mapmodule/resources/locale/is.js index 8bd1294644..845cc72978 100755 --- a/bundles/mapping/mapmodule/resources/locale/is.js +++ b/bundles/mapping/mapmodule/resources/locale/is.js @@ -154,6 +154,9 @@ Oskari.registerLocalization( }, "ControlsPlugin": { "toolLabel": "Hliðra með mús" + }, + "PublisherToolbarPlugin": { + "toolLabel": "Kortatól" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/ru.js b/bundles/mapping/mapmodule/resources/locale/ru.js index baf00222a7..7bdb473663 100644 --- a/bundles/mapping/mapmodule/resources/locale/ru.js +++ b/bundles/mapping/mapmodule/resources/locale/ru.js @@ -155,6 +155,9 @@ Oskari.registerLocalization( }, "ControlsPlugin": { "toolLabel": "Панорамирование мышью" + }, + "PublisherToolbarPlugin": { + "toolLabel": "Инструмент карты" } } } diff --git a/bundles/mapping/mapmodule/resources/locale/sv.js b/bundles/mapping/mapmodule/resources/locale/sv.js index 0af54e6f1e..a4897e9309 100755 --- a/bundles/mapping/mapmodule/resources/locale/sv.js +++ b/bundles/mapping/mapmodule/resources/locale/sv.js @@ -195,6 +195,9 @@ Oskari.registerLocalization( }, "ControlsPlugin": { "toolLabel": "Flytta kartvyn med musen" + }, + "PublisherToolbarPlugin": { + "toolLabel": "Kartverktyg" } } } diff --git a/packages/framework/bundle/publisher2/bundle.js b/packages/framework/bundle/publisher2/bundle.js index 418ef4a9f1..765922b5ab 100755 --- a/packages/framework/bundle/publisher2/bundle.js +++ b/packages/framework/bundle/publisher2/bundle.js @@ -93,10 +93,6 @@ Oskari.clazz.define("Oskari.mapframework.bundle.publisher2.PublisherBundle", fun "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/view/PanelRpc.js" }, - { - "type": "text/javascript", - "src": "../../../../bundles/framework/publisher2/tools/ToolbarTool.js" - }, { "type": "text/javascript", "src": "../../../../bundles/framework/publisher2/tools/GetInfoTool.js" From f7b98cfc770a30a13ec887345dd66bbfed9a7bc4 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Thu, 14 Nov 2024 16:49:11 +0200 Subject: [PATCH 109/181] MapRotator reactification --- .../maprotator/publisher/MapRotator.js | 187 +++++++----------- .../publisher/MapRotatorToolComponent.jsx | 17 ++ .../publisher/MapRotatorToolHandler.js | 39 ++++ 3 files changed, 132 insertions(+), 111 deletions(-) create mode 100644 bundles/mapping/maprotator/publisher/MapRotatorToolComponent.jsx create mode 100644 bundles/mapping/maprotator/publisher/MapRotatorToolHandler.js diff --git a/bundles/mapping/maprotator/publisher/MapRotator.js b/bundles/mapping/maprotator/publisher/MapRotator.js index 5e46c9604b..b127cbcca7 100755 --- a/bundles/mapping/maprotator/publisher/MapRotator.js +++ b/bundles/mapping/maprotator/publisher/MapRotator.js @@ -1,114 +1,79 @@ -Oskari.clazz.define('Oskari.mapping.publisher.tool.MapRotator', - function () { - }, { - index: 500, - lefthanded: 'top left', - righthanded: 'top right', - templates: { - 'toolOptions': '<div class="tool-options"></div>' - }, - /** - * Get tool object. - * @method getTool - * - * @returns {Object} tool description - */ - getTool: function () { - return { - id: 'Oskari.mapping.maprotator.MapRotatorPlugin', - title: 'MapRotator', - config: this.state.pluginConfig || {} - }; - }, - isDisplayed: function () { - // shouldn't be shown if bundle is not started - // otherwise results in js errors - return !!this.getMapRotatorInstance(); - }, - getMapRotatorInstance: function () { - return this.getSandbox().findRegisteredModuleInstance(this.bundleName); - }, - // Key in view config non-map-module-plugin tools (for returning the state when modifying an existing published map). - bundleName: 'maprotator', - /** - * Initialise tool - * @method init - */ - init: function (data) { - var bundleData = data && data.configuration[this.bundleName]; - if (!bundleData) { - return; - } - var conf = bundleData.conf || {}; - this.storePluginConf(conf); - this.storePluginState(bundleData.state); - this.setEnabled(true); - }, - storePluginState: function (state) { - this.state.pluginState = state || {}; - }, - _setEnabledImpl: function (enabled) { - if (enabled && this.state.pluginState?.degrees) { - this.getPlugin().setRotation(this.state.pluginState?.degrees); - } else { - this.getMapmodule().getMap().getView().setRotation(0); - } - }, - /** - * Get values. - * @method getValues - * @public - * - * @returns {Object} tool value object - */ - getValues: function () { - if (!this.isEnabled()) { - return null; +import { AbstractPublisherTool } from '../../../framework/publisher2/tools/AbstractPublisherTool'; +import { MapRotatorToolHandler } from './MapRotatorToolHandler'; +import { MapRotatorToolComponent } from './MapRotatorToolComponent'; + +class MapRotatorTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.bundleName = 'maprotator'; + this.group = 'additional'; + this.handler = new MapRotatorToolHandler(this); + } + + init (data) { + if (!data || !data.configuration[this.bundleName]) { + // new publication and no saved config but ther is a toolconfig -> init with that. + if (this.toolConfig) { + this.handler.init(this.toolConfig); } - var pluginConfig = this.getPlugin().getConfig(); - var json = { - configuration: {} - }; - json.configuration[this.bundleName] = { - conf: pluginConfig, - state: this.getMapRotatorInstance().getState() - }; - return json; - }, - /** - * Get extra options. - * @method @public getExtraOptions - * @param {Object} jQuery element toolContainer - * @return {Object} jQuery element template - */ - getExtraOptions: function (toolContainer) { - var me = this; - var template = jQuery(me.templates.toolOptions).clone(); - var loc = Oskari.getLocalization('maprotator', Oskari.getLang()); - var labelNoUI = loc.display.publisher.noUI; - var input = Oskari.clazz.create( - 'Oskari.userinterface.component.CheckboxInput' - ); + return; + } + + // saved configuration -> restore. + const conf = data.configuration[this.bundleName].conf || {}; + this.handler.init(conf); + this.storePluginConf(conf); + this.setEnabled(true); + } - input.setTitle(labelNoUI); - input.setHandler((checked) => { - const plugin = this.getPlugin(); - if (!plugin) { - return; - } - plugin.setConfig({ - ...plugin.getConfig(), - noUI: checked === 'on' - }); - plugin.refresh(); - }); - // initial value from pluginconfig that we get when opening the publisher - input.setChecked(!!this.state.pluginConfig?.noUI); - var inputEl = input.getElement(); - template.append(inputEl); - return template; + isDisplayed () { + // shouldn't be shown if bundle is not started + // otherwise results in js errors + return !!this.getMapRotatorInstance(); + } + + getMapRotatorInstance () { + return this.getSandbox().findRegisteredModuleInstance(this.bundleName); + } + + getTool () { + return { + id: 'Oskari.mapping.maprotator.MapRotatorPlugin', + title: Oskari.getMsg('maprotator', 'title'), + config: this.state.pluginConfig || {} + }; + } + + getComponent () { + return { + component: MapRotatorToolComponent, + handler: this.handler + }; + } + + getValues () { + if (!this.isEnabled()) { + return null; } - }, { - 'extend': ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], - 'protocol': ['Oskari.mapframework.publisher.Tool'] - }); + const pluginConfig = this.state.pluginConfig; + const json = { + configuration: {} + }; + json.configuration[this.bundleName] = { + conf: pluginConfig, + state: this.getMapRotatorInstance().getState() + }; + return json; + } +} + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.MapRotatorTool', + MapRotatorTool, + { + protocol: ['Oskari.mapframework.publisher.Tool'] + } +); + +export { MapRotatorTool }; diff --git a/bundles/mapping/maprotator/publisher/MapRotatorToolComponent.jsx b/bundles/mapping/maprotator/publisher/MapRotatorToolComponent.jsx new file mode 100644 index 0000000000..8fc2df8bf8 --- /dev/null +++ b/bundles/mapping/maprotator/publisher/MapRotatorToolComponent.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Message, Checkbox } from 'oskari-ui'; + +export const MapRotatorToolComponent = ({ state, controller }) => { + const { noUI } = state; + return ( + <Checkbox checked={noUI} onChange={ evt => controller.setNoUI(evt.target.checked) }> + <Message bundleKey={'maprotator'} messageKey={'display.publisher.noUI'}/> + </Checkbox> + ); +}; + +MapRotatorToolComponent.propTypes = { + state: PropTypes.object, + controller: PropTypes.object +}; diff --git a/bundles/mapping/maprotator/publisher/MapRotatorToolHandler.js b/bundles/mapping/maprotator/publisher/MapRotatorToolHandler.js new file mode 100644 index 0000000000..47349a073c --- /dev/null +++ b/bundles/mapping/maprotator/publisher/MapRotatorToolHandler.js @@ -0,0 +1,39 @@ +import { StateHandler, controllerMixin } from 'oskari-ui/util'; + +class UIHandler extends StateHandler { + constructor (tool) { + super(); + this.tool = tool; + this.setState({ + noUI: false + }); + }; + + init (config) { + this.updateState({ + noUI: config?.noUI + }); + } + + setNoUI (value) { + this.updateState({ + noUI: value + }); + + const plugin = this.tool?.getPlugin(); + if (!plugin) { + return; + } + plugin.setConfig({ + ...plugin.getConfig(), + noUI: value + }); + plugin.refresh(); + } +} + +const wrapped = controllerMixin(UIHandler, [ + 'setNoUI' +]); + +export { wrapped as MapRotatorToolHandler }; From 54cddfc537b80d96673df29b152ac993921ffaf1 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Fri, 15 Nov 2024 13:32:17 +0200 Subject: [PATCH 110/181] reactify featuredata tool --- .../featuredata/publisher/FeaturedataTool.js | 138 ++++++++++-------- 1 file changed, 77 insertions(+), 61 deletions(-) diff --git a/bundles/framework/featuredata/publisher/FeaturedataTool.js b/bundles/framework/featuredata/publisher/FeaturedataTool.js index 1f07304b70..3a7b1ba973 100644 --- a/bundles/framework/featuredata/publisher/FeaturedataTool.js +++ b/bundles/framework/featuredata/publisher/FeaturedataTool.js @@ -1,70 +1,86 @@ -Oskari.clazz.define('Oskari.mapframework.publisher.tool.FeaturedataTool', - function () { - }, { - index: 9, - // Disabled for now, need to fix config reading first allowedLocations: ['top left', 'top right', 'bottom left', 'bottom right'], - lefthanded: 'top right', - righthanded: 'top right', - /** - * Get tool object. - * @method getTool - * @private - * - * @returns {Object} tool - */ - getTool: function () { - return { - id: 'Oskari.mapframework.bundle.featuredata.plugin.FeaturedataPlugin', - title: 'FeaturedataPlugin', - config: this.state.pluginConfig || {} - }; - }, - // Key in view config non-map-module-plugin tools (for returning the state when modifying an existing published map). - bundleName: 'featuredata', +import { AbstractPublisherTool } from '../../publisher2/tools/AbstractPublisherTool'; - /** - * Initialise tool - * @method init - */ - init: function (data) { - const { configuration = {} } = data; - if (configuration[this.bundleName]) { - this.storePluginConf(configuration[this.bundleName].conf); - // even if we have the config, we don't want to enable the tool if its not shown - // if we enable it the plugin won't show and everything looks ok, but getValues() will - // still return a non-null value which makes featuredata bundle to be - // started on the embedded map even if it's not used - this.setEnabled(this.isDisplayed(data)); - } - }, - /** +class FeaturedataTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + this.bundleName = 'featuredata'; + } + + /** + * Initialise tool + * @method init + */ + init (data) { + const { configuration = {} } = data; + if (configuration[this.bundleName]) { + this.storePluginConf(configuration[this.bundleName].conf); + // even if we have the config, we don't want to enable the tool if its not shown + // if we enable it the plugin won't show and everything looks ok, but getValues() will + // still return a non-null value which makes featuredata bundle to be + // started on the embedded map even if it's not used + this.setEnabled(this.isDisplayed(data)); + } + } + + getComponent () { + return { + component: null, + handler: null + }; + } + + /** + * Get tool object. + * @method getTool + * + * @returns {Object} tool description + */ + getTool () { + return { + id: 'Oskari.mapframework.bundle.featuredata.plugin.FeaturedataPlugin', + title: Oskari.getMsg('Publisher2', 'BasicView.maptools.FeaturedataPlugin'), + config: this.state.pluginConfig || {} + }; + } + + /** * Get values. * @method getValues * @public * * @returns {Object} tool value object */ - getValues: function () { - if (!this.isEnabled()) { - return null; - } - const pluginConfig = this.getPlugin().getConfig(); - const json = { - configuration: {} - }; - json.configuration[this.bundleName] = { - conf: pluginConfig, - state: {} - }; - return json; - }, - isDisplayed: function () { - // Check if selected layers include wfs layers - return this.getSandbox() - .findAllSelectedMapLayers() - .some(l => l.hasFeatureData()); + getValues () { + if (!this.isEnabled()) { + return null; } - }, { - extend: ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], + const pluginConfig = this.getPlugin().getConfig(); + const json = { + configuration: {} + }; + json.configuration[this.bundleName] = { + conf: pluginConfig, + state: {} + }; + return json; + } + + isDisplayed () { + // Check if selected layers include wfs layers + return this.getSandbox() + .findAllSelectedMapLayers() + .some(l => l.hasFeatureData()); + } +} + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.FeaturedataTool', + FeaturedataTool, + { protocol: ['Oskari.mapframework.publisher.Tool'] - }); + } +); + +export { FeaturedataTool }; From 713c70db887fd6e47f7a05f54e348e3e6501ad5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Fri, 15 Nov 2024 13:44:07 +0200 Subject: [PATCH 111/181] Fix getting url for preloading --- bundles/framework/timeseries/WMSAnimator.js | 54 ++++++++++--------- .../plugin/wmslayer/OskariImageWMS.js | 3 +- .../plugin/wmslayer/WmsLayerPlugin.ol.js | 2 + 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/bundles/framework/timeseries/WMSAnimator.js b/bundles/framework/timeseries/WMSAnimator.js index e75d0864a5..1b61602483 100644 --- a/bundles/framework/timeseries/WMSAnimator.js +++ b/bundles/framework/timeseries/WMSAnimator.js @@ -126,27 +126,29 @@ Oskari.clazz.define('Oskari.mapframework.bundle.timeseries.WMSAnimator', * @param {function} doneCallback callback that will be called after new time has been loaded */ requestNewTime: function (newTime, nextTime, doneCallback) { - var me = this; + const me = this; this._currentTime = newTime; - var requestBuilder = Oskari.requestBuilder('MapModulePlugin.MapLayerUpdateRequest'); - if (requestBuilder) { - this._isLoading = true; - this._doneCallback = doneCallback; - if (nextTime) { - this._isBuffering = true; - this._bufferImages(this._mapModule.getLayerTileUrls(this._layer.getId()), nextTime, function (success) { - me._isBuffering = false; - me._resolveWait(); - }); - } - const layerParams = this._layer.getParams(); - layerParams.time = newTime; - var request = requestBuilder(this._layer.getId(), true, { 'TIME': newTime }); - this._sandbox.request(this, request); - if (!nextTime && this._doneCallback) { - this._doneCallback(); - this._doneCallback = null; - } + const requestBuilder = Oskari.requestBuilder('MapModulePlugin.MapLayerUpdateRequest'); + if (!requestBuilder) { + Oskari.log('WMSAnimator').warn('MapLayerUpdateRequest not available'); + return; + } + this._isLoading = true; + this._doneCallback = doneCallback; + if (nextTime) { + this._isBuffering = true; + this._bufferImages(this._mapModule.getLayerTileUrls(this._layer.getId()), nextTime, function (success) { + me._isBuffering = false; + me._resolveWait(); + }); + } + const layerParams = this._layer.getParams(); + layerParams.time = newTime; + const request = requestBuilder(this._layer.getId(), true, { TIME: newTime }); + this._sandbox.request(this, request); + if (!nextTime && this._doneCallback) { + this._doneCallback(); + this._doneCallback = null; } }, /** @@ -159,18 +161,22 @@ Oskari.clazz.define('Oskari.mapframework.bundle.timeseries.WMSAnimator', */ _bufferImages: function (urls, nextTime, callback) { /* eslint-disable n/no-callback-literal */ - var imgCount = urls.length; + let imgCount = urls.length; if (imgCount === 0) { callback(true); return; } - var aborted = false; - var timeout = setTimeout(function () { + let aborted = false; + const timeout = setTimeout(function () { aborted = true; callback(false); }, 5000); urls.forEach(function (url) { - var image = document.createElement('img'); + if (!url) { + Oskari.log('WMSAnimator').warn('Image preloading didnt receive an URL to preload'); + return; + } + const image = document.createElement('img'); image.onload = function () { if (aborted) { return; diff --git a/bundles/mapping/mapmodule/plugin/wmslayer/OskariImageWMS.js b/bundles/mapping/mapmodule/plugin/wmslayer/OskariImageWMS.js index 7128e47d47..5c5af91c60 100644 --- a/bundles/mapping/mapmodule/plugin/wmslayer/OskariImageWMS.js +++ b/bundles/mapping/mapmodule/plugin/wmslayer/OskariImageWMS.js @@ -5,6 +5,7 @@ export class OskariImageWMS extends olSourceImageWMS { * Return currently shown image url */ getImageUrl () { - return this.image_.src_; + // WMSLayerPlugin sets up the load function that injects this value to image on updateLayerParams() + return this.image?._oskariGetMapUrl; } } diff --git a/bundles/mapping/mapmodule/plugin/wmslayer/WmsLayerPlugin.ol.js b/bundles/mapping/mapmodule/plugin/wmslayer/WmsLayerPlugin.ol.js index fd56cfe5b9..31051035d4 100644 --- a/bundles/mapping/mapmodule/plugin/wmslayer/WmsLayerPlugin.ol.js +++ b/bundles/mapping/mapmodule/plugin/wmslayer/WmsLayerPlugin.ol.js @@ -244,6 +244,7 @@ Oskari.clazz.define('Oskari.mapframework.mapmodule.WmsLayerPlugin', if (typeof layerSource.getTileLoadFunction === 'function') { var originalTileLoadFunction = new OskariTileWMS().getTileLoadFunction(); layerSource.setTileLoadFunction(function (image, src) { + image._oskariGetMapUrl = src; if (src.length >= 2048) { me._imagePostFunction(image, src, proxyUrl); } else { @@ -255,6 +256,7 @@ Oskari.clazz.define('Oskari.mapframework.mapmodule.WmsLayerPlugin', else if (typeof layerSource.getImageLoadFunction === 'function') { var originalImageLoadFunction = new OskariImageWMS().getImageLoadFunction(); layerSource.setImageLoadFunction(function (image, src) { + image._oskariGetMapUrl = src; if (src.length >= 2048) { me._imagePostFunction(image, src, proxyUrl); } else { From 77c4756be7a5e730833d2689e430313c14bf3a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Fri, 15 Nov 2024 18:34:14 +0200 Subject: [PATCH 112/181] Add notes about dependency updates --- UpdatingDependencies.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 UpdatingDependencies.md diff --git a/UpdatingDependencies.md b/UpdatingDependencies.md new file mode 100644 index 0000000000..8822445ae2 --- /dev/null +++ b/UpdatingDependencies.md @@ -0,0 +1,23 @@ +# Updating dependencies + +List of some recognized fragile points on the code that could break when updating dependencies. + +## OpenLayers + +See notes: https://github.com/openlayers/openlayers/releases + +Timeseries implementation tries to preload the "next" image to be shown (the next "time" on the timeseries) for showing WMS-T layers and tinkers with some internals of OpenLayers: https://github.com/oskariorg/oskari-frontend/pull/2732 + +## Cesium + +See notes: +- https://github.com/CesiumGS/cesium/releases +- https://github.com/CesiumGS/cesium/blob/main/CHANGES.md + +On Oskari 2.14 cesium was switched to @cesium/engine as we aren't using the widgets part. However it seems it's more difficult to track the changes on the engine as changelog describes the "full release" of everything cesium. + +## ol-cesium + +Maintained under openlayers and acts as a glue between cesium and OpenLayers. We can use most of OpenLayers API while showing data on Cesium. + +Currently, doesn't have support for OpenLayers 10.x/prevents from updating to most recent version of OpenLayers. From e5d8c918f51186a86bbfca68778a7300e279e992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Fri, 15 Nov 2024 18:36:02 +0200 Subject: [PATCH 113/181] Add antd --- UpdatingDependencies.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UpdatingDependencies.md b/UpdatingDependencies.md index 8822445ae2..1db9a05554 100644 --- a/UpdatingDependencies.md +++ b/UpdatingDependencies.md @@ -21,3 +21,7 @@ On Oskari 2.14 cesium was switched to @cesium/engine as we aren't using the widg Maintained under openlayers and acts as a glue between cesium and OpenLayers. We can use most of OpenLayers API while showing data on Cesium. Currently, doesn't have support for OpenLayers 10.x/prevents from updating to most recent version of OpenLayers. + +## AntD + +Usually styles tend to break when updating AntD. However in the 5.x version AntD introduced theming using props on components. This might make it easier to update. From 533be0f2f190d9dd5fd3aa7384eabf8f735be155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Fri, 15 Nov 2024 18:49:07 +0200 Subject: [PATCH 114/181] Update UpdatingDependencies.md --- UpdatingDependencies.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UpdatingDependencies.md b/UpdatingDependencies.md index 1db9a05554..0bef9cbd7d 100644 --- a/UpdatingDependencies.md +++ b/UpdatingDependencies.md @@ -14,11 +14,11 @@ See notes: - https://github.com/CesiumGS/cesium/releases - https://github.com/CesiumGS/cesium/blob/main/CHANGES.md -On Oskari 2.14 cesium was switched to @cesium/engine as we aren't using the widgets part. However it seems it's more difficult to track the changes on the engine as changelog describes the "full release" of everything cesium. +On Oskari 2.14 cesium was switched to @cesium/engine as we aren't using the widgets part. However it seems it's more difficult to track the changes on the engine as changelog describes the "full release" of everything cesium (or more difficult to track which version of engine is used on which Cesium release). ## ol-cesium -Maintained under openlayers and acts as a glue between cesium and OpenLayers. We can use most of OpenLayers API while showing data on Cesium. +Maintained under openlayers and acts as glue between cesium and OpenLayers. We can use most of OpenLayers API while showing data on Cesium. Currently, doesn't have support for OpenLayers 10.x/prevents from updating to most recent version of OpenLayers. From 40957678e1ffaf20c0b17b9ca8bc11bc1714782b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Fri, 15 Nov 2024 18:51:53 +0200 Subject: [PATCH 115/181] Update UpdatingDependencies.md --- UpdatingDependencies.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UpdatingDependencies.md b/UpdatingDependencies.md index 0bef9cbd7d..0a872c9f2e 100644 --- a/UpdatingDependencies.md +++ b/UpdatingDependencies.md @@ -16,6 +16,8 @@ See notes: On Oskari 2.14 cesium was switched to @cesium/engine as we aren't using the widgets part. However it seems it's more difficult to track the changes on the engine as changelog describes the "full release" of everything cesium (or more difficult to track which version of engine is used on which Cesium release). +The engine version can be found here: https://github.com/CesiumGS/cesium/blob/1.123.1/packages/engine/package.json#L3 (when cesium version is 1.123.1). + ## ol-cesium Maintained under openlayers and acts as glue between cesium and OpenLayers. We can use most of OpenLayers API while showing data on Cesium. From b5af63d40310187d025cccd50f6eb0de4aca56fa Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 16 Nov 2024 01:11:19 +0200 Subject: [PATCH 116/181] don't try to get runtime data from backend --- bundles/statistics/statsgrid/constants.js | 1 + .../statistics/statsgrid/handler/IndicatorHelper.js | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bundles/statistics/statsgrid/constants.js b/bundles/statistics/statsgrid/constants.js index ea4f551a77..a3d3691b09 100644 --- a/bundles/statistics/statsgrid/constants.js +++ b/bundles/statistics/statsgrid/constants.js @@ -1,6 +1,7 @@ export const BUNDLE_KEY = 'StatsGrid'; export const LAYER_ID = 'STATS_LAYER'; export const DATA_PROVIDER = 'indicators'; +export const RUNTIME = 'RuntimeIndicator'; export const COLOR_SETS = [{ 'name': 'BrBG', diff --git a/bundles/statistics/statsgrid/handler/IndicatorHelper.js b/bundles/statistics/statsgrid/handler/IndicatorHelper.js index 8cc9ce1517..88c30e0bb1 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorHelper.js +++ b/bundles/statistics/statsgrid/handler/IndicatorHelper.js @@ -1,7 +1,7 @@ import { getHash, getDataProviderKey, populateData, populateSeriesData } from '../helper/StatisticsHelper'; import { updateIndicatorListInCache, removeIndicatorFromCache } from './SearchIndicatorOptionsHelper'; import { getRegionsAsync } from '../helper/RegionsHelper'; -import { BUNDLE_KEY } from '../constants'; +import { BUNDLE_KEY, RUNTIME } from '../constants'; // cache storage object const indicatorMetadataStore = {}; @@ -12,6 +12,7 @@ const getDataCacheKey = (indicator, regionsetId) => { const hash = getHash(indicator.ds, indicator.id, indicator.selections); return 'hash_' + hash + '_rs_' + regionsetId; }; +const isRuntime = (indicator = {}) => typeof indicator.id === 'string' && indicator.id.startsWith(RUNTIME); // for guest user's own indicators const updateIndicatorMetadataInCache = (indicator, regionsetId) => { @@ -137,7 +138,6 @@ const processMetadata = (meta) => { export const getDataForIndicator = async (indicator, regionset) => { const regions = await getRegionsAsync(regionset); - // TODO: regionsetsIsEmpty let data = {}; const fractionDigits = indicator?.classification?.fractionDigits; if (indicator.series) { @@ -173,6 +173,10 @@ export const getIndicatorData = async (indicator, regionsetId) => { // found a cached response return cachedResponse; } + if (isRuntime(indicator)) { + // backend doesn't have data for runtime indicators + return {}; + } try { const response = await fetch(Oskari.urls.getRoute('GetIndicatorData', { datasource: indicator.ds, @@ -205,7 +209,7 @@ export const saveIndicator = async (indicator) => { throw new Error('Indicator missing'); } if (!Oskari.user().isLoggedIn()) { - const id = indicator.id || 'RuntimeIndicator' + Oskari.seq.nextVal('RuntimeIndicator'); + const id = indicator.id || RUNTIME + Oskari.seq.nextVal(RUNTIME); const saved = { ...indicator, id }; updateIndicatorListInCache(saved); updateIndicatorMetadataInCache(saved); From 307a51013a4ca69abc3396debdb90934846681bd Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Sat, 16 Nov 2024 01:16:22 +0200 Subject: [PATCH 117/181] update state on empty disabled datasources --- .../statsgrid/handler/SearchHandler.js | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 3bcd265e3f..fa65b66336 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -146,20 +146,18 @@ class SearchController extends AsyncStateHandler { return; } const disabledDatasources = getUnsupportedDatasourceIds(value); - if (disabledDatasources.length) { - if (disabledDatasources.includes(this.getState().selectedDatasource)) { - this.clearSearch(); - return; - } - this.updateState({ - // reset any selected indicators because if they are disabled, user can't unselect them - selectedIndicators: [], - indicatorParams: {}, - selectedRegionset: null, - disabledDatasources, - indicatorOptions: this.validateIndicatorList(this.getState().indicatorOptions) - }); + if (disabledDatasources.includes(this.getState().selectedDatasource)) { + this.clearSearch(); + return; } + this.updateState({ + // reset any selected indicators because if they are disabled, user can't unselect them + selectedIndicators: [], + indicatorParams: {}, + selectedRegionset: null, + disabledDatasources, + indicatorOptions: this.validateIndicatorList(this.getState().indicatorOptions) + }); } setSelectedDatasource (value) { From 07643f16857342aa61c9cd83f548e2e8bda31c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Mon, 18 Nov 2024 12:50:35 +0200 Subject: [PATCH 118/181] allowDegrees and showDegress did the same thing, reformatted and simplified as isCurrentUnitDegrees() --- .../plugin/CoordinatePluginHandler.js | 65 +++++++------------ .../coordinatetool/plugin/CoordinatePopup.jsx | 4 +- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index 834ce12646..e76f95485a 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -96,8 +96,14 @@ class UIHandler extends StateHandler { }); } + getProjectionShowFormat (selectedProj = this.state.selectedProjection) { + const projConfig = this.config?.projectionShowFormat?.[selectedProj] || {}; + const globalFormat = this.config?.projectionShowFormat?.format; + return projConfig.format || globalFormat; + } + async useUserDefinedCoordinates () { - let data = { + const data = { lonlat: { lon: this.formatNumber(this.state.lonField, '.'), lat: this.formatNumber(this.state.latField, '.') @@ -105,12 +111,7 @@ class UIHandler extends StateHandler { }; // Check if format should be degrees and add '°' symbol if needed - let isDegrees = false; - if (this.config.projectionShowFormat[this.state.selectedProjection]) { - isDegrees = this.config.projectionShowFormat[this.state.selectedProjection].format === 'degrees'; - } else { - isDegrees = this.config.projectionShowFormat.format === 'degrees'; - } + const isDegrees = this.getProjectionShowFormat() === 'degrees'; if (isDegrees) { if (data.lonlat.lon.indexOf('°') < 0) data.lonlat.lon = data.lonlat.lon + '°'; if (data.lonlat.lat.indexOf('°') < 0) data.lonlat.lat = data.lonlat.lat + '°'; @@ -122,13 +123,11 @@ class UIHandler extends StateHandler { coordinatesToMetric (data) { const converted = Oskari.util.coordinateDegreesToMetric([data.lonlat?.lon, data.lonlat?.lat], 10); - const lon = converted[0]; - const lat = converted[1]; return { - 'lonlat': { - 'lon': lon, - 'lat': lat + lonlat: { + lon: converted[0], + lat: converted[1] } }; } @@ -210,7 +209,7 @@ class UIHandler extends StateHandler { let lon = parseFloat(data?.lonlat?.lon); // Need to show degrees ? - if (this.allowDegrees() && !isNaN(lat) && !isNaN(lon)) { + if (this.isCurrentUnitDegrees() && !isNaN(lat) && !isNaN(lon)) { const degreePoint = Oskari.util.coordinateMetricToDegrees([lon, lat], this.getProjectionDecimals()); lon = degreePoint[0]; lat = degreePoint[1]; @@ -284,7 +283,7 @@ class UIHandler extends StateHandler { let lon = displayData?.lonlat?.lon || data?.lonlat?.lon; try { let msg = null; - if (Oskari.util.coordinateIsDegrees([lon, lat]) && this.allowDegrees()) { + if (Oskari.util.coordinateIsDegrees([lon, lat]) && this.isCurrentUnitDegrees()) { msg = { lat: lat, lon: lon @@ -348,9 +347,9 @@ class UIHandler extends StateHandler { getMapXY () { const map = this.sandbox.getMap(); const data = { - 'lonlat': { - 'lat': parseFloat(map.getY()), - 'lon': parseFloat(map.getX()) + lonlat: { + lat: parseFloat(map.getY()), + lon: parseFloat(map.getX()) } }; return data; @@ -365,30 +364,11 @@ class UIHandler extends StateHandler { return coordinate; } - showDegrees () { + isCurrentUnitDegrees () { const projection = this.state.selectedProjection || this.originalProjection; - let showDegrees = (this.mapModule.getProjectionUnits() === 'degrees'); - const projFormats = this.config.projectionShowFormat || {}; - const formatDef = projFormats[projection]; - - if (formatDef) { - showDegrees = (formatDef.format === 'degrees'); - } - return showDegrees; - } - - allowDegrees (checkedProjection) { - const selectedProjection = this.state.selectedProjection ? this.state.selectedProjection : this.originalProjection; - const projection = checkedProjection || selectedProjection; - - const isProjectionShowConfig = !!((this.config.projectionShowFormat && this.config.projectionShowFormat[projection] && this.config.projectionShowFormat[projection].format)); - let isDegrees = !!(((isProjectionShowConfig && this.config.projectionShowFormat[projection].format === 'degrees') || this.mapModule.getProjectionUnits() === 'degrees')); - - const isAllProjectionConfig = !!((this.config.projectionShowFormat && typeof this.config.projectionShowFormat.format === 'string')); - if (!isProjectionShowConfig && isAllProjectionConfig) { - isDegrees = (this.config.projectionShowFormat.format === 'degrees'); - } - return isDegrees; + const formatDef = this.getProjectionShowFormat(projection); + const defaultedToMapmodule = formatDef || this.mapModule.getProjectionUnits(); + return defaultedToMapmodule === 'degrees'; } formatDegrees (lon, lat, type) { @@ -544,7 +524,7 @@ class UIHandler extends StateHandler { // update emergency if configured if (this.config.showEmergencyCallMessage) { // already in degrees, don't fetch again - if (this.allowDegrees() && this.originalProjection === 'EPSG:4326') { + if (this.isCurrentUnitDegrees() && this.originalProjection === 'EPSG:4326') { this.updateState({ emergencyInfo: this.formatEmergencyCallMessage({ 'lonlat': { @@ -646,7 +626,7 @@ class UIHandler extends StateHandler { const wrapped = controllerMixin(UIHandler, [ 'toggleMouseCoordinates', - 'showDegrees', + 'isCurrentUnitDegrees', 'showPopup', 'markersSupported', 'setMarker', @@ -655,7 +635,6 @@ const wrapped = controllerMixin(UIHandler, [ 'setLatInputValue', 'setLoading', 'setSelectedProjection', - 'allowDegrees', 'formatDegrees', 'toggleReverseGeoCode', 'useUserDefinedCoordinates', diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx index 337ab6a77c..f04bc6797c 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx +++ b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx @@ -79,14 +79,14 @@ const formatLeadingZero = (number) => { const PopupContent = ({ state, controller, preciseTransform, supportedProjections, crsText, decimalSeparator, showReverseGeoCodeCheckbox }) => { let latLabel = <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.lat'} />; let lonLabel = <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.lon'} />; - if (!controller.showDegrees()) { + if (!controller.isCurrentUnitDegrees()) { latLabel = <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.n'} />; lonLabel = <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.e'} />; } let degmin; let dec; - if (controller.allowDegrees() && state?.lonField && state?.latField) { + if (controller.isCurrentUnitDegrees() && state?.lonField && state?.latField) { dec = Oskari.util.coordinateDegreesToMetric([state.lonField, state.latField], 20); degmin = controller.formatDegrees(dec[0], dec[1], 'min'); degmin.minutesX = formatLeadingZero(degmin.minutesX); From 741162063bdc4c80ed69e5d5d8d4bbeee1e2d51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Mon, 18 Nov 2024 13:40:43 +0200 Subject: [PATCH 119/181] Refactor getProjectionDecimals() --- .../plugin/CoordinatePluginHandler.js | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index e76f95485a..b065e3b390 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -96,12 +96,35 @@ class UIHandler extends StateHandler { }); } + // prefer proj specific and default to global format getProjectionShowFormat (selectedProj = this.state.selectedProjection) { - const projConfig = this.config?.projectionShowFormat?.[selectedProj] || {}; + const projection = selectedProj || this.originalProjection; + const projConfig = this.config?.projectionShowFormat?.[projection] || {}; const globalFormat = this.config?.projectionShowFormat?.format; return projConfig.format || globalFormat; } + // prefer proj specific, global format, deprecated conf and default to mapmodule logic + getProjectionDecimals (selectedProj = this.state.selectedProjection) { + const projection = selectedProj || this.originalProjection; + const conf = this.config; + const projConfig = conf?.projectionShowFormat?.[projection] || {}; + const globalFormat = conf?.projectionShowFormat?.decimals; + const decimalCount = projConfig.decimals || globalFormat; + if (typeof decimalCount === 'number' && decimalCount >= 0) { + return decimalCount; + } + const deprecatedConf = conf?.roundToDecimals; + if (deprecatedConf) { + Oskari.log('coordinatetool').warn('Deprecated coordinatetool.conf.roundToDecimals - please use coordinatetool.conf.projectionShowFormat.decimals or ' + + 'coordinatetool.conf.projectionShowFormat["projection"].decimals instead.'); + if (typeof deprecatedConf === 'number' && deprecatedConf >= 0) { + return conf.roundToDecimals; + } + } + return this.mapModule.getProjectionDecimals(projection); + } + async useUserDefinedCoordinates () { const data = { lonlat: { @@ -375,26 +398,6 @@ class UIHandler extends StateHandler { return formatDegrees(lon, lat, type); } - getProjectionDecimals (checkedProjection) { - const conf = this.config; - const selectedProjection = this.state.selectedProjection ? this.state.selectedProjection : this.originalProjection; - const projection = checkedProjection || selectedProjection; - const isProjectionShowConfig = !!((conf.projectionShowFormat && conf.projectionShowFormat[projection] && typeof conf.projectionShowFormat[projection].decimals === 'number')); - - let decimals = (isProjectionShowConfig) ? conf.projectionShowFormat[projection].decimals : this.mapModule.getProjectionDecimals(selectedProjection); - - const isAllProjectionConfig = !!((conf.projectionShowFormat && typeof conf.projectionShowFormat.decimals === 'number')); - - if (!isProjectionShowConfig && isAllProjectionConfig) { - decimals = conf.projectionShowFormat.decimals; - } else if (!isProjectionShowConfig && conf.roundToDecimals) { - decimals = conf.roundToDecimals; - this.sandbox.printWarn('Deprecated coordinatetool.conf.roundToDecimals - please use coordinatetool.conf.projectionShowFormat.decimals or ' + - 'coordinatetool.conf.projectionShowFormat["projection"].decimals instead.'); - } - return decimals; - } - markersSupported () { const builder = Oskari.requestBuilder('MapModulePlugin.AddMarkerRequest'); return !!builder; @@ -407,9 +410,9 @@ class UIHandler extends StateHandler { const response = await getTransformedCoordinates(this.originalProjection, data, fromProjection, toProjection); if (response?.lat && response?.lon) { const newData = { - 'lonlat': { - 'lon': response.lon, - 'lat': response.lat + lonlat: { + lon: response.lon, + lat: response.lat } }; this.setLoading(false); From e27f97666894f3d0b62776feb03f831006b97b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Mon, 18 Nov 2024 13:47:18 +0200 Subject: [PATCH 120/181] ESLint formatting --- .../plugin/CoordinatePluginHandler.js | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index b065e3b390..5024f52f36 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -302,8 +302,8 @@ class UIHandler extends StateHandler { this.centerMap(); const data = this.state.xy || this.getMapXY(); const displayData = { lonlat: { lon: this.state.lonField, lat: this.state.latField } } || this.getMapXY(); - let lat = displayData?.lonlat?.lat || data?.lonlat?.lat; - let lon = displayData?.lonlat?.lon || data?.lonlat?.lon; + const lat = displayData?.lonlat?.lat || data?.lonlat?.lat; + const lon = displayData?.lonlat?.lon || data?.lonlat?.lon; try { let msg = null; if (Oskari.util.coordinateIsDegrees([lon, lat]) && this.isCurrentUnitDegrees()) { @@ -328,9 +328,9 @@ class UIHandler extends StateHandler { let lat = parseFloat(data.lonlat.lat); let lon = parseFloat(data.lonlat.lon); const inputLonLatData = { - 'lonlat': { - 'lat': lat, - 'lon': lon + lonlat: { + lat, + lon } }; @@ -358,7 +358,7 @@ class UIHandler extends StateHandler { const marker = { x: data.lonlat.lon, y: data.lonlat.lat, - msg: msg, + msg, size: 5, color: 'ee9900', shape: 2 @@ -439,7 +439,7 @@ class UIHandler extends StateHandler { data = this.getMapXY(); } - let reverseGeoCode = []; + const reverseGeoCode = []; service.getReverseGeocode( // Success callback @@ -455,12 +455,12 @@ class UIHandler extends StateHandler { title = r.type; } reverseGeoCode.push({ - title: title, + title, name: r.name }); } this.updateState({ - reverseGeoCode: reverseGeoCode + reverseGeoCode }); } else { this.updateState({ @@ -504,9 +504,9 @@ class UIHandler extends StateHandler { const response = await getTransformedCoordinates(this.originalProjection, data, sourceProjection, 'EPSG:4326'); if (response?.lat && response?.lon) { const newData = { - 'lonlat': { - 'lon': response.lon, - 'lat': response.lat + lonlat: { + lon: response.lon, + lat: response.lat } }; return this.formatEmergencyCallMessage(newData); @@ -530,9 +530,9 @@ class UIHandler extends StateHandler { if (this.isCurrentUnitDegrees() && this.originalProjection === 'EPSG:4326') { this.updateState({ emergencyInfo: this.formatEmergencyCallMessage({ - 'lonlat': { - 'lon': xy.lonlat.lon, - 'lat': xy.lonlat.lat + lonlat: { + lon: xy.lonlat.lon, + lat: xy.lonlat.lat } }) }); @@ -573,9 +573,9 @@ class UIHandler extends StateHandler { MouseHoverEvent: function (event) { if (this.state.showMouseCoordinates && !this.state.loading) { const data = { - 'lonlat': { - 'lat': parseFloat(event.getLat()), - 'lon': parseFloat(event.getLon()) + lonlat: { + lat: parseFloat(event.getLat()), + lon: parseFloat(event.getLon()) } }; if (event.isPaused()) { @@ -603,9 +603,9 @@ class UIHandler extends StateHandler { if (!this.popupControls) return; const lonlat = event.getLonLat(); const data = { - 'lonlat': { - 'lat': parseFloat(lonlat.lat), - 'lon': parseFloat(lonlat.lon) + lonlat: { + lat: parseFloat(lonlat.lat), + lon: parseFloat(lonlat.lon) } }; if (!this.showMouseCoordinates) { @@ -618,7 +618,7 @@ class UIHandler extends StateHandler { } onEvent (e) { - var handler = this.eventHandlers[e.getName()]; + const handler = this.eventHandlers[e.getName()]; if (!handler) { return; } From a0ccc52292c1d7b72508d3736cee53ccc4a4eefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Mon, 18 Nov 2024 19:17:08 +0200 Subject: [PATCH 121/181] Refactor into smaller components --- .../plugin/CoordinatePluginHandler.js | 17 +- .../coordinatetool/plugin/CoordinatePopup.jsx | 433 ++++++++++-------- .../framework/coordinatetool/plugin/styled.js | 62 +++ 3 files changed, 309 insertions(+), 203 deletions(-) create mode 100644 bundles/framework/coordinatetool/plugin/styled.js diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index 5024f52f36..b135769074 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -282,7 +282,7 @@ class UIHandler extends StateHandler { centerMap (coordinates) { try { - let data = coordinates || this.state.xy; + const data = coordinates || this.state.xy; this.centerMapToSelectedCoordinates(data); } catch (e) { Messaging.error(this.loc('display.checkValuesDialog.message')); @@ -308,8 +308,8 @@ class UIHandler extends StateHandler { let msg = null; if (Oskari.util.coordinateIsDegrees([lon, lat]) && this.isCurrentUnitDegrees()) { msg = { - lat: lat, - lon: lon + lat, + lon }; } else { msg = { @@ -394,15 +394,6 @@ class UIHandler extends StateHandler { return defaultedToMapmodule === 'degrees'; } - formatDegrees (lon, lat, type) { - return formatDegrees(lon, lat, type); - } - - markersSupported () { - const builder = Oskari.requestBuilder('MapModulePlugin.AddMarkerRequest'); - return !!builder; - } - async getTransformedCoordinatesFromServer (data, fromProjection, toProjection) { this.setLoading(true); data = data || this.getMapXY(); @@ -631,14 +622,12 @@ const wrapped = controllerMixin(UIHandler, [ 'toggleMouseCoordinates', 'isCurrentUnitDegrees', 'showPopup', - 'markersSupported', 'setMarker', 'centerMap', 'setLonInputValue', 'setLatInputValue', 'setLoading', 'setSelectedProjection', - 'formatDegrees', 'toggleReverseGeoCode', 'useUserDefinedCoordinates', 'isMapCentered', diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx index f04bc6797c..313e758abb 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx +++ b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx @@ -1,8 +1,10 @@ import React from 'react'; import { showPopup } from 'oskari-ui/components/window'; -import styled from 'styled-components'; import { Message, Button, TextInput, Checkbox, Select, Option, Spin } from 'oskari-ui'; +import { Content, CoordinateFields, CoordinateField, DegreeContainer, CoordinateLabel, SelectField, EmergencyInfo, SelectLabel, ReverseGeoCodes, Approximation } from './styled'; import { ButtonContainer } from 'oskari-ui/components/buttons'; +import { formatDegrees } from './helper'; +import PropTypes from 'prop-types'; const BUNDLE_KEY = 'coordinatetool'; @@ -10,213 +12,266 @@ const OPTIONS = { id: 'coordinates' }; -const Content = styled('div')` - margin: 12px 24px 24px; - display: flex; - width: 270px; - flex-direction: column; -`; - -const CoordinateFields = styled('div')` - display: flex; - flex-direction: column; - margin-top: 10px; -`; +const formatLeadingZero = (number) => { + if (parseFloat(number) < 10) return `0${number}`; + return number; +}; +const isMarkersSupported = () => { + const builder = Oskari.requestBuilder('MapModulePlugin.AddMarkerRequest'); + return !!builder; +}; -const CoordinateField = styled('div')` - display: flex; - flex-direction: row; - align-items: center; - margin-bottom: 10px; -`; +const ProjectionChanger = ({ preciseTransform, projection, onChange, supportedProjections = [], children }) => { + if (!preciseTransform || typeof onChange !== 'function') { + return children; + } + return ( + <SelectField> + <SelectLabel> + <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.header' /> + </SelectLabel> + <Select + value={projection} + onChange={onChange} + > + {supportedProjections.map(option => ( + <Option key={option} value={option}> + <Message bundleKey={BUNDLE_KEY} messageKey={`display.coordinatesTransform.projections.${option}`} /> + </Option> + )) } + </Select> + </SelectField> + ); +}; -const DegreeContainer = styled('div')` - display: flex; - flex-direction: column; - margin-left: 59px; - margin-bottom: 10px; -`; +ProjectionChanger.propTypes = { + preciseTransform: PropTypes.bool, + projection: PropTypes.string, + onChange: PropTypes.func, + supportedProjections: PropTypes.array.isRequired, + children: PropTypes.any +}; -const CoordinateLabel = styled('div')` - margin-right: 5px; - width: 40px; - display: flex; - flex-direction: row; -`; +const LatLabel = ({ isDegrees }) => { + if (isDegrees) { + return <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.lat'} LabelComponent={ CoordinateLabel } />; + } + return <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.n'} LabelComponent={ CoordinateLabel } />; +}; +LatLabel.propTypes = { + isDegrees: PropTypes.bool +}; -const SelectField = styled('div')` - margin-bottom: 10px; - display: flex; - flex-direction: column; -`; +const LonLabel = ({ isDegrees }) => { + if (isDegrees) { + return <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.lon'} LabelComponent={ CoordinateLabel } />; + } + return <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.e'} LabelComponent={ CoordinateLabel } />; +}; +LonLabel.propTypes = { + isDegrees: PropTypes.bool +}; -const EmergencyInfo = styled('div')` - margin-top: 10px; -`; +const DegreesDisplay = ({ isDegrees, lon, lat, decimalSeparator, isLat }) => { + if (!isDegrees) { + return null; + } + const dec = Oskari.util.coordinateDegreesToMetric([lon, lat], 20); + const degmin = formatDegrees(dec[0], dec[1], 'min'); + if (isLat) { + return ( + <DegreeContainer> + <span>{`${degmin.degreesY}° ${formatLeadingZero(degmin.minutesY)}'`}</span> + <span>{`${parseFloat(dec[1]).toFixed(9).replace('.', decimalSeparator)}°`}</span> + </DegreeContainer> + ); + } + return ( + <DegreeContainer> + <span>{`${degmin.degreesX}° ${formatLeadingZero(degmin.minutesX)}'`}</span> + <span>{`${parseFloat(dec[0]).toFixed(9).replace('.', decimalSeparator)}°`}</span> + </DegreeContainer> + ); +}; +DegreesDisplay.propTypes = { + isDegrees: PropTypes.bool, + lon: PropTypes.string, + lat: PropTypes.string, + decimalSeparator: PropTypes.string, + isLat: PropTypes.bool +}; -const SelectLabel = styled('div')` - font-size: 12px; - margin-top: 5px; - opacity: 0.8; -`; +const HoverToggle = ({ isShowing, onChange }) => { + if (!Oskari.util.mouseExists()) { + return null; + } + return ( + <Checkbox + checked={isShowing} + onChange={onChange} + > + <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.showMouseCoordinates' /> + </Checkbox> + ); +}; +HoverToggle.propTypes = { + isShowing: PropTypes.bool, + onChange: PropTypes.func +}; -const ReverseGeoCodes = styled('div')` - display: flex; - flex-direction: column; - margin-top: 5px; -`; +const MarkerButton = ({ isSupported, addMarker }) => { + if (!isSupported) { + return null; + } + return ( + <Button + type='default' + onClick={addMarker} + className='t_add' + > + <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.addMarkerButton' /> + </Button> + ); +}; +MarkerButton.propTypes = { + isSupported: PropTypes.bool, + addMarker: PropTypes.func +}; -const Approximation = styled('span')` - width: 12px; - font-size: 14px; -`; +const ReverseGeocodeCheckBox = ({ showCheckbox, showResults, toggleResults }) => { + if (!showCheckbox) { + return null; + } + return ( + <Checkbox + checked={showResults} + onChange={toggleResults} + > + <Message bundleKey={BUNDLE_KEY} messageKey='display.reversegeocode.moreInfo' /> + </Checkbox> + ); +}; -const formatLeadingZero = (number) => { - if (parseFloat(number) < 10) return `0${number}`; - return number; -} +ReverseGeocodeCheckBox.propTypes = { + showCheckbox: PropTypes.bool, + showResults: PropTypes.bool, + toggleResults: PropTypes.func +}; -const PopupContent = ({ state, controller, preciseTransform, supportedProjections, crsText, decimalSeparator, showReverseGeoCodeCheckbox }) => { - let latLabel = <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.lat'} />; - let lonLabel = <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.lon'} />; - if (!controller.isCurrentUnitDegrees()) { - latLabel = <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.n'} />; - lonLabel = <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.e'} />; +const ReverseGeocodeResults = ({ showResults, results = [] }) => { + if (!showResults || results.length === 0) { + return null; } + return ( + <ReverseGeoCodes> + {results.map((geoCode, index) => ( + <span key={index}>{geoCode.title} <u>{geoCode.name}</u></span> + ))} + </ReverseGeoCodes> + ); +}; - let degmin; - let dec; - if (controller.isCurrentUnitDegrees() && state?.lonField && state?.latField) { - dec = Oskari.util.coordinateDegreesToMetric([state.lonField, state.latField], 20); - degmin = controller.formatDegrees(dec[0], dec[1], 'min'); - degmin.minutesX = formatLeadingZero(degmin.minutesX); - degmin.minutesY = formatLeadingZero(degmin.minutesY); - } +ReverseGeocodeResults.propTypes = { + showResults: PropTypes.bool, + results: PropTypes.array +}; - const content = ( - <Content> - <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.info' /> - {!preciseTransform ? ( - crsText - ) : ( - <SelectField> - <SelectLabel> - <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.header' /> - </SelectLabel> - <Select - value={state.selectedProjection} - onChange={value => controller.setSelectedProjection(value)} - > - {supportedProjections.map(option => ( - <Option key={option} value={option}> - <Message bundleKey={BUNDLE_KEY} messageKey={`display.coordinatesTransform.projections.${option}`} /> - </Option> - )) } - </Select> - </SelectField> - )} - <CoordinateFields> - <CoordinateField> - <CoordinateLabel>{latLabel}:</CoordinateLabel> - <Approximation> - {state.approxValue && ('~')} - </Approximation> - <TextInput - value={state?.latField} - onChange={(e) => controller.setLatInputValue(e.target.value)} - onBlur={() => controller.useUserDefinedCoordinates()} - disabled={state.showMouseCoordinates} - className='t_lat' - /> - </CoordinateField> - {degmin && ( - <DegreeContainer> - <span>{`${degmin.degreesY}° ${degmin.minutesY}\'`}</span> - <span>{`${parseFloat(dec[1]).toFixed(9).replace('.', decimalSeparator)}°`}</span> - </DegreeContainer> - )} - <CoordinateField> - <CoordinateLabel>{lonLabel}:</CoordinateLabel> - <Approximation> - {state.approxValue && ('~')} - </Approximation> - <TextInput - value={state?.lonField} - onChange={(e) => controller.setLonInputValue(e.target.value)} - onBlur={() => controller.useUserDefinedCoordinates()} - disabled={state.showMouseCoordinates} - className='t_lon' - /> - </CoordinateField> - {degmin && ( - <DegreeContainer> - <span>{`${degmin.degreesX}° ${degmin.minutesX}\'`}</span> - <span>{`${parseFloat(dec[0]).toFixed(9).replace('.', decimalSeparator)}°`}</span> - </DegreeContainer> +const PopupContent = ({ state = {}, controller, preciseTransform, supportedProjections, crsText, decimalSeparator, showReverseGeoCodeCheckbox }) => { + const isDegrees = controller.isCurrentUnitDegrees(); + + return ( + <Spin spinning={state.loading}> + <Content> + <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.info' /> + <ProjectionChanger + preciseTransform={preciseTransform} + projection={state.selectedProjection} + onChange={value => controller.setSelectedProjection(value)} + supportedProjections={supportedProjections}>{crsText}</ProjectionChanger> + <CoordinateFields> + <CoordinateField> + <LatLabel isDegrees={isDegrees}/> + <Approximation> + {state.approxValue && ('~')} + </Approximation> + <TextInput + value={state?.latField} + onChange={(e) => controller.setLatInputValue(e.target.value)} + onBlur={() => controller.useUserDefinedCoordinates()} + disabled={state.showMouseCoordinates} + className='t_lat' + /> + </CoordinateField> + <DegreesDisplay + isDegrees={isDegrees} + lat={state?.latField} + lon={state.lonField} + decimalSeparator={decimalSeparator} + isLat={true} /> + <CoordinateField> + <LonLabel isDegrees={isDegrees}/> + <Approximation> + {state.approxValue && ('~')} + </Approximation> + <TextInput + value={state?.lonField} + onChange={(e) => controller.setLonInputValue(e.target.value)} + onBlur={() => controller.useUserDefinedCoordinates()} + disabled={state.showMouseCoordinates} + className='t_lon' + /> + </CoordinateField> + <DegreesDisplay + isDegrees={isDegrees} + lat={state?.latField} + lon={state?.lonField} + decimalSeparator={decimalSeparator} + isLat={false} /> + </CoordinateFields> + <HoverToggle + isShowing={state.showMouseCoordinates} + onChange={() => controller.toggleMouseCoordinates()} /> + <ReverseGeocodeCheckBox + showCheckbox={showReverseGeoCodeCheckbox} + showResults={state.showReverseGeoCode} + toggleResults={() => controller.toggleReverseGeoCode()} /> + <ReverseGeocodeResults + showResults={!showReverseGeoCodeCheckbox || state.showReverseGeoCode} + results={state.reverseGeoCode} /> + {state.emergencyInfo && ( + <EmergencyInfo> + <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.emergencyCallLabel' /> + {` ${state.emergencyInfo.degreesY}° `}{` ${formatLeadingZero(state.emergencyInfo.minutesY)}' `} + <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.emergencyCallLabelAnd' /> + {` ${state.emergencyInfo.degreesX}° `}{` ${formatLeadingZero(state.emergencyInfo.minutesX)}'`} + </EmergencyInfo> )} - </CoordinateFields> - {Oskari.util.mouseExists() && ( - <Checkbox - checked={state.showMouseCoordinates} - onChange={() => controller.toggleMouseCoordinates()} - > - <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.showMouseCoordinates' /> - </Checkbox> - )} - {showReverseGeoCodeCheckbox && ( - <Checkbox - checked={state.showReverseGeoCode} - onChange={() => controller.toggleReverseGeoCode()} - > - <Message bundleKey={BUNDLE_KEY} messageKey='display.reversegeocode.moreInfo' /> - </Checkbox> - )} - {((showReverseGeoCodeCheckbox && state.showReverseGeoCode && state.reverseGeoCode.length > 0) || (!showReverseGeoCodeCheckbox && state.reverseGeoCode.length > 0)) && ( - <ReverseGeoCodes> - {state.reverseGeoCode.map((geoCode, index) => ( - <span key={index}>{geoCode.title} <u>{geoCode.name}</u></span> - ))} - </ReverseGeoCodes> - )} - {state.emergencyInfo && ( - <EmergencyInfo> - <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.emergencyCallLabel' /> - {` ${state.emergencyInfo.degreesY}° `}{` ${formatLeadingZero(state.emergencyInfo.minutesY)}\' `} - <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.emergencyCallLabelAnd' /> - {` ${state.emergencyInfo.degreesX}° `}{` ${formatLeadingZero(state.emergencyInfo.minutesX)}\'`} - </EmergencyInfo> - )} - <ButtonContainer> - <Button - type='default' - disabled={state.showMouseCoordinates || controller.isMapCentered()} - onClick={() => controller.centerMap()} - className='t_center' - > - <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.searchButton' /> - </Button> - {controller.markersSupported && ( + <ButtonContainer> <Button type='default' - onClick={() => controller.setMarker()} - className='t_add' + disabled={state.showMouseCoordinates || controller.isMapCentered()} + onClick={() => controller.centerMap()} + className='t_center' > - <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.addMarkerButton' /> + <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.searchButton' /> </Button> - )} - </ButtonContainer> - </Content> + <MarkerButton + isSupported={isMarkersSupported()} + addMarker={() => controller.setMarker()} /> + </ButtonContainer> + </Content> + </Spin> ); +}; - if (state.loading) { - return ( - <Spin> - {content} - </Spin> - ); - } else { - return content; - } +PopupContent.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired, + preciseTransform: PropTypes.bool, + supportedProjections: PropTypes.array.isRequired, + crsText: PropTypes.string, + decimalSeparator: PropTypes.string, + showReverseGeoCodeCheckbox: PropTypes.bool }; export const showCoordinatePopup = (state, controller, location, supportedProjections = [], preciseTransform, crsText, decimalSeparator, showReverseGeoCodeCheckbox, onClose) => { @@ -225,7 +280,7 @@ export const showCoordinatePopup = (state, controller, location, supportedProjec ...OPTIONS, placement: location, theme: mapModule.getMapTheme() - } + }; const controls = showPopup( <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.title' />, <PopupContent diff --git a/bundles/framework/coordinatetool/plugin/styled.js b/bundles/framework/coordinatetool/plugin/styled.js new file mode 100644 index 0000000000..553144cccf --- /dev/null +++ b/bundles/framework/coordinatetool/plugin/styled.js @@ -0,0 +1,62 @@ +import styled from 'styled-components'; + +export const Content = styled('div')` + margin: 12px 24px 24px; + display: flex; + width: 270px; + flex-direction: column; +`; + +export const CoordinateFields = styled('div')` + display: flex; + flex-direction: column; + margin-top: 10px; +`; + +export const CoordinateField = styled('div')` + display: flex; + flex-direction: row; + align-items: center; + margin-bottom: 10px; +`; + +export const DegreeContainer = styled('div')` + display: flex; + flex-direction: column; + margin-left: 59px; + margin-bottom: 10px; +`; + +export const CoordinateLabel = styled('div')` + margin-right: 5px; + width: 40px; + display: flex; + flex-direction: row; +`; + +export const SelectField = styled('div')` + margin-bottom: 10px; + display: flex; + flex-direction: column; +`; + +export const EmergencyInfo = styled('div')` + margin-top: 10px; +`; + +export const SelectLabel = styled('div')` + font-size: 12px; + margin-top: 5px; + opacity: 0.8; +`; + +export const ReverseGeoCodes = styled('div')` + display: flex; + flex-direction: column; + margin-top: 5px; +`; + +export const Approximation = styled('span')` + width: 12px; + font-size: 14px; +`; \ No newline at end of file From ceddac3a2c8e6d304a2162abfe08a9ed6eec452c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Mon, 18 Nov 2024 19:49:37 +0200 Subject: [PATCH 122/181] Allow fallback prop for Message --- src/react/components/Message.jsx | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/react/components/Message.jsx b/src/react/components/Message.jsx index ac022586c4..a54662e1bd 100644 --- a/src/react/components/Message.jsx +++ b/src/react/components/Message.jsx @@ -4,10 +4,10 @@ import { LocaleConsumer } from 'oskari-ui/util'; import styled from 'styled-components'; const Label = styled('div')` - display: ${props => props.allowTextEllipsis ? 'inline': 'inline-block'}; - overflow: ${props => props.allowTextEllipsis ? 'hidden': ''}; - white-space: ${props => props.allowTextEllipsis ? 'nowrap': ''}; - text-overflow: ${props => props.allowTextEllipsis ? 'ellipsis': ''}; + display: ${props => props.allowTextEllipsis ? 'inline' : 'inline-block'}; + overflow: ${props => props.allowTextEllipsis ? 'hidden' : ''}; + white-space: ${props => props.allowTextEllipsis ? 'nowrap' : ''}; + text-overflow: ${props => props.allowTextEllipsis ? 'ellipsis' : ''}; `; /** @@ -34,7 +34,7 @@ const Label = styled('div')` * <Message messageKey="hello" messageArgs={['Jack']}/> * </LocaleProvider> */ -const Message = ({ bundleKey, messageKey, messageArgs, defaultMsg, getMessage, children, LabelComponent = Label, allowHTML = false, allowTextEllipsis = false }) => { +const Message = ({ bundleKey, messageKey, messageArgs, defaultMsg, getMessage, fallback, children, LabelComponent = Label, allowHTML = false, allowTextEllipsis = false }) => { if (!messageKey) { return null; } @@ -49,18 +49,22 @@ const Message = ({ bundleKey, messageKey, messageArgs, defaultMsg, getMessage, c // If we didn't find localization AND we have default value -> use it if (message === messageKey && defaultMsg) { - message = defaultMsg; + if (defaultMsg) { + message = defaultMsg; + } else if (fallback) { + return fallback; + } } if (allowHTML) { - return ( <LabelComponent dangerouslySetInnerHTML={{ __html:message }}></LabelComponent> ); + return (<LabelComponent dangerouslySetInnerHTML={{ __html: message }}></LabelComponent>); } return ( <LabelComponent allowTextEllipsis={allowTextEllipsis} onClick={() => Oskari.log().debug(`Text clicked - ${bundleKey}: ${messageKey}`)}> - { message } { children } + { message } { children } </LabelComponent> ); }; @@ -72,20 +76,20 @@ Message.propTypes = { getMessage: PropTypes.func, children: PropTypes.node, LabelComponent: PropTypes.elementType, + fallback: PropTypes.node, allowHTML: PropTypes.bool, allowTextEllipsis: PropTypes.bool }; -function getMessageUsingOskariGlobal(bundleKey, messageKey, messageArgs) { +function getMessageUsingOskariGlobal (bundleKey, messageKey, messageArgs) { try { return Oskari.getMsg(bundleKey, messageKey, messageArgs); - } catch(e) { + } catch (e) { // no locale provider OR bundleKey missing from locale provider Oskari.log().warn(`Message tag used without LocaleProvider or bundleKey not provided when getting: ${messageKey}. Original error: ${e.message}`); } return messageKey; } - const wrapped = LocaleConsumer(Message); export { wrapped as Message }; From fce40a52cad4c4be5349308655ce0f69036f388b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Mon, 18 Nov 2024 19:50:10 +0200 Subject: [PATCH 123/181] Use Message fallback to get localization at render instead of controller --- .../plugin/CoordinatePluginHandler.js | 2 -- .../coordinatetool/plugin/CoordinatePopup.jsx | 16 ++++++++-------- .../framework/coordinatetool/plugin/styled.js | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index b135769074..35c3935ad6 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -263,14 +263,12 @@ class UIHandler extends StateHandler { if (this.popupControls) { this.popupCleanup(); } else { - const crsText = this.loc('display.crs')[this.state.selectedProjection] || this.loc('display.crs.default', { crs: this.state.selectedProjection }); this.popupControls = showCoordinatePopup( this.getState(), this.getController(), this.popupLocation(), this.config?.supportedProjections, this.preciseTransform, - crsText, this.decimalSeparator, this.reverseGeocodingIds?.length > 2, () => this.popupCleanup() diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx index 313e758abb..df3dcef7a1 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx +++ b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx @@ -21,9 +21,12 @@ const isMarkersSupported = () => { return !!builder; }; -const ProjectionChanger = ({ preciseTransform, projection, onChange, supportedProjections = [], children }) => { +const ProjectionChanger = ({ preciseTransform, projection, onChange, supportedProjections = [] }) => { if (!preciseTransform || typeof onChange !== 'function') { - return children; + return ( + <Message bundleKey={BUNDLE_KEY} messageKey={`display.crs.${projection}`} fallback={ + <Message bundleKey={BUNDLE_KEY} messageKey='display.crs.default' messageArgs={{ crs: projection }} /> + }/>); } return ( <SelectField> @@ -176,7 +179,7 @@ ReverseGeocodeResults.propTypes = { results: PropTypes.array }; -const PopupContent = ({ state = {}, controller, preciseTransform, supportedProjections, crsText, decimalSeparator, showReverseGeoCodeCheckbox }) => { +const PopupContent = ({ state = {}, controller, preciseTransform, supportedProjections, decimalSeparator, showReverseGeoCodeCheckbox }) => { const isDegrees = controller.isCurrentUnitDegrees(); return ( @@ -187,7 +190,7 @@ const PopupContent = ({ state = {}, controller, preciseTransform, supportedProje preciseTransform={preciseTransform} projection={state.selectedProjection} onChange={value => controller.setSelectedProjection(value)} - supportedProjections={supportedProjections}>{crsText}</ProjectionChanger> + supportedProjections={supportedProjections} /> <CoordinateFields> <CoordinateField> <LatLabel isDegrees={isDegrees}/> @@ -269,12 +272,11 @@ PopupContent.propTypes = { controller: PropTypes.object.isRequired, preciseTransform: PropTypes.bool, supportedProjections: PropTypes.array.isRequired, - crsText: PropTypes.string, decimalSeparator: PropTypes.string, showReverseGeoCodeCheckbox: PropTypes.bool }; -export const showCoordinatePopup = (state, controller, location, supportedProjections = [], preciseTransform, crsText, decimalSeparator, showReverseGeoCodeCheckbox, onClose) => { +export const showCoordinatePopup = (state, controller, location, supportedProjections = [], preciseTransform, decimalSeparator, showReverseGeoCodeCheckbox, onClose) => { const mapModule = Oskari.getSandbox().findRegisteredModuleInstance('MainMapModule'); const options = { ...OPTIONS, @@ -288,7 +290,6 @@ export const showCoordinatePopup = (state, controller, location, supportedProjec controller={controller} preciseTransform={preciseTransform} supportedProjections={supportedProjections} - crsText={crsText} decimalSeparator={decimalSeparator} showReverseGeoCodeCheckbox={showReverseGeoCodeCheckbox} />, @@ -304,7 +305,6 @@ export const showCoordinatePopup = (state, controller, location, supportedProjec controller={controller} preciseTransform={preciseTransform} supportedProjections={supportedProjections} - crsText={crsText} decimalSeparator={decimalSeparator} showReverseGeoCodeCheckbox={showReverseGeoCodeCheckbox} /> diff --git a/bundles/framework/coordinatetool/plugin/styled.js b/bundles/framework/coordinatetool/plugin/styled.js index 553144cccf..239a97973f 100644 --- a/bundles/framework/coordinatetool/plugin/styled.js +++ b/bundles/framework/coordinatetool/plugin/styled.js @@ -59,4 +59,4 @@ export const ReverseGeoCodes = styled('div')` export const Approximation = styled('span')` width: 12px; font-size: 14px; -`; \ No newline at end of file +`; From 840133dcf1f0ade14df99c296695b9d177101e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Tue, 19 Nov 2024 11:29:04 +0200 Subject: [PATCH 124/181] Rename variables, remove unnecessary props --- .../plugin/CoordinatePluginHandler.js | 48 +++++++++++-------- .../coordinatetool/plugin/CoordinatePopup.jsx | 26 ++++------ .../framework/coordinatetool/plugin/helper.js | 4 ++ 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index 35c3935ad6..cf58087f3c 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -1,7 +1,7 @@ import { StateHandler, controllerMixin, Messaging } from 'oskari-ui/util'; import { showCoordinatePopup } from './CoordinatePopup'; import { PLACEMENTS } from 'oskari-ui/components/window'; -import { getTransformedCoordinates, transformCoordinates, formatDegrees } from './helper'; +import { getTransformedCoordinates, transformCoordinates, formatDegrees, isTransformAllowed } from './helper'; class UIHandler extends StateHandler { constructor (plugin, mapModule, config) { @@ -18,7 +18,7 @@ class UIHandler extends StateHandler { ); this.sandbox.registerService(coordinateToolService); this.service = coordinateToolService; - this.originalProjection = this.mapModule.getProjection(); + this.mapProjection = this.mapModule.getProjection(); this.isReverseGeoCode = this.config.isReverseGeocode; this.reverseGeocodingIds = this.config.reverseGeocodingIds?.split(','); this.setState({ @@ -27,7 +27,7 @@ class UIHandler extends StateHandler { lonField: '', loading: false, showMouseCoordinates: false, - selectedProjection: this.originalProjection, + selectedProjection: this.mapProjection, emergencyInfo: null, reverseGeocodeNotImplementedError: false, reverseGeoCode: [], @@ -37,7 +37,6 @@ class UIHandler extends StateHandler { this.popupControls = null; this.eventHandlers = this.createEventHandlers(); this.decimalSeparator = Oskari.getDecimalSeparator(); - this.preciseTransform = Array.isArray(this.config.supportedProjections); this.popupListeners = []; this.addStateListener(() => this.updatePopup()); }; @@ -96,9 +95,18 @@ class UIHandler extends StateHandler { }); } + isTransformAllowed () { + if (typeof this.__transformAllowed !== 'undefined') { + return this.__transformAllowed; + } + const { supportedProjections } = this.config; + this.__transformAllowed = isTransformAllowed(supportedProjections); + return this.__transformAllowed; + } + // prefer proj specific and default to global format getProjectionShowFormat (selectedProj = this.state.selectedProjection) { - const projection = selectedProj || this.originalProjection; + const projection = selectedProj || this.mapProjection; const projConfig = this.config?.projectionShowFormat?.[projection] || {}; const globalFormat = this.config?.projectionShowFormat?.format; return projConfig.format || globalFormat; @@ -106,7 +114,7 @@ class UIHandler extends StateHandler { // prefer proj specific, global format, deprecated conf and default to mapmodule logic getProjectionDecimals (selectedProj = this.state.selectedProjection) { - const projection = selectedProj || this.originalProjection; + const projection = selectedProj || this.mapProjection; const conf = this.config; const projConfig = conf?.projectionShowFormat?.[projection] || {}; const globalFormat = conf?.projectionShowFormat?.decimals; @@ -140,7 +148,7 @@ class UIHandler extends StateHandler { if (data.lonlat.lat.indexOf('°') < 0) data.lonlat.lat = data.lonlat.lat + '°'; } - const converted = await this.convertCoordinates(data, this.state.selectedProjection, this.originalProjection); + const converted = await this.convertCoordinates(data, this.state.selectedProjection, this.mapProjection); this.updateLonLat(converted, true, true, true); } @@ -180,7 +188,7 @@ class UIHandler extends StateHandler { } async convertCoordinates (data, fromProjection, toProjection) { - if (this.preciseTransform && this.state.selectedProjection !== this.originalProjection) { + if (this.isTransformAllowed() && this.state.selectedProjection !== this.mapProjection) { if (Oskari.util.coordinateIsDegrees([data.lonlat?.lon, data.lonlat?.lat])) { data = this.coordinatesToMetric(data); } @@ -214,18 +222,18 @@ class UIHandler extends StateHandler { let fromServer = false; if (getDataFromServer) { - data = await this.convertCoordinates(data, this.originalProjection, this.state.selectedProjection); + data = await this.convertCoordinates(data, this.mapProjection, this.state.selectedProjection); fromServer = true; - } else if (this.preciseTransform && (this.state.selectedProjection !== this.originalProjection)) { + } else if (this.isTransformAllowed() && (this.state.selectedProjection !== this.mapProjection)) { try { - data = transformCoordinates(this.mapModule, data, this.originalProjection, this.state.selectedProjection); + data = transformCoordinates(this.mapModule, data, this.mapProjection, this.state.selectedProjection); } catch (e) { - data = await this.convertCoordinates(data, this.originalProjection, this.state.selectedProjection); + data = await this.convertCoordinates(data, this.mapProjection, this.state.selectedProjection); } } const isSupported = !!((this.config && Array.isArray(this.config.supportedProjections))); - const isDifferentProjection = !!((this.state.selectedProjection !== this.originalProjection && + const isDifferentProjection = !!((this.state.selectedProjection !== this.mapProjection && data?.lonlat?.lat !== 0 && data?.lonlat?.lon !== 0)); let lat = parseFloat(data?.lonlat?.lat); @@ -268,8 +276,6 @@ class UIHandler extends StateHandler { this.getController(), this.popupLocation(), this.config?.supportedProjections, - this.preciseTransform, - this.decimalSeparator, this.reverseGeocodingIds?.length > 2, () => this.popupCleanup() ); @@ -335,7 +341,7 @@ class UIHandler extends StateHandler { // Check at data is given. If data is given then use for it. // If not then use input data's and try change data to map projection and use it to place marker try { - data = data || transformCoordinates(this.mapModule, inputLonLatData, this.state.selectedProjection, this.originalProjection); + data = data || transformCoordinates(this.mapModule, inputLonLatData, this.state.selectedProjection, this.mapProjection); } catch (err) { // Cannot transform coordinates in transformCoordinates -function Messaging.error(this.loc('cannotTransformCoordinates.message')); @@ -386,7 +392,7 @@ class UIHandler extends StateHandler { } isCurrentUnitDegrees () { - const projection = this.state.selectedProjection || this.originalProjection; + const projection = this.state.selectedProjection || this.mapProjection; const formatDef = this.getProjectionShowFormat(projection); const defaultedToMapmodule = formatDef || this.mapModule.getProjectionUnits(); return defaultedToMapmodule === 'degrees'; @@ -396,7 +402,7 @@ class UIHandler extends StateHandler { this.setLoading(true); data = data || this.getMapXY(); try { - const response = await getTransformedCoordinates(this.originalProjection, data, fromProjection, toProjection); + const response = await getTransformedCoordinates(this.mapProjection, data, fromProjection, toProjection); if (response?.lat && response?.lon) { const newData = { lonlat: { @@ -484,13 +490,13 @@ class UIHandler extends StateHandler { async getEmergencyCallCoordinatesFromServer (data) { // get the transform from current data - const sourceProjection = this.originalProjection; + const sourceProjection = this.mapProjection; // If coordinates are not EPSG:4326 then // need to get 'EPSG:4326' coordinates from service if (sourceProjection !== 'EPSG:4326') { try { - const response = await getTransformedCoordinates(this.originalProjection, data, sourceProjection, 'EPSG:4326'); + const response = await getTransformedCoordinates(this.mapProjection, data, sourceProjection, 'EPSG:4326'); if (response?.lat && response?.lon) { const newData = { lonlat: { @@ -516,7 +522,7 @@ class UIHandler extends StateHandler { // update emergency if configured if (this.config.showEmergencyCallMessage) { // already in degrees, don't fetch again - if (this.isCurrentUnitDegrees() && this.originalProjection === 'EPSG:4326') { + if (this.isCurrentUnitDegrees() && this.mapProjection === 'EPSG:4326') { this.updateState({ emergencyInfo: this.formatEmergencyCallMessage({ lonlat: { diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx index df3dcef7a1..9282ebdf16 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx +++ b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx @@ -3,7 +3,7 @@ import { showPopup } from 'oskari-ui/components/window'; import { Message, Button, TextInput, Checkbox, Select, Option, Spin } from 'oskari-ui'; import { Content, CoordinateFields, CoordinateField, DegreeContainer, CoordinateLabel, SelectField, EmergencyInfo, SelectLabel, ReverseGeoCodes, Approximation } from './styled'; import { ButtonContainer } from 'oskari-ui/components/buttons'; -import { formatDegrees } from './helper'; +import { formatDegrees, isTransformAllowed } from './helper'; import PropTypes from 'prop-types'; const BUNDLE_KEY = 'coordinatetool'; @@ -21,8 +21,8 @@ const isMarkersSupported = () => { return !!builder; }; -const ProjectionChanger = ({ preciseTransform, projection, onChange, supportedProjections = [] }) => { - if (!preciseTransform || typeof onChange !== 'function') { +const ProjectionChanger = ({ projection, onChange, supportedProjections = [] }) => { + if (!isTransformAllowed(supportedProjections) || typeof onChange !== 'function') { return ( <Message bundleKey={BUNDLE_KEY} messageKey={`display.crs.${projection}`} fallback={ <Message bundleKey={BUNDLE_KEY} messageKey='display.crs.default' messageArgs={{ crs: projection }} /> @@ -48,7 +48,6 @@ const ProjectionChanger = ({ preciseTransform, projection, onChange, supportedPr }; ProjectionChanger.propTypes = { - preciseTransform: PropTypes.bool, projection: PropTypes.string, onChange: PropTypes.func, supportedProjections: PropTypes.array.isRequired, @@ -75,11 +74,12 @@ LonLabel.propTypes = { isDegrees: PropTypes.bool }; -const DegreesDisplay = ({ isDegrees, lon, lat, decimalSeparator, isLat }) => { +const DegreesDisplay = ({ isDegrees, lon, lat, isLat }) => { if (!isDegrees) { return null; } const dec = Oskari.util.coordinateDegreesToMetric([lon, lat], 20); + const decimalSeparator = Oskari.getDecimalSeparator(); const degmin = formatDegrees(dec[0], dec[1], 'min'); if (isLat) { return ( @@ -100,7 +100,6 @@ DegreesDisplay.propTypes = { isDegrees: PropTypes.bool, lon: PropTypes.string, lat: PropTypes.string, - decimalSeparator: PropTypes.string, isLat: PropTypes.bool }; @@ -179,7 +178,7 @@ ReverseGeocodeResults.propTypes = { results: PropTypes.array }; -const PopupContent = ({ state = {}, controller, preciseTransform, supportedProjections, decimalSeparator, showReverseGeoCodeCheckbox }) => { +const PopupContent = ({ state = {}, controller, supportedProjections, showReverseGeoCodeCheckbox }) => { const isDegrees = controller.isCurrentUnitDegrees(); return ( @@ -187,7 +186,6 @@ const PopupContent = ({ state = {}, controller, preciseTransform, supportedProje <Content> <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.info' /> <ProjectionChanger - preciseTransform={preciseTransform} projection={state.selectedProjection} onChange={value => controller.setSelectedProjection(value)} supportedProjections={supportedProjections} /> @@ -209,7 +207,6 @@ const PopupContent = ({ state = {}, controller, preciseTransform, supportedProje isDegrees={isDegrees} lat={state?.latField} lon={state.lonField} - decimalSeparator={decimalSeparator} isLat={true} /> <CoordinateField> <LonLabel isDegrees={isDegrees}/> @@ -228,7 +225,6 @@ const PopupContent = ({ state = {}, controller, preciseTransform, supportedProje isDegrees={isDegrees} lat={state?.latField} lon={state?.lonField} - decimalSeparator={decimalSeparator} isLat={false} /> </CoordinateFields> <HoverToggle @@ -270,13 +266,11 @@ const PopupContent = ({ state = {}, controller, preciseTransform, supportedProje PopupContent.propTypes = { state: PropTypes.object.isRequired, controller: PropTypes.object.isRequired, - preciseTransform: PropTypes.bool, - supportedProjections: PropTypes.array.isRequired, - decimalSeparator: PropTypes.string, + supportedProjections: PropTypes.array, showReverseGeoCodeCheckbox: PropTypes.bool }; -export const showCoordinatePopup = (state, controller, location, supportedProjections = [], preciseTransform, decimalSeparator, showReverseGeoCodeCheckbox, onClose) => { +export const showCoordinatePopup = (state, controller, location, supportedProjections = [], showReverseGeoCodeCheckbox, onClose) => { const mapModule = Oskari.getSandbox().findRegisteredModuleInstance('MainMapModule'); const options = { ...OPTIONS, @@ -288,9 +282,7 @@ export const showCoordinatePopup = (state, controller, location, supportedProjec <PopupContent state={state} controller={controller} - preciseTransform={preciseTransform} supportedProjections={supportedProjections} - decimalSeparator={decimalSeparator} showReverseGeoCodeCheckbox={showReverseGeoCodeCheckbox} />, onClose, options @@ -303,9 +295,7 @@ export const showCoordinatePopup = (state, controller, location, supportedProjec <PopupContent state={state} controller={controller} - preciseTransform={preciseTransform} supportedProjections={supportedProjections} - decimalSeparator={decimalSeparator} showReverseGeoCodeCheckbox={showReverseGeoCodeCheckbox} /> ); diff --git a/bundles/framework/coordinatetool/plugin/helper.js b/bundles/framework/coordinatetool/plugin/helper.js index aa73675c85..563eb9e6bd 100644 --- a/bundles/framework/coordinatetool/plugin/helper.js +++ b/bundles/framework/coordinatetool/plugin/helper.js @@ -112,3 +112,7 @@ export const formatDegrees = (lon, lat, type) => { }; } }; + +export const isTransformAllowed = (supportedProjections = []) => { + return Array.isArray(supportedProjections) && supportedProjections.length > 1; +}; From 4ee75d78e3321cc22857bbe7e6a162d62665d2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Tue, 19 Nov 2024 12:20:22 +0200 Subject: [PATCH 125/181] Move emergency info formatting to JSX --- .../plugin/CoordinatePluginHandler.js | 54 ++--- .../coordinatetool/plugin/CoordinatePopup.jsx | 197 ++++++++++-------- .../framework/coordinatetool/plugin/helper.js | 32 +-- .../framework/coordinatetool/plugin/styled.js | 2 +- 4 files changed, 147 insertions(+), 138 deletions(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index cf58087f3c..7b3314f68f 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -517,45 +517,35 @@ class UIHandler extends StateHandler { } async getEmergencyCallInfo (data) { - const xy = data || this.getMapXY(); - + if (!this.config.showEmergencyCallMessage) { + return; + } // update emergency if configured - if (this.config.showEmergencyCallMessage) { - // already in degrees, don't fetch again - if (this.isCurrentUnitDegrees() && this.mapProjection === 'EPSG:4326') { - this.updateState({ - emergencyInfo: this.formatEmergencyCallMessage({ - lonlat: { - lon: xy.lonlat.lon, - lat: xy.lonlat.lat - } - }) - }); - } else { - await this.getEmergencyCallCoordinatesFromServer(data) - .then(emergencyData => { - this.updateState({ - emergencyInfo: emergencyData - }); + const xy = data || this.getMapXY(); + // already in degrees, don't fetch again + if (this.isCurrentUnitDegrees() && this.mapProjection === 'EPSG:4326') { + this.updateState({ + emergencyInfo: this.formatEmergencyCallMessage({ + lonlat: { + lon: xy.lonlat.lon, + lat: xy.lonlat.lat + } + }) + }); + } else { + await this.getEmergencyCallCoordinatesFromServer(data) + .then(emergencyData => { + this.updateState({ + emergencyInfo: emergencyData }); - } + }); } } formatEmergencyCallMessage (data) { - const degmin = formatDegrees(data.lonlat.lon, data.lonlat.lat, 'min'); - - let minutesX = '' + parseFloat(degmin.minutesX.replace(this.decimalSeparator, '.')).toFixed(3); - minutesX = minutesX.replace('.', this.decimalSeparator); - - let minutesY = '' + parseFloat(degmin.minutesY.replace(this.decimalSeparator, '.')).toFixed(3); - minutesY = minutesY.replace('.', this.decimalSeparator); - return { - degreesX: this.loc('display.compass.i') + ' ' + degmin.degreesX, - degreesY: this.loc('display.compass.p') + ' ' + degmin.degreesY, - minutesX, - minutesY + lon: data.lonlat.lon, + lat: data.lonlat.lat }; } diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx index 9282ebdf16..ec0326f039 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx +++ b/bundles/framework/coordinatetool/plugin/CoordinatePopup.jsx @@ -1,8 +1,9 @@ import React from 'react'; import { showPopup } from 'oskari-ui/components/window'; import { Message, Button, TextInput, Checkbox, Select, Option, Spin } from 'oskari-ui'; -import { Content, CoordinateFields, CoordinateField, DegreeContainer, CoordinateLabel, SelectField, EmergencyInfo, SelectLabel, ReverseGeoCodes, Approximation } from './styled'; +import { Content, CoordinateFields, CoordinateField, DegreeContainer, CoordinateLabel, SelectField, EmergencyInfoContainer, SelectLabel, ReverseGeoCodes, Approximation } from './styled'; import { ButtonContainer } from 'oskari-ui/components/buttons'; +import { LocaleProvider } from 'oskari-ui/util'; import { formatDegrees, isTransformAllowed } from './helper'; import PropTypes from 'prop-types'; @@ -24,14 +25,14 @@ const isMarkersSupported = () => { const ProjectionChanger = ({ projection, onChange, supportedProjections = [] }) => { if (!isTransformAllowed(supportedProjections) || typeof onChange !== 'function') { return ( - <Message bundleKey={BUNDLE_KEY} messageKey={`display.crs.${projection}`} fallback={ - <Message bundleKey={BUNDLE_KEY} messageKey='display.crs.default' messageArgs={{ crs: projection }} /> + <Message messageKey={`display.crs.${projection}`} fallback={ + <Message messageKey='display.crs.default' messageArgs={{ crs: projection }} /> }/>); } return ( <SelectField> <SelectLabel> - <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.header' /> + <Message messageKey='display.coordinatesTransform.header' /> </SelectLabel> <Select value={projection} @@ -39,7 +40,7 @@ const ProjectionChanger = ({ projection, onChange, supportedProjections = [] }) > {supportedProjections.map(option => ( <Option key={option} value={option}> - <Message bundleKey={BUNDLE_KEY} messageKey={`display.coordinatesTransform.projections.${option}`} /> + <Message messageKey={`display.coordinatesTransform.projections.${option}`} /> </Option> )) } </Select> @@ -56,9 +57,9 @@ ProjectionChanger.propTypes = { const LatLabel = ({ isDegrees }) => { if (isDegrees) { - return <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.lat'} LabelComponent={ CoordinateLabel } />; + return <Message messageKey={'display.compass.lat'} LabelComponent={ CoordinateLabel } />; } - return <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.n'} LabelComponent={ CoordinateLabel } />; + return <Message messageKey={'display.compass.n'} LabelComponent={ CoordinateLabel } />; }; LatLabel.propTypes = { isDegrees: PropTypes.bool @@ -66,9 +67,9 @@ LatLabel.propTypes = { const LonLabel = ({ isDegrees }) => { if (isDegrees) { - return <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.lon'} LabelComponent={ CoordinateLabel } />; + return <Message messageKey={'display.compass.lon'} LabelComponent={ CoordinateLabel } />; } - return <Message bundleKey={BUNDLE_KEY} messageKey={'display.compass.e'} LabelComponent={ CoordinateLabel } />; + return <Message messageKey={'display.compass.e'} LabelComponent={ CoordinateLabel } />; }; LonLabel.propTypes = { isDegrees: PropTypes.bool @@ -112,7 +113,7 @@ const HoverToggle = ({ isShowing, onChange }) => { checked={isShowing} onChange={onChange} > - <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.showMouseCoordinates' /> + <Message messageKey='display.popup.showMouseCoordinates' /> </Checkbox> ); }; @@ -131,7 +132,7 @@ const MarkerButton = ({ isSupported, addMarker }) => { onClick={addMarker} className='t_add' > - <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.addMarkerButton' /> + <Message messageKey='display.popup.addMarkerButton' /> </Button> ); }; @@ -149,7 +150,7 @@ const ReverseGeocodeCheckBox = ({ showCheckbox, showResults, toggleResults }) => checked={showResults} onChange={toggleResults} > - <Message bundleKey={BUNDLE_KEY} messageKey='display.reversegeocode.moreInfo' /> + <Message messageKey='display.reversegeocode.moreInfo' /> </Checkbox> ); }; @@ -178,88 +179,104 @@ ReverseGeocodeResults.propTypes = { results: PropTypes.array }; +const EmergencyInfo = ({ coords }) => { + if (!coords) { + return null; + } + const degmin = formatDegrees(coords.lon, coords.lat, 'min', 3); + // Coordinates are now on separate row + // the text would flow better if emergencyCallLabel Message-tag is used as wrapper for the other content + // But it might be intended to be on a separate row for clarity? + return ( + <EmergencyInfoContainer> + <Message messageKey='display.coordinatesTransform.emergencyCallLabel' /> + <Message messageKey='display.compass.p'>{` ${degmin.degreesY}° `}{` ${formatLeadingZero(degmin.minutesY)}' `}</Message> + {' '}<Message messageKey='display.coordinatesTransform.emergencyCallLabelAnd' />{' '} + <Message messageKey='display.compass.i'> {` ${degmin.degreesX}° `}{` ${formatLeadingZero(degmin.minutesX)}'`}</Message> + </EmergencyInfoContainer> + ); +}; +EmergencyInfo.propTypes = { + coords: PropTypes.object +}; + const PopupContent = ({ state = {}, controller, supportedProjections, showReverseGeoCodeCheckbox }) => { const isDegrees = controller.isCurrentUnitDegrees(); return ( - <Spin spinning={state.loading}> - <Content> - <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.info' /> - <ProjectionChanger - projection={state.selectedProjection} - onChange={value => controller.setSelectedProjection(value)} - supportedProjections={supportedProjections} /> - <CoordinateFields> - <CoordinateField> - <LatLabel isDegrees={isDegrees}/> - <Approximation> - {state.approxValue && ('~')} - </Approximation> - <TextInput - value={state?.latField} - onChange={(e) => controller.setLatInputValue(e.target.value)} - onBlur={() => controller.useUserDefinedCoordinates()} - disabled={state.showMouseCoordinates} - className='t_lat' - /> - </CoordinateField> - <DegreesDisplay - isDegrees={isDegrees} - lat={state?.latField} - lon={state.lonField} - isLat={true} /> - <CoordinateField> - <LonLabel isDegrees={isDegrees}/> - <Approximation> - {state.approxValue && ('~')} - </Approximation> - <TextInput - value={state?.lonField} - onChange={(e) => controller.setLonInputValue(e.target.value)} - onBlur={() => controller.useUserDefinedCoordinates()} - disabled={state.showMouseCoordinates} - className='t_lon' - /> - </CoordinateField> - <DegreesDisplay - isDegrees={isDegrees} - lat={state?.latField} - lon={state?.lonField} - isLat={false} /> - </CoordinateFields> - <HoverToggle - isShowing={state.showMouseCoordinates} - onChange={() => controller.toggleMouseCoordinates()} /> - <ReverseGeocodeCheckBox - showCheckbox={showReverseGeoCodeCheckbox} - showResults={state.showReverseGeoCode} - toggleResults={() => controller.toggleReverseGeoCode()} /> - <ReverseGeocodeResults - showResults={!showReverseGeoCodeCheckbox || state.showReverseGeoCode} - results={state.reverseGeoCode} /> - {state.emergencyInfo && ( - <EmergencyInfo> - <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.emergencyCallLabel' /> - {` ${state.emergencyInfo.degreesY}° `}{` ${formatLeadingZero(state.emergencyInfo.minutesY)}' `} - <Message bundleKey={BUNDLE_KEY} messageKey='display.coordinatesTransform.emergencyCallLabelAnd' /> - {` ${state.emergencyInfo.degreesX}° `}{` ${formatLeadingZero(state.emergencyInfo.minutesX)}'`} - </EmergencyInfo> - )} - <ButtonContainer> - <Button - type='default' - disabled={state.showMouseCoordinates || controller.isMapCentered()} - onClick={() => controller.centerMap()} - className='t_center' - > - <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.searchButton' /> - </Button> - <MarkerButton - isSupported={isMarkersSupported()} - addMarker={() => controller.setMarker()} /> - </ButtonContainer> - </Content> - </Spin> + <LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> + <Spin spinning={state.loading}> + <Content> + <Message messageKey='display.popup.info' /> + <ProjectionChanger + projection={state.selectedProjection} + onChange={value => controller.setSelectedProjection(value)} + supportedProjections={supportedProjections} /> + <CoordinateFields> + <CoordinateField> + <LatLabel isDegrees={isDegrees}/> + <Approximation> + {state.approxValue && ('~')} + </Approximation> + <TextInput + value={state?.latField} + onChange={(e) => controller.setLatInputValue(e.target.value)} + onBlur={() => controller.useUserDefinedCoordinates()} + disabled={state.showMouseCoordinates} + className='t_lat' + /> + </CoordinateField> + <DegreesDisplay + isDegrees={isDegrees} + lat={state?.latField} + lon={state.lonField} + isLat={true} /> + <CoordinateField> + <LonLabel isDegrees={isDegrees}/> + <Approximation> + {state.approxValue && ('~')} + </Approximation> + <TextInput + value={state?.lonField} + onChange={(e) => controller.setLonInputValue(e.target.value)} + onBlur={() => controller.useUserDefinedCoordinates()} + disabled={state.showMouseCoordinates} + className='t_lon' + /> + </CoordinateField> + <DegreesDisplay + isDegrees={isDegrees} + lat={state?.latField} + lon={state?.lonField} + isLat={false} /> + </CoordinateFields> + <HoverToggle + isShowing={state.showMouseCoordinates} + onChange={() => controller.toggleMouseCoordinates()} /> + <ReverseGeocodeCheckBox + showCheckbox={showReverseGeoCodeCheckbox} + showResults={state.showReverseGeoCode} + toggleResults={() => controller.toggleReverseGeoCode()} /> + <ReverseGeocodeResults + showResults={!showReverseGeoCodeCheckbox || state.showReverseGeoCode} + results={state.reverseGeoCode} /> + <EmergencyInfo coords={state.emergencyInfo} /> + <ButtonContainer> + <Button + type='default' + disabled={state.showMouseCoordinates || controller.isMapCentered()} + onClick={() => controller.centerMap()} + className='t_center' + > + <Message bundleKey={BUNDLE_KEY} messageKey='display.popup.searchButton' /> + </Button> + <MarkerButton + isSupported={isMarkersSupported()} + addMarker={() => controller.setMarker()} /> + </ButtonContainer> + </Content> + </Spin> + </LocaleProvider> ); }; diff --git a/bundles/framework/coordinatetool/plugin/helper.js b/bundles/framework/coordinatetool/plugin/helper.js index 563eb9e6bd..73a074dfa2 100644 --- a/bundles/framework/coordinatetool/plugin/helper.js +++ b/bundles/framework/coordinatetool/plugin/helper.js @@ -75,7 +75,7 @@ export const transformCoordinates = (mapModule, data, srs, targetSRS) => { /** * format different degree presentations of lon/lat coordinates */ -export const formatDegrees = (lon, lat, type) => { +export const formatDegrees = (lon, lat, type, accuracy) => { let degreesX, degreesY, minutesX, @@ -87,28 +87,30 @@ export const formatDegrees = (lon, lat, type) => { case 'min': degreesX = parseInt(lon); degreesY = parseInt(lat); - minutesX = Number((lon - degreesX) * 60).toFixed(5); - minutesY = Number((lat - degreesY) * 60).toFixed(5); + // defaults to 5 and 0 ?? 5 = 0 (undefined ?? 5 = 5) + minutesX = Number((lon - degreesX) * 60).toFixed(accuracy ?? 5); + minutesY = Number((lat - degreesY) * 60).toFixed(accuracy ?? 5); return { - 'degreesX': degreesX, - 'degreesY': degreesY, - 'minutesX': minutesX.replace('.', Oskari.getDecimalSeparator()), - 'minutesY': minutesY.replace('.', Oskari.getDecimalSeparator()) + degreesX, + degreesY, + minutesX: minutesX.replace('.', Oskari.getDecimalSeparator()), + minutesY: minutesY.replace('.', Oskari.getDecimalSeparator()) }; case 'sec': degreesX = parseInt(lon); degreesY = parseInt(lat); minutesX = parseFloat((lon - degreesX) * 60); minutesY = parseFloat((lat - degreesY) * 60); - secondsX = parseFloat((minutesX - parseInt(minutesX)) * 60).toFixed(3); - secondsY = parseFloat((minutesY - parseInt(minutesY)) * 60).toFixed(3); + // Note! Default to 3 instead of 5 + secondsX = parseFloat((minutesX - parseInt(minutesX)) * 60).toFixed(accuracy ?? 3); + secondsY = parseFloat((minutesY - parseInt(minutesY)) * 60).toFixed(accuracy ?? 3); return { - 'degreesX': degreesX, - 'degreesY': degreesY, - 'minutesX': parseInt(minutesX), - 'minutesY': parseInt(minutesY), - 'secondsX': secondsX.replace('.', Oskari.getDecimalSeparator()), - 'secondsY': secondsY.replace('.', Oskari.getDecimalSeparator()) + degreesX, + degreesY, + minutesX: parseInt(minutesX), + minutesY: parseInt(minutesY), + secondsX: secondsX.replace('.', Oskari.getDecimalSeparator()), + secondsY: secondsY.replace('.', Oskari.getDecimalSeparator()) }; } }; diff --git a/bundles/framework/coordinatetool/plugin/styled.js b/bundles/framework/coordinatetool/plugin/styled.js index 239a97973f..2cb65c788c 100644 --- a/bundles/framework/coordinatetool/plugin/styled.js +++ b/bundles/framework/coordinatetool/plugin/styled.js @@ -40,7 +40,7 @@ export const SelectField = styled('div')` flex-direction: column; `; -export const EmergencyInfo = styled('div')` +export const EmergencyInfoContainer = styled('div')` margin-top: 10px; `; From a9205f11c9ed36093fe996111938533ca74717d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Mon, 18 Nov 2024 19:49:37 +0200 Subject: [PATCH 126/181] Allow fallback prop for Message --- src/react/components/Message.jsx | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/react/components/Message.jsx b/src/react/components/Message.jsx index ac022586c4..a54662e1bd 100644 --- a/src/react/components/Message.jsx +++ b/src/react/components/Message.jsx @@ -4,10 +4,10 @@ import { LocaleConsumer } from 'oskari-ui/util'; import styled from 'styled-components'; const Label = styled('div')` - display: ${props => props.allowTextEllipsis ? 'inline': 'inline-block'}; - overflow: ${props => props.allowTextEllipsis ? 'hidden': ''}; - white-space: ${props => props.allowTextEllipsis ? 'nowrap': ''}; - text-overflow: ${props => props.allowTextEllipsis ? 'ellipsis': ''}; + display: ${props => props.allowTextEllipsis ? 'inline' : 'inline-block'}; + overflow: ${props => props.allowTextEllipsis ? 'hidden' : ''}; + white-space: ${props => props.allowTextEllipsis ? 'nowrap' : ''}; + text-overflow: ${props => props.allowTextEllipsis ? 'ellipsis' : ''}; `; /** @@ -34,7 +34,7 @@ const Label = styled('div')` * <Message messageKey="hello" messageArgs={['Jack']}/> * </LocaleProvider> */ -const Message = ({ bundleKey, messageKey, messageArgs, defaultMsg, getMessage, children, LabelComponent = Label, allowHTML = false, allowTextEllipsis = false }) => { +const Message = ({ bundleKey, messageKey, messageArgs, defaultMsg, getMessage, fallback, children, LabelComponent = Label, allowHTML = false, allowTextEllipsis = false }) => { if (!messageKey) { return null; } @@ -49,18 +49,22 @@ const Message = ({ bundleKey, messageKey, messageArgs, defaultMsg, getMessage, c // If we didn't find localization AND we have default value -> use it if (message === messageKey && defaultMsg) { - message = defaultMsg; + if (defaultMsg) { + message = defaultMsg; + } else if (fallback) { + return fallback; + } } if (allowHTML) { - return ( <LabelComponent dangerouslySetInnerHTML={{ __html:message }}></LabelComponent> ); + return (<LabelComponent dangerouslySetInnerHTML={{ __html: message }}></LabelComponent>); } return ( <LabelComponent allowTextEllipsis={allowTextEllipsis} onClick={() => Oskari.log().debug(`Text clicked - ${bundleKey}: ${messageKey}`)}> - { message } { children } + { message } { children } </LabelComponent> ); }; @@ -72,20 +76,20 @@ Message.propTypes = { getMessage: PropTypes.func, children: PropTypes.node, LabelComponent: PropTypes.elementType, + fallback: PropTypes.node, allowHTML: PropTypes.bool, allowTextEllipsis: PropTypes.bool }; -function getMessageUsingOskariGlobal(bundleKey, messageKey, messageArgs) { +function getMessageUsingOskariGlobal (bundleKey, messageKey, messageArgs) { try { return Oskari.getMsg(bundleKey, messageKey, messageArgs); - } catch(e) { + } catch (e) { // no locale provider OR bundleKey missing from locale provider Oskari.log().warn(`Message tag used without LocaleProvider or bundleKey not provided when getting: ${messageKey}. Original error: ${e.message}`); } return messageKey; } - const wrapped = LocaleConsumer(Message); export { wrapped as Message }; From 9f619d871a3bdab94952e3b50aacc1563f04d86a Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 12:21:40 +0200 Subject: [PATCH 127/181] add prop validation and fix required --- .../description/MetadataContent.jsx | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/bundles/statistics/statsgrid/components/description/MetadataContent.jsx b/bundles/statistics/statsgrid/components/description/MetadataContent.jsx index 1801c0a44c..cce5745ec9 100644 --- a/bundles/statistics/statsgrid/components/description/MetadataContent.jsx +++ b/bundles/statistics/statsgrid/components/description/MetadataContent.jsx @@ -3,13 +3,29 @@ import PropTypes from 'prop-types'; import { Message } from 'oskari-ui'; import styled from 'styled-components'; -export const MetadataContent = ({description, source, metadata = {}}) => { +const bTag = styled.b``; + +const DataLabel = ({ labelKey, value }) => { + if (typeof value !== 'number' && !value) { + return null; + } + return ( + <React.Fragment> + <Message LabelComponent={bTag} messageKey={labelKey} />: {value}<br /> + </React.Fragment>); +}; +DataLabel.propTypes = { + labelKey: PropTypes.string.isRequired, + value: PropTypes.any +}; + +export const MetadataContent = ({ description, source, metadata = {} }) => { if (!description) { return (<Message messageKey='metadataPopup.noMetadata' messageArgs={{ indicators: 1 }}/>); } // Description can include HTML so well have to wrap it as HTML content... return (<React.Fragment> - <p dangerouslySetInnerHTML={{__html: description}} /> + <p dangerouslySetInnerHTML={{ __html: description }} /> <p> <DataLabel labelKey='panels.newSearch.datasourceTitle' value={source} /> <DataLabel labelKey='metadataPopup.updated' value={metadata.updated} /> @@ -17,20 +33,8 @@ export const MetadataContent = ({description, source, metadata = {}}) => { </p> </React.Fragment>); }; - -const bTag = styled.b``; -const DataLabel = ({labelKey, value}) => { - if (typeof value !== 'number' && !value) { - return null; - } - return ( - <React.Fragment> - <Message LabelComponent={bTag} messageKey={labelKey} />: {value}<br /> - </React.Fragment>); -} - MetadataContent.propTypes = { - description: PropTypes.string.isRequired, - source: PropTypes.string.isRequired, + description: PropTypes.string, + source: PropTypes.string, metadata: PropTypes.object }; From 129129f2f3f789ba3cb4c8346e14cf6fd2b24c85 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 12:32:35 +0200 Subject: [PATCH 128/181] fix cache update, prepare popup, name validation, eslint --- .../statsgrid/handler/IndicatorFormHandler.js | 107 ++++++++---------- 1 file changed, 50 insertions(+), 57 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js index d33bf8ad00..385cf57451 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js +++ b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js @@ -22,7 +22,7 @@ class IndicatorFormController extends StateHandler { async showIndicatorPopup (ds, id = null) { if (!ds) return; this.reset(); - await this.preparePopupData(ds, id); + await this.preparePopupData({ id, ds }); this.instance.getViewHandler()?.show('indicatorForm'); } @@ -32,7 +32,7 @@ class IndicatorFormController extends StateHandler { getInitState () { return { - indicator: {}, // TODO: is it ok to pass additional info (name,..) for other handlers?? + indicator: {}, datasets: [], selection: '', regionset: null, @@ -46,40 +46,36 @@ class IndicatorFormController extends StateHandler { this.updateState(this.getInitState()); } - async preparePopupData (ds, id) { + async preparePopupData (partialIndicator) { this.updateState({ loading: true }); - const indicator = { id, ds }; - const datasets = await this.populateIndicatorFromMetadata(indicator); + const { datasets, indicator } = await this.populateIndicatorFromMetadata(partialIndicator); this.updateState({ indicator, datasets, loading: false }); } - async populateIndicatorFromMetadata (indicator) { + async populateIndicatorFromMetadata ({ ds, id }) { const datasets = []; - if (indicator.id && indicator.ds) { + let indicator = { ds, id }; + if (id && ds) { try { - const { selectors, regionsets, name, description, source } = await getIndicatorMetadata(indicator.ds, indicator.id); - indicator.name = name; - indicator.description = description; - indicator.source = source; - selectors.forEach(sel => regionsets.forEach(regionset => sel.allowedValues.forEach(val => datasets.push({ [sel.id]: val.id, regionset })))); + const { selectors, regionsets, ...fromMeta } = await getIndicatorMetadata(ds, id); + indicator = { ...indicator, ...fromMeta }; + // create dataset for every value and regionset compination like {year: 2024, regionset: 2036} + selectors.forEach(({ values, id }) => regionsets.forEach(regionset => values.forEach(({ value }) => datasets.push({ [id]: value, regionset })))); } catch (error) { Messaging.error(Oskari.getMsg('StatsGrid', 'errors.indicatorMetadataError')); } } - return datasets; + return { datasets, indicator }; } - // TODO: is this needed? - async getIndicatorDatasets (indicator) { - try { - const { selectors, regionsets, name, source, description } = await getIndicatorMetadata(indicator.ds, indicator.id); - const datasets = []; - // create dataset for every value and regionset compination like {year: 2024, regionset: 2036} - selectors.forEach(s => s.values.forEach(valObj => regionsets.forEach(regionset => datasets.push({ [s.id]: valObj.value, regionset })))); - this.updateState({ datasets, indicator: { ...indicator, name, source, description } }); - } catch (error) { - Messaging.error(Oskari.getMsg('StatsGrid', 'errors.indicatorMetadataError')); + getFullIndicator (optSelection) { + const { indicator, selection } = this.getState(); + const selections = { [SELECTOR]: optSelection || selection }; + const full = { ...indicator, selections }; + if (full.id) { + full.hash = getHashForIndicator(full); } + return full; } updateIndicator (key, value) { @@ -115,14 +111,14 @@ class IndicatorFormController extends StateHandler { async showDataTable () { this.updateState({ loading: true, showDataTable: true }); - const { indicator, regionset, selection } = this.getState(); - const selections = { [SELECTOR]: selection }; + const indicator = this.getFullIndicator(); + const { regionset } = this.getState(); try { const regions = await getRegionsAsync(regionset); let data = {}; if (indicator.id) { try { - data = await getIndicatorData({ ...indicator, selections }, regionset); + data = await getIndicatorData(indicator, regionset); } catch (e) { // no data saved for selections } @@ -139,7 +135,6 @@ class IndicatorFormController extends StateHandler { } closeDataTable () { - // TODO: selection, regionset?? this.updateState({ loading: false, showDataTable: false, @@ -148,13 +143,17 @@ class IndicatorFormController extends StateHandler { } async saveIndicator () { - this.updateState({ loading: true }); const { indicator } = this.getState(); + if (typeof indicator.name !== 'string' || indicator.name.trim().length === 0) { + Messaging.warn(this.loc('userIndicators.validate.name')); + return; + } + this.updateState({ loading: true }); try { const id = await saveIndicator(indicator); const updated = { ...indicator, id }; this.updateState({ indicator: updated, loading: false }); - this.notifyCacheUpdate(updated); + this.notifyCacheUpdate(updated, 'save'); this.log.info(`Saved indicator with id: ${id}`, updated); Messaging.success(this.loc('userIndicators.success.indicatorSave')); } catch (error) { @@ -166,13 +165,7 @@ class IndicatorFormController extends StateHandler { async saveData () { this.updateState({ loading: true }); const { dataByRegions, regionset, selection } = this.getState(); - const selections = { [SELECTOR]: selection }; - const indicator = { ...this.getState().indicator, selections }; - if (typeof indicator.name !== 'string' || indicator.name.trim().length === 0) { - Messaging.warn(this.loc('userIndicators.validate.name')); - return; - } const data = {}; dataByRegions.forEach(({ key, value }) => { if (typeof value === 'undefined') { @@ -189,36 +182,36 @@ class IndicatorFormController extends StateHandler { return; } try { + const indicator = this.getFullIndicator(); await saveIndicatorData(indicator, data, regionset); const indicatorInfo = `Indicator: ${indicator.id}, selection: ${selection}, regionset: ${regionset}.`; this.log.info('Saved data form values', data, indicatorInfo); Messaging.success(this.loc('userIndicators.success.datasetSave')); - // add indicator only when data is saved - const dataset = { ...selections, regionset }; - this.selectIndicator(dataset); - - this.updateState({ datasets: [...this.getState().datasets, dataset] }); + this.selectIndicator(selection, regionset); + this.preparePopupData(indicator); this.closeDataTable(); - this.notifyCacheUpdate(indicator); + this.notifyCacheUpdate(indicator, 'data'); } catch (error) { - this.updateSate({ loading: false }); + this.updateState({ loading: false }); Messaging.error(this.loc('userIndicators.error.datasetSave')); } } - notifyCacheUpdate (indicator) { + notifyCacheUpdate (indicator, operation) { this.instance.getSearchHandler()?.onCacheUpdate(indicator); + this.instance.getMyIndicatorsHandler()?.refreshIndicatorsList(); + if (operation !== 'delete') { + this.instance.getStateHandler()?.onCacheUpdate(indicator, operation === 'data'); + } } - selectIndicator (dataset) { - const selections = { [SELECTOR]: dataset[SELECTOR] }; - const indicator = { ...this.getState().indicator, selections }; - indicator.hash = getHashForIndicator(indicator); - this.instance.getStateHandler()?.getController().selectSavedIndicator(indicator, dataset.regionset); - } - - selectSavedIndicator (indicator, regionset) { - this.instance.getStateHandler()?.getController().selectSavedIndicator(indicator, regionset); + async selectIndicator (selection, regionset) { + const indicator = this.getFullIndicator(selection); + const handler = this.instance.getStateHandler(); + const { success } = await handler?.addIndicator(indicator, regionset) || {}; + if (success) { + handler?.setActiveIndicator(indicator.hash); + } } importFromClipboard (data) { @@ -261,12 +254,12 @@ class IndicatorFormController extends StateHandler { async deleteDataset (item = {}) { const selection = item[SELECTOR]; if (!selection) { - // without selection deletes all datasets + // should always have selection + // without selection deleteIndicator removes all datasets + Messaging.error(this.loc('userIndicators.error.datasetDelete')); return; } - const selections = { [SELECTOR]: selection }; - const indicator = { ...this.getState().indicator, selections }; - indicator.hash = getHashForIndicator(indicator); + const indicator = this.getFullIndicator(selection); const handler = this.instance.getStateHandler(); if (handler?.isIndicatorSelected(indicator, true)) { handler.getController().removeIndicator(indicator); @@ -278,7 +271,7 @@ class IndicatorFormController extends StateHandler { Messaging.error(this.loc('userIndicators.error.datasetDelete')); } this.preparePopupData(indicator); - this.notifyCacheUpdate(indicator); + this.notifyCacheUpdate(indicator, 'delete'); } } From 1d0c12f325abac70d409d90ef0842b817f7486c4 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 12:49:17 +0200 Subject: [PATCH 129/181] fix cached selectors and cache handling, disable delete indicator from guest --- .../statsgrid/handler/IndicatorHelper.js | 42 ++++++++++--------- bundles/statistics/statsgrid/instance.js | 7 +++- .../statsgrid/view/Form/DatasetsTable.jsx | 15 +++---- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/IndicatorHelper.js b/bundles/statistics/statsgrid/handler/IndicatorHelper.js index 88c30e0bb1..572ead0c9c 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorHelper.js +++ b/bundles/statistics/statsgrid/handler/IndicatorHelper.js @@ -13,7 +13,6 @@ const getDataCacheKey = (indicator, regionsetId) => { return 'hash_' + hash + '_rs_' + regionsetId; }; const isRuntime = (indicator = {}) => typeof indicator.id === 'string' && indicator.id.startsWith(RUNTIME); - // for guest user's own indicators const updateIndicatorMetadataInCache = (indicator, regionsetId) => { const { selections = {}, ...restToStore } = indicator; @@ -52,12 +51,12 @@ const updateIndicatorMetadataInCache = (indicator, regionsetId) => { cachedSelectors.push(selector); return; } - selector.values.forEach(value => { - if (cached.values.some(v => v.id === value.id)) { + selector.values.forEach(val => { + if (cached.values.some(v => v.value === val.value)) { // already added return; } - cached.values.push(value); + cached.values.push(val); }); }); }; @@ -113,7 +112,7 @@ const processMetadata = (meta) => { const metaSelectors = meta.selectors || []; const selectors = []; metaSelectors.forEach(metaSelector => { - const { id, allowedValues, time = false } = metaSelector; + const { id, allowedValues = [], time = false } = metaSelector; const values = allowedValues.map(val => { // value or { name, id } const value = val.id || val; @@ -122,6 +121,10 @@ const processMetadata = (meta) => { // use value, label to use as Select option return { value, label }; }); + if (!values.length) { + // don't add selector without values + return; + } const label = locParams[id] || id; const selector = { id, values, time, label }; if (time) { @@ -216,20 +219,21 @@ export const saveIndicator = async (indicator) => { return id; } // All keys used in Frontend doesn't match backend + const { id, ds, name, description = '', source = '' } = indicator; const body = { - datasource: indicator.ds, - name: indicator.name, - desc: indicator.description, - source: indicator.source + datasource: ds, + name, + desc: description, + source }; - if (indicator.id) { - body.id = indicator.id; + if (id) { + body.id = id; } try { const response = await fetch(Oskari.urls.getRoute('SaveIndicator'), { method: 'POST', headers: { - 'Accept': 'application/json' + Accept: 'application/json' }, body: new URLSearchParams(body) }); @@ -238,7 +242,7 @@ export const saveIndicator = async (indicator) => { } const result = await response.json(); // Flush cache as update is implemented only for quest user - removeIndicatorFromCache({ ds: indicator.ds }); + removeIndicatorFromCache({ ds }); return result.id; } catch (error) { throw new Error('Error saving data to server'); @@ -285,6 +289,9 @@ export const saveIndicatorData = async (indicator, data, regionsetId) => { }; // selectors and regionset are optional -> will only delete dataset from indicator if given export const deleteIndicator = async (indicator, regionsetId) => { + if (!Oskari.user().isLoggedIn()) { + throw new Error('Guest user can not delete indicator'); + } const flushDataCache = () => { // clearCacheOnDelete const cacheKey = getDataCacheKey(indicator, regionsetId); @@ -301,12 +308,6 @@ export const deleteIndicator = async (indicator, regionsetId) => { }); } }; - if (!Oskari.user().isLoggedIn()) { - // just flush caches - flushDataCache(); - removeIndicatorFromCache(indicator); - return; - } const data = { datasource: indicator.ds, id: indicator.id @@ -321,7 +322,7 @@ export const deleteIndicator = async (indicator, regionsetId) => { method: 'POST', body: new URLSearchParams(data), headers: { - 'Accept': 'application/json' + Accept: 'application/json' } }); if (!response.ok) { @@ -331,6 +332,7 @@ export const deleteIndicator = async (indicator, regionsetId) => { flushDataCache(); // Flush caches as update is implemented only for quest user removeIndicatorFromCache({ ds: indicator.ds }); + flushIndicatorMetadataCache(indicator); } catch (error) { throw new Error('Error on server'); } diff --git a/bundles/statistics/statsgrid/instance.js b/bundles/statistics/statsgrid/instance.js index 6b9f4c86e9..6c567e1f9b 100644 --- a/bundles/statistics/statsgrid/instance.js +++ b/bundles/statistics/statsgrid/instance.js @@ -83,8 +83,8 @@ Oskari.clazz.define( const myDataService = sandbox.getService('Oskari.mapframework.bundle.mydata.service.MyDataService'); if (myDataService) { - const indHandler = new MyIndicatorsHandler(this, formHandler, userDsId); - myDataService.addTab('indicators', this.loc('tab.title'), MyIndicatorsTab, indHandler); + this.myIndicatorsHandler = new MyIndicatorsHandler(this, formHandler, userDsId); + myDataService.addTab('indicators', this.loc('tab.title'), MyIndicatorsTab, this.myIndicatorsHandler); } else if (!appStarted) { // Wait for the application to load all bundles and try again Oskari.on('app.start', () => { @@ -111,6 +111,9 @@ Oskari.clazz.define( getSearchHandler: function () { return this.searchHandler; }, + getMyIndicatorsHandler: function () { + return this.myIndicatorsHandler; + }, removeDataProviverInfo: function (ind) { const service = this.getDataProviderInfoService(); if (service) { diff --git a/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx b/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx index 3561563c02..6513631ff1 100644 --- a/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx +++ b/bundles/statistics/statsgrid/view/Form/DatasetsTable.jsx @@ -24,6 +24,7 @@ export const DatasetsTable = ({ state, controller }) => { if (!datasets.length) { return <Message messageKey='userIndicators.datasets.noDatasets'/>; } + const allowDelete = Oskari.user().isLoggedIn(); const getRegionsetName = id => getRegionsets().find(rs => rs.id === id)?.name || ''; const columnSettings = [ { @@ -34,7 +35,7 @@ export const DatasetsTable = ({ state, controller }) => { render: (title, item) => { const { year, regionset } = item; return ( - <a onClick={() => controller.selectIndicator(item)}> + <a onClick={() => controller.selectIndicator(year, regionset)}> <Message messageKey='parameters.year' /> {year} - {getRegionsetName(regionset)} </a> ); @@ -48,12 +49,12 @@ export const DatasetsTable = ({ state, controller }) => { <ButtonContainer> <IconButton type='edit' - onClick={() => controller.editDataset(item)} - /> - <IconButton - type='delete' - onConfirm={() => controller.deleteDataset(item)} - /> + onClick={() => controller.editDataset(item)}/> + {allowDelete && + <IconButton + type='delete' + onConfirm={() => controller.deleteDataset(item)}/> + } </ButtonContainer> ); } From 0c0622e9652861817012b904b623986964bf664e Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 12:49:58 +0200 Subject: [PATCH 130/181] fix loc keys --- .../statistics/statsgrid/handler/MyIndicatorsHandler.js | 7 ++++--- bundles/statistics/statsgrid/resources/locale/fi.js | 2 +- bundles/statistics/statsgrid/resources/locale/fr.js | 2 +- bundles/statistics/statsgrid/resources/locale/ru.js | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js b/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js index 03f1bbdfd4..a03097c752 100644 --- a/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js +++ b/bundles/statistics/statsgrid/handler/MyIndicatorsHandler.js @@ -53,7 +53,7 @@ class IndicatorsHandler extends AsyncStateHandler { const indicator = this.getState().indicators.find(ind => ind.id === id); if (!indicator) { // couldn't find indicator -> show an error - Messaging.error(this.loc('tab.error.notfound')); + Messaging.error(this.loc('userIndicators.error.indicatorNotfound')); } return indicator; } @@ -61,16 +61,17 @@ class IndicatorsHandler extends AsyncStateHandler { async deleteIndicator (id) { const indicator = this.getIndicatorById(id); if (!indicator) { - Messaging.error(this.loc('tab.error.notdeleted')); + Messaging.error(this.loc('userIndicators.error.indicatorDelete')); return; } this.updateState({ loading: true }); try { // removes all indicator data (no selections or regionset) await deleteIndicator({ ...indicator, ds: this.userDsId }); - Messaging.success(this.loc('tab.popup.deleteSuccess')); + Messaging.success(this.loc('userIndicators.success.indicatorDelete')); this.refreshIndicatorsList(); } catch (error) { + Messaging.error(this.loc('userIndicators.error.indicatorDelete')); this.updateState({ loading: false }); } } diff --git a/bundles/statistics/statsgrid/resources/locale/fi.js b/bundles/statistics/statsgrid/resources/locale/fi.js index cbd31ef822..9008c68010 100644 --- a/bundles/statistics/statsgrid/resources/locale/fi.js +++ b/bundles/statistics/statsgrid/resources/locale/fi.js @@ -278,7 +278,7 @@ Oskari.registerLocalization({ 'error': { 'indicatorSave': 'Indikaattorin tallennus epäonnistui', 'indicatorDelete': 'Indikaattorin poisto epäonnistui', - 'IndicatorNotfound': 'Indikaattoria ei löytynyt', + 'indicatorNotfound': 'Indikaattoria ei löytynyt', 'datasetSave': 'Virhe tallennetaessa aineistoa', 'datasetDelete': 'Virhe poistaessa aineistoa' }, diff --git a/bundles/statistics/statsgrid/resources/locale/fr.js b/bundles/statistics/statsgrid/resources/locale/fr.js index 6da577f673..16e43fafdb 100644 --- a/bundles/statistics/statsgrid/resources/locale/fr.js +++ b/bundles/statistics/statsgrid/resources/locale/fr.js @@ -259,7 +259,7 @@ Oskari.registerLocalization( "error": { "indicatorSave": "Erreur lors de l'enregistrement de l'indicateur", "indicatorDelete": "L'indicateur n'a pas été supprimé", - "IndicatorNotfound": "Impossible de trouver l'indicateur", + "indicatorNotfound": "Impossible de trouver l'indicateur", "datasetSave": "Erreur lors de l'enregistrement du jeu de données", "datasetDelete": "Erreur lors de la suppression du jeu de données" }, diff --git a/bundles/statistics/statsgrid/resources/locale/ru.js b/bundles/statistics/statsgrid/resources/locale/ru.js index 4b13e1a648..8137c3bedc 100644 --- a/bundles/statistics/statsgrid/resources/locale/ru.js +++ b/bundles/statistics/statsgrid/resources/locale/ru.js @@ -258,7 +258,7 @@ Oskari.registerLocalization( "error": { "indicatorSave": "Индикатор сохранения ошибок", "indicatorDelete": "Индикатор не был удален.", - "IndicatorNotfound": "Индикатор не найден.", + "indicatorNotfound": "Индикатор не найден.", "datasetSave": "Ошибка сохранения набора данных.", "datasetDelete": "Ошибка удаления набора данных." }, From ca98e8d2dfb28ce609dab7a536a17894f44f3807 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 12:50:57 +0200 Subject: [PATCH 131/181] fix typo --- .../statsgrid/handler/SearchHandler.js | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index fa65b66336..3c3e99baa0 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -23,7 +23,7 @@ class SearchController extends AsyncStateHandler { const ds = getDatasources().length === 1 ? getDatasources()[0] : null; if (ds && !isReset) { // prepopulate indicator options - setTimeout(() => this.fetchindicatorOptions(ds.id), 10000); + setTimeout(() => this.fetchIndicatorOptions(ds.id), 10000); } return { searchTimeseries: false, @@ -47,7 +47,7 @@ class SearchController extends AsyncStateHandler { async populateForm (indicator) { const { id, ds } = indicator; this.updateState({ selectedDatasource: ds }); - await this.fetchindicatorOptions(); + await this.fetchIndicatorOptions(); const indicators = id ? [id] : []; this.setSelectedIndicators(indicators); } @@ -55,14 +55,14 @@ class SearchController extends AsyncStateHandler { onCacheUpdate (indicator = {}) { const { selectedDatasource, selectedIndicators } = this.getState(); if (selectedDatasource === indicator.ds) { - this.fetchindicatorOptions(); + this.fetchIndicatorOptions(); } if (selectedIndicators.includes(indicator.id)) { - this.fetchIndicatorParams(); + this.fetchIndicatorParamOptions(); } } - async fetchindicatorOptions () { + async fetchIndicatorOptions () { const { selectedDatasource, isUserDatasource } = this.getState(); if (!selectedDatasource) { return; @@ -169,16 +169,19 @@ class SearchController extends AsyncStateHandler { indicatorParams: {}, selectedRegionset: null }); - this.fetchindicatorOptions(); + this.fetchIndicatorOptions(); } setSelectedIndicators (selectedIndicators) { this.updateState({ selectedIndicators }); - if (!selectedIndicators.length) { + this.fetchIndicatorParamOptions(); + } + + fetchIndicatorParamOptions () { + const { selectedIndicators } = this.getState(); + if (selectedIndicators.length === 0) { this.updateState({ indicatorParams: {}, selectedRegionset: null }); - return; - } - if (selectedIndicators.length > 1) { + } else if (selectedIndicators.length > 1) { this.handleMultipleIndicatorParams(selectedIndicators); } else { this.handleSingleIndicatorParams(selectedIndicators[0]); From ce3d98b803888a5be001b501b6ab8b8852629a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Tue, 19 Nov 2024 12:51:55 +0200 Subject: [PATCH 132/181] Remove unused import --- .../framework/coordinatetool/plugin/CoordinatePluginHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index 7b3314f68f..5175dcb6ec 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -1,7 +1,7 @@ import { StateHandler, controllerMixin, Messaging } from 'oskari-ui/util'; import { showCoordinatePopup } from './CoordinatePopup'; import { PLACEMENTS } from 'oskari-ui/components/window'; -import { getTransformedCoordinates, transformCoordinates, formatDegrees, isTransformAllowed } from './helper'; +import { getTransformedCoordinates, transformCoordinates, isTransformAllowed } from './helper'; class UIHandler extends StateHandler { constructor (plugin, mapModule, config) { From 6a31c677c45b208d7db133a3e94cfeea36b15cf0 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 12:54:00 +0200 Subject: [PATCH 133/181] removing last indicator on update will reset state, use onCacheUpdate instead of removing --- .../statsgrid/handler/StatisticsHandler.js | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/StatisticsHandler.js b/bundles/statistics/statsgrid/handler/StatisticsHandler.js index 436bfa079e..88c323bb00 100644 --- a/bundles/statistics/statsgrid/handler/StatisticsHandler.js +++ b/bundles/statistics/statsgrid/handler/StatisticsHandler.js @@ -216,16 +216,29 @@ class StatisticsController extends AsyncStateHandler { } } - async selectSavedIndicator (indicator, regionset) { - if (this.isIndicatorSelected(indicator, true)) { - // remove indicator first to get updated indicator and data - this.removeIndicator(indicator); + async onCacheUpdate (indicator, onlyData) { + // use strict (hash) comparison for only data update + if (!this.isIndicatorSelected(indicator, onlyData)) { + // nothing to update + return; } - const response = await this.addIndicator(indicator, regionset); - if (response.success) { - this.setActiveIndicator(indicator.hash); + const { indicators: current, regionset } = this.getState(); + const indicators = []; + const meta = await getIndicatorMetadata(indicator.ds, indicator.id); + const { name, source, description } = indicator; + // async/await doesn't work with forEach() + for (let i = 0; i < current.length; i++) { + let ind = current[i]; + if (ind.hash === indicator.hash) { + ind = await this.getIndicatorToAdd(indicator, regionset); + } else if (!onlyData && ind.id === indicator.id) { + // update indicator + ind = { ...ind, name, source, description }; + ind.labels = getUILabels(ind, meta); + } + indicators.push(ind); } - return response; + this.updateState({ indicators }); } async addIndicator (indicator, regionset) { @@ -235,9 +248,6 @@ class StatisticsController extends AsyncStateHandler { } if (this.isIndicatorSelected(indicator, true)) { // already selected - if (regionset !== this.getState().regionset) { - await this.setActiveRegionset(regionset); - } return { success: true }; } try { @@ -298,9 +308,7 @@ const wrapped = controllerMixin(StatisticsController, [ 'setActiveRegionset', 'setActiveRegion', 'addIndicator', - 'selectSavedIndicator', 'resetState', - 'updateIndicator', 'updateClassification', 'setSeriesValue' ]); From 15172b01ac3bf4bfbf559f80fc2fa00771e956d3 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 12:54:22 +0200 Subject: [PATCH 134/181] eslint --- bundles/statistics/statsgrid/handler/ViewHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/ViewHandler.js b/bundles/statistics/statsgrid/handler/ViewHandler.js index fcfba21823..a20d94cf1b 100644 --- a/bundles/statistics/statsgrid/handler/ViewHandler.js +++ b/bundles/statistics/statsgrid/handler/ViewHandler.js @@ -155,7 +155,7 @@ class UIHandler extends AsyncStateHandler { } onSearchChange (state) { - const { search, metadata} = this.controls; + const { search, metadata } = this.controls; if (search) { search.update(state, this.stateHandler.getState().indicators); } @@ -227,7 +227,7 @@ class UIHandler extends AsyncStateHandler { this.controls.metadata = showMedataPopup(indicators, () => this.close('metadata')); } - show (id) { // TODO: use optional options/params?? + show (id) { if (!id || this.controls[id]) { // already shown, do nothing return; From 5fc83bb5287ea7ad5a136b76653e5e9e2fb5e3d4 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 12:56:48 +0200 Subject: [PATCH 135/181] add RegionsetSelect and eslint fixes --- .../statsgrid/plugin/TogglePlugin.js | 1 - .../statsgrid/view/Table/TableFlyout.jsx | 56 ++++++++++--------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/bundles/statistics/statsgrid/plugin/TogglePlugin.js b/bundles/statistics/statsgrid/plugin/TogglePlugin.js index 31cf0ff4df..3e20179157 100644 --- a/bundles/statistics/statsgrid/plugin/TogglePlugin.js +++ b/bundles/statistics/statsgrid/plugin/TogglePlugin.js @@ -11,7 +11,6 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.TogglePlugin', function (handle }, { refresh: function (state = this.handler.getState()) { let el = this.getElement(); - // TODO: if (!el) { el = this._createControlElement(); this.addToPluginContainer(el); diff --git a/bundles/statistics/statsgrid/view/Table/TableFlyout.jsx b/bundles/statistics/statsgrid/view/Table/TableFlyout.jsx index 8779ac2462..3a6f53bfe6 100644 --- a/bundles/statistics/statsgrid/view/Table/TableFlyout.jsx +++ b/bundles/statistics/statsgrid/view/Table/TableFlyout.jsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import PropTypes from 'prop-types'; import { Select, Message } from 'oskari-ui'; import { IconButton } from 'oskari-ui/components/buttons'; import { Table, getSorterFor } from 'oskari-ui/components/Table'; @@ -66,6 +67,27 @@ const HeaderTools = styled.div` margin-top: 10px; height: 20px; `; +const RegionsetSelect = ({ value, indicators, controller }) => { + const regionsets = getRegionsets(); + if (Oskari.dom.isEmbedded()) { + return regionsets.find(r => r.id === value)?.name || ''; + } + const ids = indicators.map(ind => ind.allowedRegionsets || []).flat(); + const options = regionsets + .filter(rs => ids.includes(rs.id)) + .map(rs => ({ value: rs.id, label: rs.name })); + return <Select + filterOption={false} + options={options} + value={value} + onChange={(value) => controller.setActiveRegionset(value)}/>; +}; + +RegionsetSelect.propTypes = { + value: PropTypes.any, + indicators: PropTypes.array.isRequired, + controller: PropTypes.object.isRequired +}; const TableFlyout = ThemeConsumer(({ state, controller, theme }) => { const { indicators, activeIndicator, regionset, activeRegion } = state; @@ -77,34 +99,25 @@ const TableFlyout = ThemeConsumer(({ state, controller, theme }) => { const changeSortOrder = (column) => { let order = 'ascend'; if (column === sortOrder.column) { - order = sortOrder.order === 'ascend' ? 'descend' : 'ascend' + order = sortOrder.order === 'ascend' ? 'descend' : 'ascend'; } setSortOrder({ column, order }); }; // every value is set by looping regions => same indexes const dataByHash = indicators.reduce((data, ind) => { - data[ind.hash] = getDataByRegions(ind) + data[ind.hash] = getDataByRegions(ind); return data; }, {}); const hashes = indicators.map(ind => ind.hash); const dataSource = regions.map(({ id, name }, i) => { const data = { key: id, name }; hashes.forEach(hash => { - const { value, formatted } = dataByHash[hash][i]; + const { value, formatted } = dataByHash[hash][i] || {}; data[hash] = { value, formatted }; }); return data; }); const columnSettings = []; - const regionsetIds = []; - indicators.forEach(ind => { - const sets = ind.allowedRegionsets || []; - sets.forEach(id => { - if (!regionsetIds.includes(id)) { - regionsetIds.push(id); - } - }); - }); const getCellStyle = (regionId, hash) => { const style = { background: '#ffffff' }; if (regionId === activeRegion) { @@ -138,18 +151,7 @@ const TableFlyout = ThemeConsumer(({ state, controller, theme }) => { <HeaderCell> <RegionHeader> <Message messageKey='statsgrid.areaSelection.title' /> - {Oskari.dom.isEmbedded() ? ( - getRegionsets().find(r => r.id === regionset)?.name || '' - ) : ( - <Select - filterOption={false} - options={getRegionsets() - .filter(rs => regionsetIds.includes(rs.id)) - .map(rs => ({ value: rs.id, label: rs.name }))} - value={regionset} - onChange={(value) => controller.setActiveRegionset(value)} - /> - )} + <RegionsetSelect indicators={indicators} value={regionset} controller={controller}/> </RegionHeader> <HeaderTools> <Sorter @@ -163,7 +165,7 @@ const TableFlyout = ThemeConsumer(({ state, controller, theme }) => { }); indicators?.forEach(indicator => { const { hash } = indicator; - const sorter = (c1,c2) => { + const sorter = (c1, c2) => { const a = c1[hash].value; const b = c2[hash].value; if (a === b) return 0; @@ -207,7 +209,7 @@ const TableFlyout = ThemeConsumer(({ state, controller, theme }) => { columns={columnSettings} dataSource={dataSource} rowSelection={{ selectedRowKeys }} - onRow={item => ({onClick: () => controller.setActiveRegion(item.key)})} /> + onRow={item => ({ onClick: () => controller.setActiveRegion(item.key) })} />; }); export const showTableFlyout = (state, controller, onClose) => { @@ -234,5 +236,5 @@ export const showTableFlyout = (state, controller, onClose) => { /> </FlyoutContent> ) - } + }; }; From 4e0dc024014b0a9bcfa4b1187c767cafb541e807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Tue, 19 Nov 2024 12:57:21 +0200 Subject: [PATCH 136/181] Rename function as it's no longer formating --- .../coordinatetool/plugin/CoordinatePluginHandler.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js index 5175dcb6ec..cb079fdec6 100644 --- a/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js +++ b/bundles/framework/coordinatetool/plugin/CoordinatePluginHandler.js @@ -504,7 +504,7 @@ class UIHandler extends StateHandler { lat: response.lat } }; - return this.formatEmergencyCallMessage(newData); + return this.simplifyEmergencyData(newData); } } catch (e) { Messaging.error(this.loc('display.cannotTransformCoordinates.message')); @@ -512,7 +512,7 @@ class UIHandler extends StateHandler { } // Else if coordinates are from 'EPSG:4326' then use these else { - return this.formatEmergencyCallMessage(data); + return this.simplifyEmergencyData(data); } } @@ -525,7 +525,7 @@ class UIHandler extends StateHandler { // already in degrees, don't fetch again if (this.isCurrentUnitDegrees() && this.mapProjection === 'EPSG:4326') { this.updateState({ - emergencyInfo: this.formatEmergencyCallMessage({ + emergencyInfo: this.simplifyEmergencyData({ lonlat: { lon: xy.lonlat.lon, lat: xy.lonlat.lat @@ -542,7 +542,8 @@ class UIHandler extends StateHandler { } } - formatEmergencyCallMessage (data) { + // just simplifying the object + simplifyEmergencyData (data) { return { lon: data.lonlat.lon, lat: data.lonlat.lat From b86d87ed44a548c73c3469f439df285c0f789cbd Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 13:00:37 +0200 Subject: [PATCH 137/181] add spinner, adjust style and fix required proptypes --- .../statsgrid/view/Form/IndicatorForm.jsx | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx b/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx index 5712c887b2..454cc088b9 100644 --- a/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx +++ b/bundles/statistics/statsgrid/view/Form/IndicatorForm.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Message, Button } from 'oskari-ui'; +import { Message, Button, Spin } from 'oskari-ui'; import { LocaleProvider } from 'oskari-ui/util'; import { PrimaryButton, SecondaryButton, ButtonContainer } from 'oskari-ui/components/buttons'; import { showPopup } from 'oskari-ui/components/window'; @@ -16,11 +16,11 @@ const Content = styled('div')` display: flex; flex-direction: column; padding: 20px; - min-width: 365px; + min-width: 460px; `; const Warning = styled.div` - width: 350px; + width: 420px; font-style: italic; margin-top: 10px; `; @@ -40,31 +40,33 @@ const Title = ({ indicator, showDataTable, regionset, selection }) => { }; Title.propTypes = { indicator: PropTypes.object.isRequired, - regionset: PropTypes.number.isRequired, - showDataTable: PropTypes.func.isRequired, + regionset: PropTypes.number, + showDataTable: PropTypes.bool.isRequired, selection: PropTypes.any.isRequired }; const IndicatorForm = ({ state, controller, onClose }) => { - const { showDataTable, indicator } = state; + const { showDataTable, indicator, loading } = state; const showWarning = !showDataTable && !indicator.id && !Oskari.user().isLoggedIn(); const Component = showDataTable ? StatisticalData : IndicatorCollapse; const onSave = () => showDataTable ? controller.saveData() : controller.saveIndicator(); const onCancel = () => showDataTable ? controller.closeDataTable() : onClose(); return ( - <Content> - <Component state={state} controller={controller}/> - {showWarning && (<Warning><Message messageKey='userIndicators.notLoggedInWarning'/></Warning>)} - <ButtonContainer> - <SecondaryButton type='cancel' onClick={onCancel}/> - {showDataTable && ( - <Button onClick={() => controller.showClipboardPopup()} > - <Message messageKey='userIndicators.import.title' /> - </Button> - )} - <PrimaryButton type='save' onClick={onSave}/> - </ButtonContainer> - </Content> + <Spin showTip spinning={loading}> + <Content> + <Component state={state} controller={controller}/> + {showWarning && (<Warning><Message messageKey='userIndicators.notLoggedInWarning'/></Warning>)} + <ButtonContainer> + <SecondaryButton type='cancel' onClick={onCancel}/> + {showDataTable && ( + <Button onClick={() => controller.showClipboardPopup()} > + <Message messageKey='userIndicators.import.title' /> + </Button> + )} + <PrimaryButton type='save' onClick={onSave}/> + </ButtonContainer> + </Content> + </Spin> ); }; IndicatorForm.propTypes = { From 61500d19a1136b000763d949dd4746af6002e6ee Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 13:02:03 +0200 Subject: [PATCH 138/181] don't show error message while loading --- .../statistics/statsgrid/view/Form/StatisticalData.jsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx b/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx index fd1de3c903..aac14d8cda 100644 --- a/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx +++ b/bundles/statistics/statsgrid/view/Form/StatisticalData.jsx @@ -8,9 +8,15 @@ const StyledTable = styled(Table)` max-height: 475px; overflow-y: auto; `; +const Space = styled.div` + height: 80px; +`; export const StatisticalData = ({ state, controller }) => { - const { regionset, dataByRegions } = state; + const { regionset, dataByRegions, loading } = state; + if (loading) { + return <Space/>; + } if (!regionset || !dataByRegions.length) { return <Message messageKey='errors.regionsetsIsEmpty' />; } From 44d290760945f8aacfccee842edf3b820ac517c1 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 13:02:42 +0200 Subject: [PATCH 139/181] use fixed width for select --- bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx b/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx index c77597d3c9..4bd7f6a782 100644 --- a/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx +++ b/bundles/statistics/statsgrid/view/Form/StatisticalInfo.jsx @@ -13,10 +13,12 @@ const Content = styled('div')` margin-top: 10px; `; const StyledSelect = styled(Select)` - max-width: 200px; + width: 200px; + margin-right: 10px; `; const YearField = styled(TextInput)` width: 100px; + margin-right: 10px; `; export const StatisticalInfo = ({ state, controller }) => { From 7c92e51af7f38e8798b7c9d41024945641c21510 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 13:03:17 +0200 Subject: [PATCH 140/181] fix confirm title --- bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx b/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx index 256fda601f..fa469d1386 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx @@ -38,7 +38,7 @@ const PanelExtra = ({ indicators = [], removeAll }) => { <div onClick={e => e.stopPropagation()}> <RemoveAll type='delete' - confirmTitle={<Message messageKey='indicatorList.removeAll' />} + confirm={{ title: <Message messageKey='indicatorList.removeAll' /> }} title={<Message messageKey='indicatorList.removeAll' />} onConfirm={() => removeAll()}/> </div> From 81766d721e656d3280e554f767cc262b666c2d46 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 13:25:48 +0200 Subject: [PATCH 141/181] show search from tool and use loc function --- bundles/statistics/statsgrid/instance.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/bundles/statistics/statsgrid/instance.js b/bundles/statistics/statsgrid/instance.js index 6c567e1f9b..16990f5655 100644 --- a/bundles/statistics/statsgrid/instance.js +++ b/bundles/statistics/statsgrid/instance.js @@ -135,7 +135,7 @@ Oskari.clazz.define( }; if (!service.addItemToGroup(DATA_PROVIDER, data)) { // if adding failed, it might because group was not registered. - service.addGroup(DATA_PROVIDER, this.getLocalization().dataProviderInfoTitle); + service.addGroup(DATA_PROVIDER, this.loc('dataProviderInfoTitle')); // Try adding again service.addItemToGroup(DATA_PROVIDER, data); } @@ -150,16 +150,15 @@ Oskari.clazz.define( * Add vectorlayer to map for features. Layer is empty on module start. */ addVectorLayer: function () { - const locale = this.getLocalization(); this.sandbox.postRequestByName( 'VectorLayerRequest', [ { layerId: LAYER_ID, showLayer: 'registerOnly', - layerName: locale.layer.name, - layerInspireName: locale.layer.inspireName, - layerOrganizationName: locale.layer.organizationName, + layerName: this.loc('layer.name'), + layerInspireName: this.loc('layer.inspireName'), + layerOrganizationName: this.loc('layer.organizationName'), layerPermissions: { publish: 'publication_permission_ok' } @@ -203,8 +202,7 @@ Oskari.clazz.define( this.getViewHandler().updateLayer('onMap', true); if (!this.getTile().isAttached() && this.stateHandler.getState().indicators.length < 1) { // layer has added from layerlist and doesn't have indicators - this.getViewHandler()?.show('search'); - this.getSandbox().postRequestByName('userinterface.UpdateExtensionRequest', [this, 'attach']); + this.showSearchFlyout(); } }, 'MapLayerVisibilityChangedEvent': function (event) { @@ -234,6 +232,10 @@ Oskari.clazz.define( this.getStateHandler().setActiveRegion(region); } }, + showSearchFlyout: function () { + this.getViewHandler()?.show('search'); + this.getSandbox().postRequestByName('userinterface.UpdateExtensionRequest', [this, 'attach']); + }, __addTool: function () { const service = this.getLayerService(); @@ -242,13 +244,11 @@ Oskari.clazz.define( if (!layerModel) { return; } - const layerLoc = this.getLocalization('layertools').table_icon || {}; - const label = layerLoc.title || 'Thematic maps'; const tool = Oskari.clazz.create('Oskari.mapframework.domain.Tool'); tool.setName('table_icon'); - tool.setTitle(label); - tool.setTooltip(layerLoc.tooltip || label); - tool.setCallback(() => this.getSandbox().postRequestByName('userinterface.UpdateExtensionRequest', [this, 'attach'])); + tool.setTitle(this.loc('layerTool.title')); + tool.setTooltip(this.loc('layerTool.tooltip')); + tool.setCallback(() => this.showSearchFlyout()); service.addToolForLayer(layerModel, tool, false); }, setState: function (newState) { From 19267612b306f3a66a4dec705c1731b08b234220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Tue, 19 Nov 2024 13:56:37 +0200 Subject: [PATCH 142/181] Add simple test --- .../coordinatetool/plugin/helper.test.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 bundles/framework/coordinatetool/plugin/helper.test.js diff --git a/bundles/framework/coordinatetool/plugin/helper.test.js b/bundles/framework/coordinatetool/plugin/helper.test.js new file mode 100644 index 0000000000..722fc9f317 --- /dev/null +++ b/bundles/framework/coordinatetool/plugin/helper.test.js @@ -0,0 +1,26 @@ +import { formatDegrees } from './helper'; + +describe('Coordinatetool.helper', () => { + test('format degrees min default', () => { + expect.assertions(4); + const lon = 62.723013889; + const lat = 26.145404444; + const value = formatDegrees(lon, lat, 'min'); + expect(value.degreesX).toBe(62); + expect(value.degreesY).toBe(26); + + expect(value.minutesX).toBe('43,38083'); + expect(value.minutesY).toBe('8,72427'); + }); + test('format degrees min with accuracy 3', () => { + expect.assertions(4); + const lon = 62.723013889; + const lat = 26.145404444; + const value = formatDegrees(lon, lat, 'min', 3); + expect(value.degreesX).toBe(62); + expect(value.degreesY).toBe(26); + + expect(value.minutesX).toBe('43,381'); + expect(value.minutesY).toBe('8,724'); + }); +}); From 61935cfd44b15b37f4a0bc5a707ef6ad8ef5d110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Tue, 19 Nov 2024 14:04:15 +0200 Subject: [PATCH 143/181] ESLint formatting --- src/i18n.js | 62 ++++++++++++------------ src/oskari.es6.js | 117 +++++++++++++++++++++++----------------------- 2 files changed, 89 insertions(+), 90 deletions(-) diff --git a/src/i18n.js b/src/i18n.js index 278bee74a4..4bf77da9bf 100755 --- a/src/i18n.js +++ b/src/i18n.js @@ -1,13 +1,13 @@ -import IntlMessageFormat from 'intl-messageformat' +import IntlMessageFormat from 'intl-messageformat'; /** * Adds internalization support for Oskari */ (function (O) { - var oskariLang = 'en'; - var localizations = {}; - var supportedLocales = null; - var log = Oskari.log('Oskari.deprecated'); + let oskariLang = 'en'; + const localizations = {}; + let supportedLocales = null; + const log = Oskari.log('Oskari.deprecated'); // FIXME: remove html from localization files to get rid of these!! // These are required by intl-messageformat as instructions for handling HTML-tags in locale strings const HTML_CONTEXT_VARIABLE_HANDLERS = { @@ -76,10 +76,10 @@ import IntlMessageFormat from 'intl-messageformat' * @return {string[]} Supported languages */ O.getSupportedLanguages = function () { - var langs = []; - var supported = O.getSupportedLocales(); - var locale; - var i; + const langs = []; + const supported = O.getSupportedLocales(); + let locale; + let i; for (i = 0; i < supported.length; i += 1) { locale = supported[i]; @@ -96,12 +96,12 @@ import IntlMessageFormat from 'intl-messageformat' * @return {string} Default language */ O.getDefaultLanguage = function () { - var supported = O.getSupportedLocales(); + const supported = O.getSupportedLocales(); if (supported.length === 0) { return this.getLang(); } - var locale = supported[0]; + const locale = supported[0]; if (locale.indexOf('_') !== -1) { return locale.substring(0, locale.indexOf('_')); @@ -121,7 +121,7 @@ import IntlMessageFormat from 'intl-messageformat' * @param {string=} value Value * */ - var setLocalization = function (lang, key, value) { + const setLocalization = function (lang, key, value) { if (lang === null || lang === undefined) { throw new TypeError( 'setLocalization(): Missing lang' @@ -147,7 +147,7 @@ import IntlMessageFormat from 'intl-messageformat' */ O.getLocalization = function (key, lang, fallbackToDefault) { log.deprecated('Oskari.getLocalization()', 'Use Oskari.getMsg() instead.'); - var l = lang || oskariLang; + const l = lang || oskariLang; if (key === null || key === undefined) { throw new TypeError( 'getLocalization(): Missing key' @@ -159,7 +159,7 @@ import IntlMessageFormat from 'intl-messageformat' if (localizations[l] && localizations[l][key]) { return localizations[l][key]; } else { - var defaultLang = O.getDefaultLanguage(); + const defaultLang = O.getDefaultLanguage(); if (fallbackToDefault && localizations[defaultLang] && localizations[defaultLang][key]) { return localizations[defaultLang][key]; } else { @@ -175,7 +175,7 @@ import IntlMessageFormat from 'intl-messageformat' * */ O.registerLocalization = function (props, override) { - var p, + let p, pp, loc; @@ -232,7 +232,7 @@ import IntlMessageFormat from 'intl-messageformat' // ------------------------------------------------ // Decimal separators // ------------------------------------------------ - var decimalSeparator; + let decimalSeparator; /** * @public @method setDecimalSeparator @@ -265,7 +265,7 @@ import IntlMessageFormat from 'intl-messageformat' if (!lang) { lang = Oskari.getLang(); } - var value = locale[lang]; + let value = locale[lang]; if (!value) { value = locale[Oskari.getDefaultLanguage()]; } @@ -279,14 +279,14 @@ import IntlMessageFormat from 'intl-messageformat' } return value; }; - var intlCache = {}; - function resolvePath(key, path) { - var ob = O.getLocalization(key); - var parts = path.split('.'); - for (var i = 0; i < parts.length; i++) { + const intlCache = {}; + function resolvePath (key, path) { + let ob = O.getLocalization(key); + const parts = path.split('.'); + for (let i = 0; i < parts.length; i++) { ob = ob[parts[i]]; if (!ob) { - if(i === parts.length-1 && ob === '') { + if (i === parts.length - 1 && ob === '') { return ob; } return null; @@ -295,19 +295,19 @@ import IntlMessageFormat from 'intl-messageformat' return ob; } O.getMsg = function (key, path, values) { - var message; + let message; if (!values) { message = resolvePath(key, path); - if(message === null) { + if (message === null) { return path; } return message; } - var cacheKey = oskariLang + '_' + key + '_' + path; - var formatter = intlCache[cacheKey]; + const cacheKey = oskariLang + '_' + key + '_' + path; + let formatter = intlCache[cacheKey]; if (!formatter) { message = resolvePath(key, path); - if(message === null) { + if (message === null) { return path; } formatter = new IntlMessageFormat(message, oskariLang); @@ -316,11 +316,11 @@ import IntlMessageFormat from 'intl-messageformat' const htmlValues = { ...values, ...HTML_CONTEXT_VARIABLE_HANDLERS - } + }; return formatter.format(htmlValues); }; O.getNumberFormatter = function (fractionDigits) { - var opts; + let opts; if (typeof fractionDigits !== 'undefined') { opts = { minimumFractionDigits: fractionDigits, @@ -328,5 +328,5 @@ import IntlMessageFormat from 'intl-messageformat' }; } return new Intl.NumberFormat(oskariLang, opts); - } + }; }(Oskari)); diff --git a/src/oskari.es6.js b/src/oskari.es6.js index 086b5499e2..7e1b1939e3 100755 --- a/src/oskari.es6.js +++ b/src/oskari.es6.js @@ -9,17 +9,17 @@ import pkg from '../package.json'; import { DOMHelper } from './oskari.dom.js'; import { Customization } from './oskari.customization.js'; -let defaultSequence = new Sequence(); -let sequences = {}; +const defaultSequence = new Sequence(); +const sequences = {}; // keep track of existing loggers -let loggers = {}; +const loggers = {}; -let _urls= {}; +let _urls = {}; function getUrl (key) { return _urls[key]; } -function encodeParams(params) { +function encodeParams (params) { if (typeof params !== 'object') { return ''; } @@ -29,7 +29,7 @@ function encodeParams(params) { .join('&'); } -function appendQueryToURL(url, query) { +function appendQueryToURL (url, query) { if (typeof query === 'undefined' || query === '') { return url; } @@ -65,77 +65,76 @@ const Oskari = { dom: DOMHelper, seq: defaultSequence, getSeq (type) { - if(typeof type === 'undefined') { + if (typeof type === 'undefined') { return defaultSequence; } else if (!sequences[type]) { sequences[type] = new Sequence(); } return sequences[type]; }, - log(name = 'Oskari') { + log (name = 'Oskari') { if (loggers[name]) { return loggers[name]; } - var log = new Logger(name); + const log = new Logger(name); loggers[name] = log; return log; }, urls: { - /** - * Oskari.urls.set({ - "map" : "https://my.map.com", - "api": "https://api.map.com/action?", - "login" :"https://my.map.com/login", - "register" :"http://some.auth.site.com/register", - "tou" :"http://my.organization/map/tou", - }); - OR - Oskari.urls.set('login', 'https://my.map.com/login'); - */ - set: function (urlsOrKey, optionalValue) { - if (typeof urlsOrKey === 'string') { - _urls[urlsOrKey] = optionalValue; - } else if (typeof urlsOrKey === 'object') { - _urls = urlsOrKey || {}; - } else { - throw new Error('Unrecognized parameter for urls: ' + urlsOrKey); - } - }, - /** - * Generic url "location" getter - * @param {String} key type of url like "login" or "registration" - * @return {String} URL that points to requested functionality - */ - getLocation: function (key) { - return getUrl(key); - }, - /** - * Action route urls - * @param {String} route optional route name. Returns base url if name is not given. - * @param {Object} optionalParams optional object that will be encoded as querystring parameters for the URL. - * @return {String} url to use when making API calls - */ - getRoute: function (route, optionalParams) { - var url = appendQueryToURL(getUrl('api') || '/action?', encodeParams(optionalParams)); + /** + * Oskari.urls.set({ + "map" : "https://my.map.com", + "api": "https://api.map.com/action?", + "login" :"https://my.map.com/login", + "register" :"http://some.auth.site.com/register", + "tou" :"http://my.organization/map/tou", + }); + OR + Oskari.urls.set('login', 'https://my.map.com/login'); + */ + set: function (urlsOrKey, optionalValue) { + if (typeof urlsOrKey === 'string') { + _urls[urlsOrKey] = optionalValue; + } else if (typeof urlsOrKey === 'object') { + _urls = urlsOrKey || {}; + } else { + throw new Error('Unrecognized parameter for urls: ' + urlsOrKey); + } + }, + /** + * Generic url "location" getter + * @param {String} key type of url like "login" or "registration" + * @return {String} URL that points to requested functionality + */ + getLocation: function (key) { + return getUrl(key); + }, + /** + * Action route urls + * @param {String} route optional route name. Returns base url if name is not given. + * @param {Object} optionalParams optional object that will be encoded as querystring parameters for the URL. + * @return {String} url to use when making API calls + */ + getRoute: function (route, optionalParams) { + const url = appendQueryToURL(getUrl('api') || '/action?', encodeParams(optionalParams)); - if (route) { - return appendQueryToURL(url, 'action_route=' + route); - } - return url; - }, - /** - * Builds an URL by attaching optional parameters to base url - * @param {String} url complete baseUrl that might already have querystring - * @param {*} optionalParams parameters that should be attached to baseUrl - * @returns base url with optional params included as querystring - */ - buildUrl: function (url, optionalParams) { - return appendQueryToURL(url, encodeParams(optionalParams)); + if (route) { + return appendQueryToURL(url, 'action_route=' + route); } + return url; + }, + /** + * Builds an URL by attaching optional parameters to base url + * @param {String} url complete baseUrl that might already have querystring + * @param {*} optionalParams parameters that should be attached to baseUrl + * @returns base url with optional params included as querystring + */ + buildUrl: function (url, optionalParams) { + return appendQueryToURL(url, encodeParams(optionalParams)); + } } }; window.Oskari = Oskari; // TODO: remove when whole of core is ES6 export default Oskari; - From fdfb9ac3857742f713874b66aa8a39fd0fb14dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <sami.makinen@nls.fi> Date: Tue, 19 Nov 2024 14:18:36 +0200 Subject: [PATCH 144/181] Add mechanism to debug localizations, disabled by default --- src/i18n.js | 9 ++++++++- src/react/components/Message.jsx | 11 +++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/i18n.js b/src/i18n.js index 4bf77da9bf..61b93125e2 100755 --- a/src/i18n.js +++ b/src/i18n.js @@ -270,7 +270,7 @@ import IntlMessageFormat from 'intl-messageformat'; value = locale[Oskari.getDefaultLanguage()]; } if (!value) { - for (var key in locale) { + for (const key in locale) { if (locale[key]) { // any locale will do at this point return locale[key]; @@ -329,4 +329,11 @@ import IntlMessageFormat from 'intl-messageformat'; } return new Intl.NumberFormat(oskariLang, opts); }; + let _msgDebugMode = 'TBD'; + O.isMsgDebugMode = function () { + if (_msgDebugMode === 'TBD') { + _msgDebugMode = Oskari.util.getRequestParam('msgDebugMode', false) === 'true'; + } + return _msgDebugMode; + }; }(Oskari)); diff --git a/src/react/components/Message.jsx b/src/react/components/Message.jsx index a54662e1bd..1a345ca048 100644 --- a/src/react/components/Message.jsx +++ b/src/react/components/Message.jsx @@ -55,15 +55,18 @@ const Message = ({ bundleKey, messageKey, messageArgs, defaultMsg, getMessage, f return fallback; } } - + const injectedProps = {}; + if (Oskari.isMsgDebugMode()) { + injectedProps.onClick = () => Oskari.log('Message').debug(`Text clicked - ${bundleKey}: ${messageKey}`); + } if (allowHTML) { - return (<LabelComponent dangerouslySetInnerHTML={{ __html: message }}></LabelComponent>); + return (<LabelComponent dangerouslySetInnerHTML={{ __html: message }} { ...injectedProps }></LabelComponent>); } return ( <LabelComponent allowTextEllipsis={allowTextEllipsis} - onClick={() => Oskari.log().debug(`Text clicked - ${bundleKey}: ${messageKey}`)}> + { ...injectedProps }> { message } { children } </LabelComponent> ); @@ -86,7 +89,7 @@ function getMessageUsingOskariGlobal (bundleKey, messageKey, messageArgs) { return Oskari.getMsg(bundleKey, messageKey, messageArgs); } catch (e) { // no locale provider OR bundleKey missing from locale provider - Oskari.log().warn(`Message tag used without LocaleProvider or bundleKey not provided when getting: ${messageKey}. Original error: ${e.message}`); + Oskari.log('Message').warn(`Message tag used without LocaleProvider or bundleKey not provided when getting: ${messageKey}. Original error: ${e.message}`); } return messageKey; } From 13230d8e8644eb7945f627f0fbcede15e33aeb3c Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 14:52:20 +0200 Subject: [PATCH 145/181] props validation and eslint fixes --- .../statsgrid/view/Diagram/Diagram.jsx | 42 ++++++++++--------- .../statsgrid/view/Diagram/DiagramFlyout.jsx | 34 +++++---------- .../statsgrid/view/FlyoutContent.jsx | 8 ++-- .../statsgrid/view/Table/Sorter.jsx | 10 ++++- .../statsgrid/view/Table/TableFlyout.jsx | 2 +- 5 files changed, 46 insertions(+), 50 deletions(-) diff --git a/bundles/statistics/statsgrid/view/Diagram/Diagram.jsx b/bundles/statistics/statsgrid/view/Diagram/Diagram.jsx index d401f71c17..6b815c05c7 100644 --- a/bundles/statistics/statsgrid/view/Diagram/Diagram.jsx +++ b/bundles/statistics/statsgrid/view/Diagram/Diagram.jsx @@ -1,10 +1,11 @@ import React, { useRef, useEffect } from 'react'; +import PropTypes from 'prop-types'; import * as d3 from 'd3'; import styled from 'styled-components'; import { getDataByRegions, getValueSorter } from '../../helper/StatisticsHelper'; import { BUNDLE_KEY } from '../../constants'; -const MARGIN = { +const MARGIN = { top: 0, bottom: 20, left: 50, @@ -55,7 +56,7 @@ const getDimensionsFromData = (data) => { const widestLabel = d3.max(data, (d) => d.name.length); const labelPx = widestLabel * pxPerChar; - let labelMargin = labelPx < MARGIN.maxForLabel ? labelPx : MARGIN.maxForLabel; + const labelMargin = labelPx < MARGIN.maxForLabel ? labelPx : MARGIN.maxForLabel; // TODO: why chartWidt and x are calculated with default values and later updated?? let chartWidth = WIDTH - left - right; const x = d3.scaleLinear().domain(getScaleArray(min, max)).range([0, chartWidth]); @@ -113,7 +114,7 @@ const getTextContent = (d, maxLength) => { }; const getSortedData = (indicator, sortingType = 'value-descending') => { - const data = getDataByRegions(indicator); + const data = getDataByRegions(indicator); if (sortingType === 'name-ascending') { return data.toSorted((a, b) => d3.descending(a.name, b.name)); } @@ -129,16 +130,14 @@ const getSortedData = (indicator, sortingType = 'value-descending') => { return data; }; -const createGraph = (ref, labelsRef, indicator, sortOrder ) => { +const createGraph = (ref, labelsRef, indicator, sortOrder) => { const { groups, error } = indicator.classifiedData; const { min, max } = indicator.data; const data = getSortedData(indicator, sortOrder); const dimensions = calculateDimensions(data); const { format } = Oskari.getNumberFormatter(); - let x; - let y; - x = d3.scaleLinear(); - y = d3.scaleBand(); + const x = d3.scaleLinear(); + const y = d3.scaleBand(); const xScaleDomain = getScaleArray(min, max); const yScaleDomain = data.map((d) => d.name); y.range([dimensions.chart?.height, 0]); @@ -163,7 +162,6 @@ const createGraph = (ref, labelsRef, indicator, sortOrder ) => { .append('g') .attr('transform', 'translate(' + dimensions.margin.left + ',' + dimensions.margin.top + ')'); - svg.each(data => { const { chart: { height }, axis: { ticks, xOffset } } = dimensions; const xAxis = d3.axisTop(x) @@ -171,18 +169,18 @@ const createGraph = (ref, labelsRef, indicator, sortOrder ) => { .tickSizeInner(-height + xOffset) .tickSizeOuter(0) .tickFormat(d => format(d)); - + const xtickAxis = svg.append('g') - .attr('class', 'x axis') - .attr('transform', 'translate( 0, 0)') - .attr('shape-rendering', 'crispEdges') - .call(xAxis); - + .attr('class', 'x axis') + .attr('transform', 'translate( 0, 0)') + .attr('shape-rendering', 'crispEdges') + .call(xAxis); + xtickAxis.selectAll('x axis, tick, text').remove(); xtickAxis.select('.domain').remove(); xtickAxis.selectAll('line') .attr('stroke', '#eee'); - + const { container, margin } = dimensions; // append the x-axis to different element so we can show the values when scrollign const labelheight = 12; @@ -194,7 +192,7 @@ const createGraph = (ref, labelsRef, indicator, sortOrder ) => { .attr('class', 'x axis header') .attr('transform', 'translate(' + margin.left + ',' + labelheight + ')') .call(xAxis); - + gx.select('.domain').remove(); gx.selectAll('line').remove(); }); @@ -311,9 +309,9 @@ const createGraph = (ref, labelsRef, indicator, sortOrder ) => { }); }; -export const Diagram = ({ indicator, sortOrder }) => { - let ref = useRef(null); - let labelsRef = useRef(null); +export const Diagram = ({ indicator, sortOrder }) => { + const ref = useRef(null); + const labelsRef = useRef(null); useEffect(() => { if (ref?.current?.children?.length > 0) { ref.current.removeChild(ref.current.children[0]); @@ -335,3 +333,7 @@ export const Diagram = ({ indicator, sortOrder }) => { </Content> ); }; +Diagram.propTypes = { + indicator: PropTypes.object.isRequired, + sortOrder: PropTypes.string.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/Diagram/DiagramFlyout.jsx b/bundles/statistics/statsgrid/view/Diagram/DiagramFlyout.jsx index a2da84394b..f0d3b30221 100644 --- a/bundles/statistics/statsgrid/view/Diagram/DiagramFlyout.jsx +++ b/bundles/statistics/statsgrid/view/Diagram/DiagramFlyout.jsx @@ -1,12 +1,12 @@ import React, { Fragment, useState } from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; import { Diagram } from './Diagram'; import { Select, Message } from 'oskari-ui'; import { showFlyout } from 'oskari-ui/components/window'; -import styled from 'styled-components'; import { IndicatorName } from '../IndicatorName'; import { FlyoutContent } from '../FlyoutContent'; - -const BUNDLE_KEY = 'StatsGrid'; +import { BUNDLE_KEY } from '../../constants'; const Selections = styled('div')` display: flex; @@ -18,24 +18,8 @@ const StyledSelect = styled(Select)` max-width: 325px; `; -const sortOptions = [ - { - value: 'value-descending', - label: <Message messageKey='datacharts.sorting.value-descending' bundleKey={BUNDLE_KEY} /> - }, - { - value: 'value-ascending', - label: <Message messageKey='datacharts.sorting.value-ascending' bundleKey={BUNDLE_KEY} /> - }, - { - value: 'name-ascending', - label: <Message messageKey='datacharts.sorting.name-ascending' bundleKey={BUNDLE_KEY} /> - }, - { - value: 'name-descending', - label: <Message messageKey='datacharts.sorting.name-descending' bundleKey={BUNDLE_KEY} /> - } -]; +const sorters = ['value-descending', 'value-ascending', 'name-descending', 'name-ascending']; +const sortOptions = sorters.map(value => ({ value, label: <Message messageKey={`diagram.sort.${value}`}/> })); const DiagramFlyout = ({ state, controller }) => { const { indicators, activeIndicator } = state; @@ -56,13 +40,17 @@ const DiagramFlyout = ({ state, controller }) => { options={sortOptions} onChange={(value) => setSortOrder(value)} value={sortOrder} - placeholder={<Message messageKey='datacharts.sorting.desc' />} + placeholder={<Message messageKey='diagram.sort.desc' />} /> </Selections> <Diagram indicator={current} sortOrder={sortOrder} /> </Fragment> ); }; +DiagramFlyout.propTypes = { + state: PropTypes.object.isRequired, + controller: PropTypes.object.isRequired +}; export const showDiagramFlyout = (state, controller, onClose) => { const title = <Message bundleKey={BUNDLE_KEY} messageKey='tile.diagram' />; @@ -82,5 +70,5 @@ export const showDiagramFlyout = (state, controller, onClose) => { <DiagramFlyout state={state} controller={controller} /> </FlyoutContent> ) - } + }; }; diff --git a/bundles/statistics/statsgrid/view/FlyoutContent.jsx b/bundles/statistics/statsgrid/view/FlyoutContent.jsx index ee7aefb442..03992fe76e 100644 --- a/bundles/statistics/statsgrid/view/FlyoutContent.jsx +++ b/bundles/statistics/statsgrid/view/FlyoutContent.jsx @@ -19,12 +19,11 @@ export const FlyoutContent = ({ state, children }) => { const Component = ( <LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> - <Content> + <Content> {current ? children : <Message messageKey='statsgrid.noResults' />} - </Content> + </Content> </LocaleProvider> ); - if (loading) { return <Spin showTip={true}>{Component}</Spin>; } @@ -32,5 +31,6 @@ export const FlyoutContent = ({ state, children }) => { }; FlyoutContent.propTypes = { - state: PropTypes.object.isRequired + state: PropTypes.object.isRequired, + children: PropTypes.any }; diff --git a/bundles/statistics/statsgrid/view/Table/Sorter.jsx b/bundles/statistics/statsgrid/view/Table/Sorter.jsx index e1b8f629f9..49a93104ed 100644 --- a/bundles/statistics/statsgrid/view/Table/Sorter.jsx +++ b/bundles/statistics/statsgrid/view/Table/Sorter.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Message, Tooltip } from 'oskari-ui'; import { CaretDownFilled, CaretUpFilled } from '@ant-design/icons'; import styled from 'styled-components'; @@ -15,9 +16,9 @@ const Icon = styled('div')` export const Sorter = ({ sortOrder, changeSortOrder, column }) => { const isActive = sortOrder.column === column; - const order = isActive ? sortOrder.order : null; + const order = isActive ? sortOrder.order : null; let icon = null; - if ( order === 'ascend') { + if (order === 'ascend') { icon = <CaretUpFilled />; } else if (order === 'descend') { icon = <CaretDownFilled />; @@ -31,3 +32,8 @@ export const Sorter = ({ sortOrder, changeSortOrder, column }) => { </Tooltip> ); }; +Sorter.propTypes = { + column: PropTypes.string.isRequired, + changeSortOrder: PropTypes.func.isRequired, + sortOrder: PropTypes.object.isRequired +}; diff --git a/bundles/statistics/statsgrid/view/Table/TableFlyout.jsx b/bundles/statistics/statsgrid/view/Table/TableFlyout.jsx index 3a6f53bfe6..290d5e768c 100644 --- a/bundles/statistics/statsgrid/view/Table/TableFlyout.jsx +++ b/bundles/statistics/statsgrid/view/Table/TableFlyout.jsx @@ -13,8 +13,8 @@ import { getDataByRegions } from '../../helper/StatisticsHelper'; import { getRegions } from '../../helper/RegionsHelper'; import { ThemeConsumer } from 'oskari-ui/util'; import { getHeaderTheme } from 'oskari-ui/theme'; +import { BUNDLE_KEY } from '../../constants'; -const BUNDLE_KEY = 'StatsGrid'; const COLUMN = 200; const StyledTable = styled(Table)` From a9cc4f13e2d814ca6f861f3fe7ff7b1ad17d857d Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 15:39:18 +0200 Subject: [PATCH 146/181] remove unused localization --- .../statsgrid/resources/locale/en.js | 85 +++------------- .../statsgrid/resources/locale/fi.js | 91 +++-------------- .../statsgrid/resources/locale/fr.js | 79 +++------------ .../statsgrid/resources/locale/is.js | 97 +++---------------- .../statsgrid/resources/locale/ru.js | 66 ++----------- .../statsgrid/resources/locale/sv.js | 89 +++-------------- 6 files changed, 77 insertions(+), 430 deletions(-) diff --git a/bundles/statistics/statsgrid/resources/locale/en.js b/bundles/statistics/statsgrid/resources/locale/en.js index 808c70cace..c51c6c3f5a 100644 --- a/bundles/statistics/statsgrid/resources/locale/en.js +++ b/bundles/statistics/statsgrid/resources/locale/en.js @@ -12,19 +12,9 @@ Oskari.registerLocalization({ 'title': 'Thematic maps' }, 'dataProviderInfoTitle': 'Indicators', - 'layertools': { - 'table_icon': { - 'tooltip': 'Move to thematic maps', - 'title': 'Thematic maps' - }, - 'diagram_icon': { - 'tooltip': 'Show data in diagram', - 'title': 'Diagram' - }, - 'statistics': { - 'tooltip': 'Move to thematic maps', - 'title': 'Statistics' - } + 'layerTool': { + 'tooltip': 'Move to thematic maps', + 'title': 'Thematic maps' }, 'publisher': { "label": "Thematic maps", @@ -95,7 +85,14 @@ Oskari.registerLocalization({ }, 'diagram': { 'title': 'Diagram', - 'noValue': 'N/A' + 'noValue': 'N/A', + 'sort': { + 'desc': 'Order', + 'name-ascending': 'Name ascending', + 'name-descending': 'Name descending', + 'value-ascending': 'Value ascending', + 'value-descending': 'Value descending' + } }, 'parameters': { 'sex': 'Gender', @@ -107,11 +104,6 @@ Oskari.registerLocalization({ 'value': 'Value', 'region': 'Region' }, - 'datatable': 'Table', - 'published': { - 'showMap': 'Show map', - 'showTable': 'Show table' - }, 'classify': { 'classify': 'Classification', 'labels': { @@ -186,44 +178,6 @@ Oskari.registerLocalization({ 'regionsetName': 'Unknown', 'indicator': 'Unknown indicator' }, - 'datacharts': { - 'flyout': 'Searched data', - 'barchart': 'Bar chart', - 'linechart': 'Line chart', - 'table': 'Table', - 'desc': 'Table and graphs', - 'nodata': 'Indicators were not chosen', - 'indicatorVar': 'Variable to be shown in graph', - 'descColor': 'Color of the graph', - 'selectClr': 'Selected color', - 'clrFromMap': 'Colors by classification in the map', - 'chooseColor': 'Select color', - 'sorting': { - 'desc': 'Order', - 'name-ascending': 'Name ascending', - 'name-descending': 'Name descending', - 'value-ascending': 'Value ascending', - 'value-descending': 'Value descending' - } - }, - 'filter': { - 'title': 'Filtering', - 'indicatorToFilter': 'Variable to be filtered', - 'condition': 'Condition', - 'value': 'Value', - 'variable': 'Variable', - 'conditionPlaceholder': 'Select condition', - 'greater': 'Greater than (>)', - 'greaterEqual': 'Greater than or equal to (>=)', - 'equal': 'Equal to (=)', - 'lessEqual': 'Less than or equal to (<=)', - 'lessThan': 'Less than (<)', - 'between': 'Between (exclusive)', - 'filter': 'Filter values', - 'desc': 'Filter by values', - 'filtered': 'Filtered values', - 'area': 'Filter by areas' - }, 'layer': { 'name': 'Areal division of thematic map', 'inspireName': 'Thematic map', @@ -231,19 +185,12 @@ Oskari.registerLocalization({ }, 'tab': { 'title': 'Indicators', - 'edit': 'Edit', - 'delete': 'Delete', + 'confirmDelete': 'You are deleting the indicator "{name}". Do you want to delete the indicator?', 'grid': { 'name': 'Name', - 'edit': 'Edit', - 'delete': 'Delete', 'actions': 'Actions', 'createDate': 'Created', 'updateDate': 'Updated' - }, - 'popup': { - 'deletetitle': 'Delete Indicator', - 'deletemsg': 'You are deleting the indicator "{name}". Do you want to delete the indicator?' } }, 'userIndicators': { @@ -301,14 +248,6 @@ Oskari.registerLocalization({ 'noMetadata': 'Service did not return {indicators, plural, one {description for the indicator} other {descriptions for the indicators}}', 'updated': 'Last update', 'nextUpdate': 'Next update' - }, - 'sumo': { - 'placeholder': 'Select Here', - 'captionFormat': '{0} selected', - 'captionFormatAllSelected': 'All {0} selected!', - 'searchText': 'Search...', - 'noMatch': 'No results found matching "{0}"', - 'locale': ['OK', 'Cancel', 'Select All'] } } }); diff --git a/bundles/statistics/statsgrid/resources/locale/fi.js b/bundles/statistics/statsgrid/resources/locale/fi.js index 9008c68010..842ede3d8f 100644 --- a/bundles/statistics/statsgrid/resources/locale/fi.js +++ b/bundles/statistics/statsgrid/resources/locale/fi.js @@ -12,19 +12,9 @@ Oskari.registerLocalization({ 'title': 'Teemakartat' }, 'dataProviderInfoTitle': 'Indikaattorit', - 'layertools': { - 'table_icon': { - 'tooltip': 'Siirry teemakarttoihin', - 'title': 'Teemakartat' - }, - 'diagram_icon': { - 'tooltip': 'Näytä tiedot diagrammissa', - 'title': 'Diagrammi' - }, - 'statistics': { - 'tooltip': 'siirry teemakarttoihin', - 'title': 'Tilastot' - } + 'layerTool': { + 'tooltip': 'Siirry teemakarttoihin', + 'title': 'Teemakartat' }, 'publisher': { "label": "Teemakartat", @@ -95,7 +85,14 @@ Oskari.registerLocalization({ }, 'diagram': { 'title': 'Pylväsdiagrammi', - 'noValue': 'Ei arvoa' + 'noValue': 'Ei arvoa', + 'sort': { + 'desc': 'Järjestys', + 'name-ascending': 'Nimen mukaan nouseva', + 'name-descending': 'Nimen mukaan laskeva', + 'value-ascending': 'Arvon mukaan nouseva', + 'value-descending': 'Arvon mukaan laskeva' + } }, 'parameters': { 'sex': 'Sukupuoli', @@ -107,22 +104,17 @@ Oskari.registerLocalization({ 'value': 'Arvo', 'region': 'Alue' }, - 'datatable': 'Taulukko', - 'published': { - 'showMap': 'Näytä kartta', - 'showTable': 'Näytä taulukko' - }, 'classify': { 'classify': 'Luokittelu', 'labels': { 'method': 'Luokittelutapa', - 'count': 'Luokkajako', // classes + 'count': 'Luokkajako', 'mode': 'Luokkarajat', 'mapStyle': 'Kartan tyyli', - 'type': 'Jakauma', // setselection + 'type': 'Jakauma', 'reverseColors': 'Käännä värit', 'color': 'Väri', - 'colorset': 'Värit', //button + 'colorset': 'Värit', 'pointSize': 'Pisteen koko', 'transparency': 'Läpinäkyvyys', 'showValues': 'Näytä arvot', @@ -186,44 +178,6 @@ Oskari.registerLocalization({ 'regionsetName': 'Tuntematon', 'indicator': 'Tuntematon indikaattori' }, - 'datacharts': { - 'flyout': 'Haettu aineisto', - 'barchart': 'Pylväskuvio', - 'linechart': 'Viivakuvio', - 'table': 'Taulukko', - 'desc': 'Taulukko ja kuvaajat', - 'nodata': 'Ei valittuja indikaattoreita', - 'indicatorVar': 'Kuvaajassa esitettävä muuttuja', - 'descColor': 'Kuvaajan väri', - 'selectClr': 'Valittu väri', - 'clrFromMap': 'Värit kartalla olevan luokittelun mukaan', - 'chooseColor': 'Valitse väri', - 'sorting': { - 'desc': 'Järjestys', - 'name-ascending': 'Nimen mukaan nouseva', - 'name-descending': 'Nimen mukaan laskeva', - 'value-ascending': 'Arvon mukaan nouseva', - 'value-descending': 'Arvon mukaan laskeva' - } - }, - 'filter': { - 'title': 'Suodatus', - 'indicatorToFilter': 'Suodatettava muuttuja', - 'condition': 'Ehto', - 'value': 'Arvo', - 'variable': 'Muuttuja', - 'conditionPlaceholder': 'Valitse ehto', - 'greater': 'Suurempi kuin (>)', - 'greaterEqual': 'Suurempi tai yhtäsuuri kuin (>=)', - 'equal': 'Yhtäsuuri kuin (=)', - 'lessEqual': 'Pienempi tai yhtäsuuri kuin (<=)', - 'lessThan': 'Pienempi kuin (<)', - 'between': 'Välillä (poissulkeva)', - 'filter': 'Suodata arvot', - 'desc': 'Suodata arvoilla', - 'filtered': 'Suodatetut arvot', - 'area': 'Suodata alueilla' - }, 'layer': { 'name': 'Teemakartan aluejako', 'inspireName': 'Teemakartta', @@ -231,19 +185,12 @@ Oskari.registerLocalization({ }, 'tab': { 'title': 'Indikaattorit', - 'edit': 'Muokkaa', - 'delete': 'Poista', + 'confirmDelete': 'Haluatko poistaa indikaattorin "{name}"?', 'grid': { 'name': 'Nimi', - 'edit': 'Muokkaa', - 'delete': 'Poista', 'actions': 'Toiminnot', 'createDate': 'Luontiaika', 'updateDate': 'Muokkausaika' - }, - 'popup': { - 'deletetitle': 'Poista indikaattori', - 'deletemsg': 'Haluatko poistaa indikaattorin "{name}"?' } }, 'userIndicators': { @@ -301,14 +248,6 @@ Oskari.registerLocalization({ 'noMetadata': 'Palvelusta ei saatu {indicators, plural, one {indikaattorin kuvausta} other {indikaattorien kuvauksia}}.', 'updated': 'Viimeksi päivitetty', 'nextUpdate': 'Seuraava päivitys' - }, - 'sumo': { - 'placeholder': 'Valitse tästä', - 'captionFormat': '{0} valittu', - 'captionFormatAllSelected': 'Kaikki {0} valittu!', - 'searchText': 'Etsi...', - 'noMatch': 'Yhtään hakutulosta ei löytynyt haulla "{0}"', - 'locale': ['OK', 'Peruuta', 'Valitse kaikki'] } } }); diff --git a/bundles/statistics/statsgrid/resources/locale/fr.js b/bundles/statistics/statsgrid/resources/locale/fr.js index 16e43fafdb..cf8258283e 100644 --- a/bundles/statistics/statsgrid/resources/locale/fr.js +++ b/bundles/statistics/statsgrid/resources/locale/fr.js @@ -13,19 +13,9 @@ Oskari.registerLocalization( "title": "Cartes thématiques" }, "dataProviderInfoTitle": "Indicateurs", - "layertools": { - "table_icon": { - "tooltip": "Passer aux cartes thématiques", - "title": "Cartes thématiques" - }, - "diagram_icon": { - "tooltip": "Afficher les données dans le schéma", - "title": "Schéma" - }, - "statistics": { - "tooltip": "Passer aux cartes thématiques", - "title": "Statistiques" - } + "layerTool": { + "tooltip": "Passer aux cartes thématiques", + "title": "Cartes thématiques" }, 'publisher': { "label": "Cartes thématiques", @@ -93,7 +83,14 @@ Oskari.registerLocalization( }, "diagram": { "title": "Schéma", - "noValue": "S/O" + "noValue": "S/O", + "sort": { + "desc": "Ordre", + "name-ascending": "Nom en ordre croissant", + "name-descending": "Nom en ordre décroissant", + "value-ascending": "Valeur en ordre croissant", + "value-descending": "Valeur en ordre décroissant" + } }, "parameters": { "sex": "Sexe", @@ -104,11 +101,6 @@ Oskari.registerLocalization( "value": "Valeur", "region": "Région" }, - "datatable": "Tableau", - "published": { - "showMap": "Afficher la carte", - "showTable": "Afficher le tableau" - }, "classify": { "classify": "Classification", "labels": { @@ -176,44 +168,6 @@ Oskari.registerLocalization( "noData": "Les données ne sont pas accessibles pour le moment précis sélectionné.", "cannotCreateLegend": "Impossible de créer la légende à l'aide des valeurs choisies. Veuillez essayer des valeurs différentes." }, - "datacharts": { - "flyout": "Données recherchées", - "barchart": "Diagramme à barres", - "linechart": "Diagramme à ligne brisée", - "table": "Tableau", - "desc": "Tableau et graphiques", - "nodata": "Les indicateurs n'ont pas été choisis", - "indicatorVar": "La variable sera affichée dans le graphique", - "descColor": "Couleur du graphique", - "selectClr": "Couleur sélectionnée", - "clrFromMap": "Couleurs par classification dans la carte", - "chooseColor": "Sélectionner la couleur", - "sorting": { - "desc": "Ordre", - "name-ascending": "Nom en ordre croissant", - "name-descending": "Nom en ordre décroissant", - "value-ascending": "Valeur en ordre croissant", - "value-descending": "Valeur en ordre décroissant" - } - }, - "filter": { - "title": "Filtrage", - "indicatorToFilter": "Variable à filtrer", - "condition": "Condition", - "value": "Valeur", - "variable": "Variable", - "conditionPlaceholder": "Sélectionner la condition", - "greater": "est supérieur(e) à (>)", - "greaterEqual": "est supérieur(e) ou égal(e) à (>=)", - "equal": "est égal(e) à (=)", - "lessEqual": "est inférieur(e) ou égal(e) à (<=)", - "lessThan": "est inférieur(e) à (<)", - "between": "Entre (exclusif)", - "filter": "Filtrer les valeurs", - "desc": "Filtrer par valeur", - "filtered": "Valeurs filtrées", - "area": "Filtrer par superficie" - }, "layer": { "name": "Division par superficie de la carte thématique", "inspireName": "Carte thématique", @@ -221,16 +175,9 @@ Oskari.registerLocalization( }, "tab": { "title": "Indicateurs", - "edit": "Modifier", - "delete": "Supprimer", + "confirmDelete": "Vous supprimez l'indicateur \"{name}\". Souhaitez-vous supprimer l'indicateur?", "grid": { - "name": "Nom", - "edit": "Modifier", - "delete": "Supprimer" - }, - "popup": { - "deletetitle": "Supprimer l'indicateur", - "deletemsg": "Vous supprimez l'indicateur \"{name}\". Souhaitez-vous supprimer l'indicateur?", + "name": "Nom" } }, "userIndicators": { diff --git a/bundles/statistics/statsgrid/resources/locale/is.js b/bundles/statistics/statsgrid/resources/locale/is.js index a249675b48..65b4ede5d4 100644 --- a/bundles/statistics/statsgrid/resources/locale/is.js +++ b/bundles/statistics/statsgrid/resources/locale/is.js @@ -12,19 +12,9 @@ Oskari.registerLocalization({ "title": "Þemakort" }, "dataProviderInfoTitle": "Indicators", - "layertools": { - "table_icon": { - "tooltip": "Move to thematic maps", - "title": "Thematic maps" - }, - "diagram_icon": { - "tooltip": "Show data in diagram", - "title": "Diagram" - }, - "statistics": { - "tooltip": "Move to thematic maps", - "title": "Statistics" - } + "layerTool": { + "tooltip": "Move to thematic maps", + "title": "Thematic maps" }, 'publisher': { "label": "Þemakort", @@ -93,7 +83,14 @@ Oskari.registerLocalization({ } }, "diagram": { - "title": "Diagram" + "title": "Diagram", + "sort": { + "desc": "Order", + "name-ascending": "Name ascending", + "name-descending": "Name descending", + "value-ascending": "Value ascending", + "value-descending": "Value descending" + } }, "parameters": { "sex": "Gender", @@ -103,11 +100,6 @@ Oskari.registerLocalization({ "from": "from", "to": "to" }, - "datatable": "Table", - "published": { - "showMap": "Show map", - "showTable": "Show table" - }, "classify": { "classify": "Classification", "labels": { @@ -180,44 +172,6 @@ Oskari.registerLocalization({ "missing": { "regionsetName": "Unknown" }, - "datacharts": { - "flyout": "Searched data", - "barchart": "Bar chart", - "linechart": "Line chart", - "table": "Table", - "desc": "Table and graphs", - "nodata": "Indicators were not chosen", - "indicatorVar": "Variable to be shown in graph", - "descColor": "Color of the graph", - "selectClr": "Selected color", - "clrFromMap": "Colors by classification in the map", - "chooseColor": "Select color", - "sorting": { - "desc": "Order", - "name-ascending": "Name ascending", - "name-descending": "Name descending", - "value-ascending": "Value ascending", - "value-descending": "Value descending" - } - }, - "filter": { - "title": "Filtering", - "indicatorToFilter": "Variable to be filtered", - "condition": "Condition", - "value": "Value", - "variable": "Variable", - "conditionPlaceholder": "Select condition", - "greater": "Greater than (>)", - "greaterEqual": "Greater than or equal to (>=)", - "equal": "Equal to (=)", - "lessEqual": "Less than or equal to (<=)", - "lessThan": "Less than (<)", - "between": "Between (exclusive)", - "filter": "Filter values", - "desc": "Filter by values", - "filtered": "Filtered values", - "area": "Filter by areas" - }, "layer": { "name": "Areal division of thematic map", "inspireName": "Thematic map", @@ -225,26 +179,9 @@ Oskari.registerLocalization({ }, "tab": { "title": "Indicators", - "edit": "Edit", - "delete": "Delete", + "confirmDelete": "You are deleting the indicator \"{name}\". Do you want to delete the indicator?", "grid": { - "name": "Name", - "edit": "Edit", - "delete": "Delete" - }, - "popup": { - "deletetitle": "Delete Indicator", - "deletemsg": "You are deleting the indicator \"{name}\". Do you want to delete the indicator?", - "deleteSuccess": "Indicator removed" - }, - "button": { - "cancel": "Cancel", - "ok": "OK" - }, - "error": { - "title": "Error", - "notfound": "The indicator was not found.", - "notdeleted": "The indicator was not removed." + "name": "Name" } }, "userIndicators": { @@ -258,14 +195,6 @@ Oskari.registerLocalization({ "open": "Show indicator {indicators, plural, one {description} other {descriptions}}", "title": "Indicator {indicators, plural, one {description} other {descriptions}}", "noMetadata": "Service did not return {indicators, plural, one {description for the indicator} other {descriptions for the indicators}}" - }, - "sumo": { - "placeholder": "Select Here", - "captionFormat": "{0} selected", - "captionFormatAllSelected": "All {0} selected!", - "searchText": "Search...", - "noMatch": "No results found matching '{0}'", - "locale": ["OK", "Cancel", "Select All"] } } }); diff --git a/bundles/statistics/statsgrid/resources/locale/ru.js b/bundles/statistics/statsgrid/resources/locale/ru.js index 8137c3bedc..020d46a1f2 100644 --- a/bundles/statistics/statsgrid/resources/locale/ru.js +++ b/bundles/statistics/statsgrid/resources/locale/ru.js @@ -99,7 +99,14 @@ Oskari.registerLocalization( }, "diagram": { "title": "Диаграмма", - "noValue": "Недоступно" + "noValue": "Недоступно", + "sort": { + "desc": "Порядок", + "name-ascending": "Название по возрастанию", + "name-descending": "Название по убыванию", + "value-ascending": "Значение по возрастанию", + "value-descending": "Значение по убыванию" + } }, "parameters": { "sex": "Пол", @@ -110,11 +117,6 @@ Oskari.registerLocalization( "value": "Значение", "region": "Облаcть" }, - "datatable": "Таблица", - "published": { - "showMap": "Показать карту", - "showTable": "Показать таблицу" - }, "classify": { "classify": "Классификация", "labels": { @@ -176,44 +178,6 @@ Oskari.registerLocalization( "noData": "Данные недоступны для выбранного момента времени.", "cannotCreateLegend": "Условные обозначения не могут быть созданы выбранными значениями, попробуйте разные значения." }, - "datacharts": { - "flyout": "Искомые данные", - "barchart": "Гистограмма", - "linechart": "Линейный график", - "table": "Таблица", - "desc": "Таблица и графики", - "nodata": "Индикаторы не были выбраны", - "indicatorVar": "Переменная, показываемая на графике", - "descColor": "Цвет графика", - "selectClr": "Выбранный цвет", - "clrFromMap": "Цвета по классификации на карте", - "chooseColor": "Выбрать цвет", - "sorting": { - "desc": "Порядок", - "name-ascending": "Название по возрастанию", - "name-descending": "Название по убыванию", - "value-ascending": "Значение по возрастанию", - "value-descending": "Значение по убыванию" - } - }, - "filter": { - "title": "Фильтр", - "indicatorToFilter": "Фильтр переменной", - "condition": "Условие", - "value": "Значение", - "variable": "Переменная", - "conditionPlaceholder": "Выбрать условие", - "greater": "Больше (>)", - "greaterEqual": "Больше или равно (>=)", - "equal": "Равно (=)", - "lessEqual": "Меньше или равно (<=)", - "lessThan": "Меньше(<)", - "between": "Между (исключительно)", - "filter": "Фильтр значений", - "desc": "Фильтр по значению", - "filtered": "Отфильтрованные значения", - "area": "Фильтр по области" - }, "layer": { "name": "Ареал обитания на тематической карте", "inspireName": "Тематическая карта", @@ -221,16 +185,9 @@ Oskari.registerLocalization( }, "tab": { "title": "Индикаторы", - "edit": "Редактировать", - "delete": "Удалить", + "confirmDelete": "Вы удаляете индикатор \"{name}\". Вы действительно хотите удалить индикатор?", "grid": { - "name": "Название", - "edit": "Редактировать", - "delete": "Удалить" - }, - "popup": { - "deletetitle": "Удалить индикатор", - "deletemsg": "Вы удаляете индикатор \"{name}\". Вы действительно хотите удалить индикатор?" + "name": "Название" } }, "userIndicators": { @@ -271,9 +228,6 @@ Oskari.registerLocalization( 'title': 'Индикаторы', 'removeAll': 'Remove all', 'emptyMsg': 'Отсутствуют выбранные индикаторы' - }, - 'sumo': { - 'locale': ['ОК', 'Отменить', 'Выбрать все'] } } }); diff --git a/bundles/statistics/statsgrid/resources/locale/sv.js b/bundles/statistics/statsgrid/resources/locale/sv.js index 023b906b84..9d0767d147 100644 --- a/bundles/statistics/statsgrid/resources/locale/sv.js +++ b/bundles/statistics/statsgrid/resources/locale/sv.js @@ -12,19 +12,9 @@ Oskari.registerLocalization({ 'title': 'Tematiska kartor' }, 'dataProviderInfoTitle': 'Indikatorer', - 'layertools': { - 'table_icon': { - 'tooltip': 'gå till tematiska kartor', - 'title': 'Tematiska kartor' - }, - 'diagram_icon': { - 'tooltip': 'Visa uppgifter i diagram', - 'title': 'Diagram' - }, - 'statistics': { - 'tooltip': 'gå till tematiska kartor', - 'title': 'Statistik' - } + 'layerTool': { + 'tooltip': 'Gå till tematiska kartor', + 'title': 'Tematiska kartor' }, 'publisher': { "label": "Statistik", @@ -95,7 +85,14 @@ Oskari.registerLocalization({ }, 'diagram': { 'title': 'Stapeldiagram', - 'noValue': 'Inte tillgänglig' + 'noValue': 'Inte tillgänglig', + 'sort': { + 'desc': 'Sortera', + 'name-ascending': 'Stigande enligt namn', + 'name-descending': 'Sjunkande enligt namn', + 'value-ascending': 'Stigande enligt värde', + 'value-descending': 'Sjunkande enligt värde' + } }, 'parameters': { 'sex': 'Kön', @@ -107,11 +104,6 @@ Oskari.registerLocalization({ 'value': 'Värde', 'region': 'Område' }, - 'datatable': 'Tabell', - 'published': { - 'showMap': 'Visa karta', - 'showTable': 'Visa tabell' - }, 'classify': { 'classify': 'Klassificering', 'labels': { @@ -186,44 +178,6 @@ Oskari.registerLocalization({ 'regionsetName': 'Okänd', 'indicator': 'Okänd indikator' }, - 'datacharts': { - 'flyout': 'Sökta datamängden', - 'barchart': 'Stapeldiagram', - 'linechart': 'Linjediagram', - 'table': 'Tabell', - 'desc': 'Tabell och figurer', - 'nodata': 'Inga valda indikatorer', - 'indicatorVar': 'Variabeln som ska visas i figuren', - 'descColor': 'Figurens färg', - 'selectClr': 'Vald färg', - 'clrFromMap': 'Färgsättning enligt kartas klassificering', - 'chooseColor': 'Välj färg', - 'sorting': { - 'desc': 'Sortera', - 'name-ascending': 'Stigande enligt namn', - 'name-descending': 'Sjunkande enligt namn', - 'value-ascending': 'Stigande enligt värde', - 'value-descending': 'Sjunkande enligt värde' - } - }, - 'filter': { - 'title': 'Filtrering', - 'indicatorToFilter': 'Variabel som ska filtreras', - 'condition': 'Villkor', - 'value': 'Värde', - 'variable': 'Variabel', - 'conditionPlaceholder': 'Välj villkor', - 'greater': 'Större än (>)', - 'greaterEqual': 'Större än eller lika med (>=)', - 'equal': 'Lika med (=)', - 'lessEqual': 'Mindre än eller lika med (<=)', - 'lessThan': 'Mindre än (<)', - 'between': 'Mellan (uteslutande)', - 'filter': 'Filtrera värden', - 'desc': 'Filtrera med värden', - 'filtered': 'Filtrerade värden', - 'area': 'Filtrera med områden' - }, 'layer': { 'name': 'Områdesindelning av tematiska kartan', 'inspireName': 'Tematisk karta', @@ -231,19 +185,12 @@ Oskari.registerLocalization({ }, 'tab': { 'title': 'Indikatorer', - 'edit': 'Redigera', - 'delete': 'Ta bort', + 'confirmDelete': 'Vill du ta bort indikatorn "{name}"?', 'grid': { 'name': 'Namn', - 'edit': 'Redigera', - 'delete': 'Ta bort', 'actions': 'Handlingar', 'createDate': 'Skapad', 'updateDate': 'Uppdaterad', - }, - 'popup': { - 'deletetitle': 'Ta bort indikatorn', - 'deletemsg': 'Vill du ta bort indikatorn "{name}"?' } }, 'userIndicators': { @@ -260,8 +207,8 @@ Oskari.registerLocalization({ 'datasets': { 'title': 'Statistisk information', 'dataset': 'Datamängder', - 'noIndicator': 'Tallenna indikaattorin tiedot lisätäksesi aineistoja.', - 'noDatasets': 'Indikaattorilla ei ole tallennettuja aineistoja.' + 'noIndicator': 'Spara indikatorinformation för att lägga till datauppsättningar.', + 'noDatasets': 'Indikatorn har ingen datauppsättning.' }, 'import': { 'title': 'Hämta från urklipp', @@ -302,14 +249,6 @@ Oskari.registerLocalization({ 'noMetadata': 'Tjänsten returnerade ingen beskrivning för {indicators, plural, one {indikatorn} other {indikatorer}}.', 'updated': 'Senast uppdaterad', 'nextUpdate': 'Nästa uppdatering' - }, - 'sumo': { - 'placeholder': 'Välj här', - 'captionFormat': '{0} valda', - 'captionFormatAllSelected': 'Alla {0} valda!', - 'searchText': 'Sök...', - 'noMatch': 'Inga sökresultat hittades med "{0}"', - 'locale': ['OK', 'Avbryt', 'Välj alla'] } } }); From ef6f022cf5628b1b18d99fdbbd4c1b920b429bab Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 15:41:10 +0200 Subject: [PATCH 147/181] use iconbutton types --- .../statistics/statsgrid/MyIndicatorsList.jsx | 36 +++++++------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/bundles/statistics/statsgrid/MyIndicatorsList.jsx b/bundles/statistics/statsgrid/MyIndicatorsList.jsx index 5c3b39f4fc..0d403bbdb2 100644 --- a/bundles/statistics/statsgrid/MyIndicatorsList.jsx +++ b/bundles/statistics/statsgrid/MyIndicatorsList.jsx @@ -1,10 +1,9 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { Message, Button } from 'oskari-ui'; import { Table, ToolsContainer, getSorterFor } from 'oskari-ui/components/Table'; -import { EditOutlined } from '@ant-design/icons'; import styled from 'styled-components'; -import { IconButton, DeleteButton } from 'oskari-ui/components/buttons'; +import { IconButton } from 'oskari-ui/components/buttons'; const ButtonContainer = styled.div` margin: 10px 0 10px 0; @@ -12,10 +11,6 @@ const ButtonContainer = styled.div` justify-content: flex-end; `; -const editIconStyle = { - fontSize: '16px' -}; - export const MyIndicatorsList = ({ controller, indicators = [], loading }) => { const columnSettings = [ { @@ -24,11 +19,7 @@ export const MyIndicatorsList = ({ controller, indicators = [], loading }) => { title: <Message messageKey='tab.grid.name' />, sorter: getSorterFor('name'), defaultSortOrder: 'ascend', - render: (title, item) => { - return ( - <a onClick={() => controller.openIndicator(item)} >{title}</a> - ) - } + render: (title, item) => <a onClick={() => controller.openIndicator(item)}>{title}</a> }, { dataIndex: 'created', @@ -52,27 +43,26 @@ export const MyIndicatorsList = ({ controller, indicators = [], loading }) => { title: <Message messageKey='tab.grid.actions' />, width: 100, render: (title, item) => { + const { id, name } = item; return ( <ToolsContainer> <IconButton - className='t_edit' - title={<Message messageKey='tab.grid.edit' />} - icon={<EditOutlined style={editIconStyle} />} - onClick={() => controller.editIndicator(item.id)} + type='edit' + onClick={() => controller.editIndicator(id)} /> - <DeleteButton - type='icon' - title={<Message messageKey='tab.popup.deletemsg' messageArgs={{ name: item.name }} />} - onConfirm={() => controller.deleteIndicator(item.id)} + <IconButton + type='delete' + confirm={{ title: <Message messageKey='tab.confirmDelete' messageArgs={{ name }} /> }} + onConfirm={() => controller.deleteIndicator(id)} /> </ToolsContainer> - ) + ); } } ]; return ( - <> + <Fragment> <ButtonContainer> <Button type='primary' onClick={() => controller.addNewIndicator()}> <Message messageKey='userIndicators.add' /> @@ -87,7 +77,7 @@ export const MyIndicatorsList = ({ controller, indicators = [], loading }) => { pagination={false} loading={loading} /> - </> + </Fragment> ); }; From 83a54bc5b7fc2d6f9dfa4c1b8cceb43156062582 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 16:28:42 +0200 Subject: [PATCH 148/181] use spinning --- bundles/statistics/statsgrid/MyIndicatorsTab.jsx | 4 ++-- .../statistics/statsgrid/view/FlyoutContent.jsx | 14 ++++++-------- .../statsgrid/view/search/SearchFlyout.jsx | 10 +++------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/bundles/statistics/statsgrid/MyIndicatorsTab.jsx b/bundles/statistics/statsgrid/MyIndicatorsTab.jsx index c8a9e6b89a..1eb1496626 100644 --- a/bundles/statistics/statsgrid/MyIndicatorsTab.jsx +++ b/bundles/statistics/statsgrid/MyIndicatorsTab.jsx @@ -15,9 +15,9 @@ export const MyIndicatorsTab = ({ state, controller }) => { /> </LocaleProvider> ); -} +}; MyIndicatorsTab.propTypes = { state: PropTypes.object.isRequired, controller: PropTypes.object.isRequired -} +}; diff --git a/bundles/statistics/statsgrid/view/FlyoutContent.jsx b/bundles/statistics/statsgrid/view/FlyoutContent.jsx index 03992fe76e..bb3311423f 100644 --- a/bundles/statistics/statsgrid/view/FlyoutContent.jsx +++ b/bundles/statistics/statsgrid/view/FlyoutContent.jsx @@ -17,17 +17,15 @@ export const FlyoutContent = ({ state, children }) => { const { indicators, activeIndicator, loading } = state; const current = indicators.find(ind => ind.hash === activeIndicator); - const Component = ( + return ( <LocaleProvider value={{ bundleKey: BUNDLE_KEY }}> - <Content> - {current ? children : <Message messageKey='statsgrid.noResults' />} - </Content> + <Spin showTip spinning={loading}> + <Content> + {current ? children : <Message messageKey='statsgrid.noResults' />} + </Content> + </Spin> </LocaleProvider> ); - if (loading) { - return <Spin showTip={true}>{Component}</Spin>; - } - return Component; }; FlyoutContent.propTypes = { diff --git a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx index 1f39e753d9..360050a2c1 100644 --- a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx +++ b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx @@ -70,8 +70,8 @@ const SearchFlyout = ({ state, controller }) => { const singleIndicatorSelected = state.selectedIndicators.length === 1; const multipleRegionsetsAvailable = regionsets.length > 1; const multipleDatasourcesAvailable = datasources.length > 1; - const Component = ( - <React.Fragment> + return ( + <Spin showTip spinning={state.loading}> <Field> <b><Message messageKey='panels.newSearch.seriesTitle' /></b> <ClickableArea> @@ -146,12 +146,8 @@ const SearchFlyout = ({ state, controller }) => { onClick={() => controller.search()} /> </ButtonContainer> - </React.Fragment> + </Spin> ); - if (state.loading) { - return <Spin showTip={true}>{Component}</Spin>; - } - return Component; }; SearchFlyout.propTypes = { state: PropTypes.object.isRequired, From 4e98085324d00d5a94297d79d079ef9454bfc8ec Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 16:31:21 +0200 Subject: [PATCH 149/181] shorten name in getUILabels --- .../statsgrid/helper/StatisticsHelper.js | 15 ++++++-- .../statsgrid/view/IndicatorName.jsx | 36 ++++++++----------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/bundles/statistics/statsgrid/helper/StatisticsHelper.js b/bundles/statistics/statsgrid/helper/StatisticsHelper.js index 04b88ad51f..b5c90c054c 100644 --- a/bundles/statistics/statsgrid/helper/StatisticsHelper.js +++ b/bundles/statistics/statsgrid/helper/StatisticsHelper.js @@ -147,7 +147,7 @@ export const formatData = (data, classification) => { export const getUILabels = (ind, metadata) => { if (!metadata) { - return; + return {}; } const { selections, series } = ind; const { name, selectors, source } = metadata; @@ -163,16 +163,25 @@ export const getUILabels = (ind, metadata) => { paramsList.push({ id, value, label }); } }); - let selectorsFormatted = '(' + paramsList.map(l => l.label).join(' / ') + ')'; + let selectorsFormatted = paramsList.length ? '(' + paramsList.map(l => l.label).join(' / ') + ')' : ''; const range = series ? series.values[0] + ' - ' + series.values[series.values.length - 1] : ''; if (range) { selectorsFormatted = range + ' ' + selectorsFormatted; } + const max = 50; + const min = 20; + const full = name + ' ' + selectorsFormatted; + let short = ''; + if (full.length > max) { + const end = Math.max(max - selectorsFormatted.length, min); + short = name.substring(0, end) + '... ' + selectorsFormatted; + } return { indicator: name, source, params: selectorsFormatted, - full: name + ' ' + selectorsFormatted, + full, + short, paramsList, range }; diff --git a/bundles/statistics/statsgrid/view/IndicatorName.jsx b/bundles/statistics/statsgrid/view/IndicatorName.jsx index e4410611d5..e38d994733 100644 --- a/bundles/statistics/statsgrid/view/IndicatorName.jsx +++ b/bundles/statistics/statsgrid/view/IndicatorName.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Message, Tooltip, WarningIcon } from 'oskari-ui'; import styled from 'styled-components'; @@ -6,33 +7,24 @@ const StyledWarningIcon = styled(WarningIcon)` margin-left: 10px; `; -const getIndicatorText = (labels) => { - const { indicator, params, full } = labels; - let cutLength = 60; - let minLength = 20; - const dots = '... '; - if (indicator && full.length > cutLength) { - if (params) { - cutLength = cutLength - dots.length - params.length; - return indicator.substring(0, Math.max(minLength, cutLength)) + dots + params; - } else { - cutLength = cutLength - dots.length; - return indicator.substring(0, cutLength) + dots; - } - } else { - return full; - } -}; - export const IndicatorName = ({ indicator }) => { - if (!indicator.labels?.full || indicator.labels?.full === '') { + const { full, short } = indicator.labels || {}; + if (!full) { return ( - <span><Message messageKey='missing.indicator' /><StyledWarningIcon /></span> + <span><Message messageKey='missing.indicator' /><StyledWarningIcon /></span> + ); + } + if (!short) { + return ( + <span>{full}</span> ); } return ( - <Tooltip title={indicator.labels?.full}> - <span>{getIndicatorText(indicator.labels)}</span> + <Tooltip title={full}> + <span>{short}</span> </Tooltip> ); }; +IndicatorName.propTypes = { + indicator: PropTypes.object.isRequired +}; From 5c216f658dc3647a61d3888d13d791031eb7c3f7 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 19 Nov 2024 16:48:15 +0200 Subject: [PATCH 150/181] remove service/ --- bundles/statistics/statsgrid/service/Cache.js | 79 ---- .../statsgrid/service/CacheHelper.js | 154 -------- .../statsgrid/service/StatisticsService.js | 346 ------------------ 3 files changed, 579 deletions(-) delete mode 100644 bundles/statistics/statsgrid/service/Cache.js delete mode 100644 bundles/statistics/statsgrid/service/CacheHelper.js delete mode 100644 bundles/statistics/statsgrid/service/StatisticsService.js diff --git a/bundles/statistics/statsgrid/service/Cache.js b/bundles/statistics/statsgrid/service/Cache.js deleted file mode 100644 index 3624e28cc9..0000000000 --- a/bundles/statistics/statsgrid/service/Cache.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @class Oskari.statistics.statsgrid.Cache - */ -Oskari.clazz.define('Oskari.statistics.statsgrid.Cache', - - /** - * @method create called automatically on construction - * @static - */ - function () { - this.cache = {}; - }, { - remove: function (key) { - this.cache[key] = null; - }, - put: function (key, value) { - this.cache[key] = value; - }, - get: function (key) { - return this.cache[key]; - }, - getKeysStartingWith: function (prefix) { - return Object.keys(this.cache).filter((key) => { - return key.indexOf(prefix) === 0; - }); - }, - flushKeysStartingWith: function (prefix) { - this.getKeysStartingWith(prefix).forEach((key) => { - this.remove(key); - }); - }, - /** - * Adds a callback to a response queue. - * When multiple calls are made to same resource we only want to send one request, - * queue the callbacks and call all of them when we have a response. - * @param {String} key id for the queue - * @return {Boolean} true if this was not the first queued callback. - * True means that request is already in progress and we shouldn't start another, - * but wait for the response. - */ - addToQueue: function (key) { - const queueKey = 'queue_' + key; - let queue = this.get(queueKey); - if (!queue) { - queue = []; - } - queue.push(key); - this.put(queueKey, queue); - // if not first > request already in progress - return queue.length > 1; - }, - /** - * Finds a queue with the given key and calls the functions that are in the queue with the err and response param values. - * Clears the queue after so functions are only called once. - * Also caches the response if it's not an error - * @param {String} key id for the queue - * @param {String} err Error message when there is one - * @param {Object} response pretty much anything that needs to be passed to the callbacks as result - */ - respondToQueue: function (key, err, response) { - if (!err) { - this.put(key, response); - } - const queueKey = 'queue_' + key; - this.put(queueKey, null); - }, - /** - * Tries to find response from cache. - * @param {String} cacheKey id for the cache - * @return {any|Boolean} Cached value if cached response was found, false if not. - */ - tryCachedVersion: function (cacheKey) { - const cached = this.get(cacheKey); - if (cached) { - return cached; - } - return false; - } - }); diff --git a/bundles/statistics/statsgrid/service/CacheHelper.js b/bundles/statistics/statsgrid/service/CacheHelper.js deleted file mode 100644 index de0ecb75eb..0000000000 --- a/bundles/statistics/statsgrid/service/CacheHelper.js +++ /dev/null @@ -1,154 +0,0 @@ -import { removeIndicatorFromCache, updateIndicatorListInCache } from '../handler/SearchIndicatorOptionsHelper'; -/** - * @class Oskari.statistics.statsgrid.CacheHelper - */ -Oskari.clazz.define('Oskari.statistics.statsgrid.CacheHelper', function (cache, service) { - this.cache = cache; - this.service = service; -}, { - getRegionsKey: function (regionset) { - return 'GetRegions_' + regionset; - }, - getIndicatorMetadataKey: function (datasrc, indicatorId) { - return 'GetIndicatorMetadata_' + datasrc + '_' + indicatorId; - }, - getIndicatorDataKey: function (datasrc, indicatorId, selectors, regionset) { - let serialized = ''; - if (typeof selectors === 'object') { - serialized = '_' + Object.keys(selectors).sort().map(function (key) { - return key + '=' + JSON.stringify(selectors[key]); - }).join(':'); - } - return this.getIndicatorDataKeyPrefix(datasrc, indicatorId) + regionset + serialized; - }, - getIndicatorDataKeyPrefix: function (datasrc, indicatorId) { - return 'GetIndicatorData_' + datasrc + '_' + indicatorId + '_'; - }, - /** - * @method updateIndicatorInCache - * @param {Number} datasrc id for datasource - * @param {String} indicatorId id for indicator. Note! For new indicators on guest users it's generated - */ - updateIndicatorInCache: async function (datasrc, indicatorId, data) { - const me = this; - const updateMetadataCache = function () { - // only inject when guest user, otherwise flush from cache - const metadataCacheKey = me.getIndicatorMetadataKey(datasrc, indicatorId); - // flush/update indicator metadata from cache - const metadata = me.cache.get(metadataCacheKey) || me.__getCachedMetadataSkeleton(indicatorId); - // update indicator fields - metadata.name[Oskari.getLang()] = data.name; - metadata.description[Oskari.getLang()] = data.description; - metadata.source[Oskari.getLang()] = data.datasource; - me.cache.put(metadataCacheKey, metadata); - }; - - try { - if (data.id) { - // existing indicator -> update it - updateIndicatorListInCache(datasrc, { - id: data.id, - name: data.name - }); - // possibly update "regionsets": [1851,1855] in listing cache - updateMetadataCache(); - } else { - // new indicator - // only inject when guest user, otherwise flush from cache - updateIndicatorListInCache(datasrc, { - id: indicatorId, - name: data.name, - regionsets: [] - }); - updateMetadataCache(); - } - } catch (error) { - throw new Error(error); - } - }, - updateIndicatorDataCache: async function (datasrc, indicatorId, selectors, regionset, data) { - const me = this; - try { - // existing indicator -> update it - const existingIndicator = updateIndicatorListInCache(datasrc, { - id: indicatorId, - newRegionset: regionset - }); - - // only inject when guest user, otherwise flush from cache - const metadataCacheKey = me.getIndicatorMetadataKey(datasrc, indicatorId); - // flush/update indicator metadata from cache - const metadata = me.cache.get(metadataCacheKey) || me.__getCachedMetadataSkeleton(indicatorId); - // update regionsets - metadata.regionsets = existingIndicator.regionsets; - Object.keys(selectors).forEach(function (selectorId) { - const selectorValue = selectors[selectorId]; - const existingSelector = metadata.selectors.find(function (item) { - return item.id === selectorId; - }); - if (!existingSelector) { - // new selector -> add it with the only allowed value (the one we are inserting) - metadata.selectors.push({ - id: selectorId, - name: selectorId, - allowedValues: [{ - name: selectorValue, - id: selectorValue - }] - }); - } else { - // there's an existing one, check if the value we are adding is new or not - const existingValue = existingSelector.allowedValues.find(function (item) { - if (typeof item !== 'object') { - // allowed value might be a simple value instead of an object - return '' + item === '' + selectorValue; - } - return '' + item.id === '' + selectorValue; - }); - if (!existingValue) { - // only need to modify the values if it's a new value for the selector - existingSelector.allowedValues.push({ - name: selectorValue, - id: selectorValue - }); - } - } - }); - me.cache.put(metadataCacheKey, metadata); - - const dataCacheKey = me.getIndicatorDataKey(datasrc, indicatorId, selectors, regionset); - me.cache.put(dataCacheKey, data); - } catch (error) { - throw new Error(error); - } - }, - __getCachedMetadataSkeleton: function (indicatorId) { - return { - public: true, - id: indicatorId, - name: {}, - description: {}, - source: {}, - regionsets: [], - selectors: [] - }; - }, - clearCacheOnDelete: function (datasrc, indicatorId, selectors, regionset) { - const metadataCacheKey = this.getIndicatorMetadataKey(datasrc, indicatorId); - if (!selectors && !regionset) { - // removed the whole indicator: flush indicator from cache - this.cache.flushKeysStartingWith(this.getIndicatorDataKeyPrefix(datasrc, indicatorId)); - // remove single with guest user - removeIndicatorFromCache(datasrc, indicatorId); - this.cache.remove(metadataCacheKey); - } else if (Oskari.user().isLoggedIn()) { - // flush the cache for logged in user so it gets reloaded from the server - // for guests this will show some erronous info, but it's a beast to track which - // year/regionsets actually have data and can be removed - this.cache.remove(metadataCacheKey); - // flush whole user indicators listing from cache for logged in user - removeIndicatorFromCache(datasrc); - this.cache.remove(this.getIndicatorDataKey(datasrc, indicatorId, selectors, regionset)); - } - } -}); diff --git a/bundles/statistics/statsgrid/service/StatisticsService.js b/bundles/statistics/statsgrid/service/StatisticsService.js deleted file mode 100644 index d90c160a3a..0000000000 --- a/bundles/statistics/statsgrid/service/StatisticsService.js +++ /dev/null @@ -1,346 +0,0 @@ -/** - * @class Oskari.statistics.statsgrid.StatisticsService - */ -(function (Oskari) { - /* eslint-disable n/no-callback-literal */ - const _log = Oskari.log('StatsGrid.StatisticsService'); - let _cacheHelper = null; - - Oskari.clazz.define('Oskari.statistics.statsgrid.StatisticsService', function (instance, locale) { - this.instance = instance; - this.sandbox = instance.getSandbox(); - this.locale = locale; - this.cache = Oskari.clazz.create('Oskari.statistics.statsgrid.Cache'); - _cacheHelper = Oskari.clazz.create('Oskari.statistics.statsgrid.CacheHelper', this.cache, this); - - this.log = Oskari.log(this.getQName()); - }, { - __name: 'StatsGrid.StatisticsService', - __qname: 'Oskari.statistics.statsgrid.StatisticsService', - - getQName: function () { - return this.__qname; - }, - getName: function () { - return this.__name; - }, - getSandbox: function () { - return this.sandbox; - }, - /** - * Calls callback with a list of regions for the regionset. - * @param {Number} regionset regionset id - */ - getRegions: async function (regionset) { - if (!regionset) { - this.log.warn('Requested regions without regionset'); - throw new Error('Regionset missing'); - } - const cacheKey = _cacheHelper.getRegionsKey(regionset); - const cachedResponse = this.cache.tryCachedVersion(cacheKey); - if (cachedResponse) { - // found a cached response - return new Promise((resolve) => { - resolve(cachedResponse); - }); - } - const createNewAttempt = (resolve) => { - const cachedResponse = this.cache.tryCachedVersion(cacheKey); - if (cachedResponse) { - resolve(cachedResponse); - } else { - // try again in 200ms - setTimeout(() => createNewAttempt(resolve), 200); - } - }; - if (this.cache.addToQueue(cacheKey)) { - // request already in progress - // return a promise to make async await work!! - return new Promise((resolve) => createNewAttempt(resolve)); - } - // call GetRegions with parameter regionset=regionset - // use first param as error indicator - null == no error - try { - const response = await fetch(Oskari.urls.getRoute('GetRegions', { - regionset, - srs: this.sandbox.getMap().getSrsName() - }), { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - if (!response.ok) { - throw new Error(response.statusText); - } - const result = await response.json(); - const onlyWithNames = result.regions.filter(region => region.name); - // stores value to cache so we can get it later in the createNewAttempt() - this.cache.respondToQueue(cacheKey, null, onlyWithNames); - return onlyWithNames; - } catch (error) { - this.cache.respondToQueue(cacheKey, 'Error loading regions'); - throw new Error(error); - } - }, - - /** - * Calls callback with a list of indicators for the datasource. - * @param {Number} ds datasource id - * @param {Number} indicator indicator id - */ - getIndicatorMetadata: async function (ds, indicator) { - if (!ds || !indicator) { - // log error message - throw new Error('Datasource or indicator missing'); - } - - const cacheKey = _cacheHelper.getIndicatorMetadataKey(ds, indicator); - const cachedResponse = this.cache.tryCachedVersion(cacheKey); - if (cachedResponse) { - // found a cached response - return cachedResponse; - } - - if (this.cache.addToQueue(cacheKey)) { - // request already in progress - return; - } - // call GetIndicatorMetadata with parameter datasource=ds and indicator=indicator - // use first param as error indicator - null == no error - try { - const response = await fetch(Oskari.urls.getRoute('GetIndicatorMetadata', { - datasource: ds, - indicator - }), { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - if (!response.ok) { - throw new Error(response.statusText); - } - const result = await response.json(); - - this.cache.respondToQueue(cacheKey, null, result); - return result; - } catch (error) { - this.cache.respondToQueue(cacheKey, 'Error loading indicator metadata'); - throw new Error(error); - } - }, - /** - * Calls callback with a list of indicators for the datasource. - * @param {Object} indicator indicator id - * @param {Number} regionset regionset - */ - getIndicatorData: async function (indicator = {}, regionset) { - const { ds, id, selections, series } = indicator; - if (!ds || !id || !regionset) { - // log error message - throw new Error('Datasource, regionset or indicatorId missing'); - } - if (series && series.values.indexOf(selections[series.id]) === -1) { - throw new Error('Requested dataset is out of range'); - } - - const cacheKey = _cacheHelper.getIndicatorDataKey(ds, id, selections, regionset); - _log.debug('Getting data with key', cacheKey); - - const cachedResponse = this.cache.tryCachedVersion(cacheKey); - if (cachedResponse) { - // found a cached response - return cachedResponse; - } - - if (this.cache.addToQueue(cacheKey)) { - // request already in progress - return; - } - // call GetIndicatorData with parameters: - // - datasource=ds - // - indicator=indicator - // - selectors=serialized params - // - regionset = regionset - // use first param as error indicator - null == no error - try { - const response = await fetch(Oskari.urls.getRoute('GetIndicatorData', { - datasource: ds, - indicator: id, - regionset, - selectors: JSON.stringify(selections || {}) - }), { - method: 'GET', - headers: { - 'Accept': 'application/json' - } - }); - if (!response.ok) { - await response.json(); - throw new Error(response.statusText); - } - const result = await response.json(); - this.cache.respondToQueue(cacheKey, null, result); - return result; - } catch (error) { - this.cache.respondToQueue(cacheKey, 'Error loading indicator data'); - throw new Error(error); - } - }, - saveIndicator: async function (datasrc, data) { - if (!datasrc) { - throw new Error('Datasource missing'); - } - if (!data) { - throw new Error('Data missing'); - } - - if (!Oskari.user().isLoggedIn()) { - // successfully saved for guest user - const indicatorId = data.id || 'RuntimeIndicator' + Oskari.seq.nextVal('RuntimeIndicator'); - _cacheHelper.updateIndicatorInCache(datasrc, indicatorId, data); - return { - ds: datasrc, - id: indicatorId - }; - } - const body = { - // my indicators datasource id - datasource: datasrc, - name: data.name, - desc: data.description, - // textual name for the source the data is from - source: data.datasource - }; - - if (data.id) { - body.id = data.id; - } - // send data to server for logged in users - try { - const response = await fetch(Oskari.urls.getRoute('SaveIndicator'), { - method: 'POST', - headers: { - Accept: 'application/json' - }, - body: new URLSearchParams(body) - }); - if (!response.ok) { - throw new Error(response.statusText); - } - const result = await response.json(); - _log.debug('SaveIndicator', result); - _cacheHelper.updateIndicatorInCache(datasrc, result.id, data); - return { - ds: datasrc, - id: result.id - }; - } catch (error) { - throw new Error('Error saving data to server'); - } - }, - saveIndicatorData: async function (datasrc, indicatorId, selectors, data) { - if (!datasrc) { - throw new Error('Datasource missing'); - } - if (!indicatorId) { - throw new Error('Indicator missing'); - } - if (!selectors) { - throw new Error('Selectors missing'); - } - if (!data) { - throw new Error('Data missing'); - } - const regionset = selectors.regionset; - const actualSelectors = {}; - Object.keys(selectors).forEach(function (selectorId) { - if (selectorId !== 'regionset') { - // filter out regionset - actualSelectors[selectorId] = selectors[selectorId]; - } - }); - if (!Oskari.user().isLoggedIn()) { - // successfully saved for guest user - _cacheHelper.updateIndicatorDataCache(datasrc, indicatorId, actualSelectors, regionset, data); - return; - } - // send data to server for logged in users - try { - const response = await fetch(Oskari.urls.getRoute('AddIndicatorData'), { - method: 'POST', - headers: { - 'Accept': 'application/json' - }, - body: new URLSearchParams({ - datasource: datasrc, - id: indicatorId, - selectors: JSON.stringify(actualSelectors), - regionset: regionset, - data: JSON.stringify(data) - }) - }); - if (!response.ok) { - throw new Error(response.statusText); - } - const result = await response.json(); - _log.debug('AddIndicatorData', result); - _cacheHelper.updateIndicatorDataCache(datasrc, indicatorId, actualSelectors, regionset, data); - return result; - } catch (error) { - throw new Error('Error saving data to server'); - } - }, - /** - * selectors and regionset are optional -> will only delete dataset from indicator if given - */ - deleteIndicator: async function (datasrc, indicatorId, selectors, regionset) { - const stateHandler = this.instance.getStateHandler(); - if (!stateHandler) { - throw new Error('Failed to get state handler'); - } - // remove indicators from state before deleting indicator data - stateHandler.getController().removeIndicators(datasrc, indicatorId); - if (!Oskari.user().isLoggedIn()) { - // just flush cache - _cacheHelper.clearCacheOnDelete(datasrc, indicatorId, selectors, regionset); - return; - } - const data = { - datasource: datasrc, - id: indicatorId - }; - - const postData = new URLSearchParams(data); - postData.append('datasource', datasrc); - postData.append('id', indicatorId); - if (selectors && typeof selectors === 'object') { - // only remove dataset from indicator, not the whole indicator - postData.append('selectors', JSON.stringify(selectors)); - postData.append('regionset', regionset); - } - - try { - const response = await fetch(Oskari.urls.getRoute('DeleteIndicator'), { - method: 'POST', - body: postData, - headers: { - 'Accept': 'application/json' - } - }); - if (!response.ok) { - throw new Error(response.statusText); - } - const result = await response.json(); - _log.debug('DeleteIndicator', result); - _cacheHelper.clearCacheOnDelete(datasrc, indicatorId, selectors, regionset); - return result; - } catch (error) { - throw new Error('Error on server'); - } - } - }, { - protocol: ['Oskari.mapframework.service.Service'] - }); -}(Oskari)); From dc0f2613a25e1f7af2e31388942eaaeaca6fd90c Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 20 Nov 2024 08:56:49 +0200 Subject: [PATCH 151/181] use error key --- bundles/statistics/statsgrid/handler/IndicatorHelper.js | 3 +-- bundles/statistics/statsgrid/helper/RegionsHelper.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/IndicatorHelper.js b/bundles/statistics/statsgrid/handler/IndicatorHelper.js index 572ead0c9c..341b0ef44b 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorHelper.js +++ b/bundles/statistics/statsgrid/handler/IndicatorHelper.js @@ -102,8 +102,7 @@ export const getIndicatorMetadata = async (datasourceId, indicatorId) => { indicatorMetadataStore[cacheKey] = processed; return processed; } catch (error) { - // TODO: indicatorMetadataError - throw new Error(error); + throw new Error('indicatorMetadataError'); } }; const processMetadata = (meta) => { diff --git a/bundles/statistics/statsgrid/helper/RegionsHelper.js b/bundles/statistics/statsgrid/helper/RegionsHelper.js index 4158b1ec90..3144dd6e69 100644 --- a/bundles/statistics/statsgrid/helper/RegionsHelper.js +++ b/bundles/statistics/statsgrid/helper/RegionsHelper.js @@ -38,7 +38,6 @@ export const getRegionsAsync = async (regionset) => { regionsStore[regionset] = onlyWithNames; return onlyWithNames; } catch (error) { - // TODO: regionsDataError - throw new Error(error); + throw new Error('regionsDataError'); } }; From 830ef5a87700782732f15115eb8227f8aa2d7830 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 20 Nov 2024 09:07:08 +0200 Subject: [PATCH 152/181] update loc --- bundles/statistics/statsgrid/resources/locale/en.js | 2 +- bundles/statistics/statsgrid/resources/locale/sv.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bundles/statistics/statsgrid/resources/locale/en.js b/bundles/statistics/statsgrid/resources/locale/en.js index c51c6c3f5a..9af59419c2 100644 --- a/bundles/statistics/statsgrid/resources/locale/en.js +++ b/bundles/statistics/statsgrid/resources/locale/en.js @@ -208,7 +208,7 @@ Oskari.registerLocalization({ 'title': 'Statistical data', 'dataset': 'Dataset', 'noIndicator': 'Save indicator information to add datasets.', - 'noDatasets': 'Indicator doesn\'t have any dataset.' + 'noDatasets': 'Indicator doesn\'t have any stored dataset.' }, 'import': { 'title': 'Import from the clipboard', diff --git a/bundles/statistics/statsgrid/resources/locale/sv.js b/bundles/statistics/statsgrid/resources/locale/sv.js index 9d0767d147..bd77d4bcbb 100644 --- a/bundles/statistics/statsgrid/resources/locale/sv.js +++ b/bundles/statistics/statsgrid/resources/locale/sv.js @@ -207,8 +207,8 @@ Oskari.registerLocalization({ 'datasets': { 'title': 'Statistisk information', 'dataset': 'Datamängder', - 'noIndicator': 'Spara indikatorinformation för att lägga till datauppsättningar.', - 'noDatasets': 'Indikatorn har ingen datauppsättning.' + 'noIndicator': 'Spara indikatorinformation för att lägga till datamängder.', + 'noDatasets': 'Indikatorn har ingen lagrad datamängd.' }, 'import': { 'title': 'Hämta från urklipp', From 94a0d77476900d6fb858f56732d3a47447eb1ae5 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 20 Nov 2024 14:13:00 +0200 Subject: [PATCH 153/181] fix error keys --- bundles/statistics/statsgrid/handler/SearchHandler.js | 8 +++++--- bundles/statistics/statsgrid/resources/locale/en.js | 2 ++ bundles/statistics/statsgrid/resources/locale/fi.js | 2 ++ bundles/statistics/statsgrid/resources/locale/sv.js | 2 ++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 3c3e99baa0..c5c8715b1a 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -382,7 +382,8 @@ class SearchController extends AsyncStateHandler { if (!Array.isArray(values)) { indSearchValues.selections[key] = values; if (!checkAllowed(values)) { - indSearchValues.error = 'invalidSelection'; + indSearchValues.error = 'notAllowedSelection'; + indSearchValues.notAllowedKey = key; } return; } @@ -416,7 +417,7 @@ class SearchController extends AsyncStateHandler { invalid: values.filter(val => !checkAllowed(val)) }); }); - // Add own search for each value of the multiple select + // Add own search for each value of the multiple select (e.g Year) if (multiSelections.length) { const indicators = []; multiSelections.forEach(({ key, values, invalid }) => { @@ -424,7 +425,8 @@ class SearchController extends AsyncStateHandler { const selections = { ...indSearchValues.selections, [key]: val }; const indicator = { ...indSearchValues, selections }; if (invalid.includes(val)) { - indicator.error = 'invalidSelection'; + indicator.error = 'notAllowedSelection'; + indicator.notAllowedKey = key; } indicators.push(indicator); }); diff --git a/bundles/statistics/statsgrid/resources/locale/en.js b/bundles/statistics/statsgrid/resources/locale/en.js index 9af59419c2..982e0a10c8 100644 --- a/bundles/statistics/statsgrid/resources/locale/en.js +++ b/bundles/statistics/statsgrid/resources/locale/en.js @@ -169,6 +169,8 @@ Oskari.registerLocalization({ 'cannotDisplayAsSeries': 'Indicator cannot be inspected as a series.', 'noDataForIndicators': 'Service did not return data for {indicators, plural, one {the indicator} other {indicators}}', 'onlyPartialDataForIndicators': 'Service did not return all data for {indicators, plural, one {the indicator} other {indicators}}', + 'notAllowedRegionset': 'Data is not available for the selected regional division.', + 'notAllowedSelection': 'Data is not available for the selections.', 'noActiveLegend': 'Data was not selected, select data to see map classification.', 'noEnough': 'The data is too small to be classified, try different data or change limitings.', 'noData': 'Data is not available for the selected point in time.', diff --git a/bundles/statistics/statsgrid/resources/locale/fi.js b/bundles/statistics/statsgrid/resources/locale/fi.js index 842ede3d8f..cb0031ef38 100644 --- a/bundles/statistics/statsgrid/resources/locale/fi.js +++ b/bundles/statistics/statsgrid/resources/locale/fi.js @@ -169,6 +169,8 @@ Oskari.registerLocalization({ 'cannotDisplayAsSeries': 'Indikaattoria ei voida tarkastella sarjana', 'noDataForIndicators': 'Palvelusta ei saatu tietoja {indicators, plural, one {indikaattorille} other {indikaattoreille}}', 'onlyPartialDataForIndicators': 'Palvelusta ei saatu kaikkia tietoja {indicators, plural, one {indikaattorille} other {indikaattoreille}}', + 'notAllowedRegionset': 'Aineistoa ei ole saatavilla valitulle aluejaolle.', + 'notAllowedSelection': 'Aineistoa ei ole saatavilla valinnoille.', 'noActiveLegend': 'Ei valittuna aineistoa, valitse aineisto nähdäksesi kartan luokittelun.', 'noEnough': 'Aineisto on liian pieni luokittelun muodostamiseksi, kokeile eri aineistoa tai muuta rajauksia.', 'noData': 'Aineistoa ei ole saatavilla valitsemaltasi ajankohdalta', diff --git a/bundles/statistics/statsgrid/resources/locale/sv.js b/bundles/statistics/statsgrid/resources/locale/sv.js index bd77d4bcbb..8539bf001d 100644 --- a/bundles/statistics/statsgrid/resources/locale/sv.js +++ b/bundles/statistics/statsgrid/resources/locale/sv.js @@ -170,6 +170,8 @@ Oskari.registerLocalization({ 'noDataForIndicators': 'Tjänsten returnerade ingen data för {indicators, plural, one {indikatorn} other {indikatorer}}', 'onlyPartialDataForIndicators': 'Tjänsten returnerade inte alla data för {indicators, plural, one {indikatorn} other {indikatorer}}', 'noActiveLegend': 'Inga valda datamängder, välj datamängd för att se kartans klassificering.', + 'notAllowedRegionset': 'Ingen data vid den valda områdesindelning.', + 'notAllowedSelection': 'Ingen data vid den valda datamängden.', 'noEnough': 'Datamängden är för liten för att klassificeras, försök en annan datamängd eller avgränsning.', 'noData': 'Ingen data vid den valda tidspunkten.', 'cannotCreateLegend': 'Teckenförklaringen kan inte skapas utgående från de valda värden, vänligen försök andra värden.' From 09831eeba30109d58e13878e3ebf893808fb2954 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 20 Nov 2024 14:13:50 +0200 Subject: [PATCH 154/181] refine search label to own row --- bundles/statistics/statsgrid/view/search/SearchFlyout.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx index 360050a2c1..52fef5b499 100644 --- a/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx +++ b/bundles/statistics/statsgrid/view/search/SearchFlyout.jsx @@ -44,6 +44,10 @@ const MetadataButton = styled(Button)` margin-right: auto; padding: 0; `; +const ParamsLabel = styled.div` + font-weight: bold; + margin-bottom: 10px; +`; const UserIndicator = ({ isSingle, onClick }) => { const type = isSingle ? 'edit' : 'add'; @@ -130,7 +134,7 @@ const SearchFlyout = ({ state, controller }) => { <Message messageKey='metadataPopup.open' messageArgs={{ indicators: state.selectedIndicators.length }} /> </MetadataButton> )} - <b><Message messageKey='panels.newSearch.refineSearchLabel' /></b> + <ParamsLabel><Message messageKey='panels.newSearch.refineSearchLabel' /></ParamsLabel> <IndicatorParams { ...state } allRegionsets={regionsets} From a9c6bf47de88b9616f59419b8f6878f4b51b1f60 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 20 Nov 2024 14:14:20 +0200 Subject: [PATCH 155/181] show no data error for partial series --- bundles/statistics/statsgrid/view/search/ErrorPopup.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx index 6e9282868d..e4d7226c9e 100644 --- a/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx +++ b/bundles/statistics/statsgrid/view/search/ErrorPopup.jsx @@ -23,7 +23,7 @@ const Popup = ({ errors }) => { const selection = partialSeries ? getInvalidSerie(partialSeries) : getSelection(selections); - const cause = errorLoc[error]; + const cause = partialSeries ? errorLoc.noData : errorLoc[error]; const errorMsg = cause ? `: ${cause}` : ''; return <div key={i}>{`${name} (${selection})${errorMsg}`}</div>; })} From 253f391e8b0a10c9a2a46d4591ca805352ab1d08 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Wed, 20 Nov 2024 15:20:17 +0200 Subject: [PATCH 156/181] announcements tools reactification --- .../publisher/AnnouncementToolComponent.jsx | 67 +++ .../publisher/AnnouncementsPopup.jsx | 103 +++++ .../publisher/AnnouncementsTool.js | 437 ++++++------------ .../publisher/AnnouncementsToolHandler.js | 78 ++++ .../resources/locale/en.js | 2 + .../resources/locale/fi.js | 2 + .../resources/locale/sv.js | 2 + .../publisher2/resources/locale/en.js | 1 - .../publisher2/resources/locale/fi.js | 1 - .../publisher2/resources/locale/sv.js | 1 - 10 files changed, 396 insertions(+), 298 deletions(-) create mode 100644 bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx create mode 100644 bundles/admin/admin-announcements/publisher/AnnouncementsPopup.jsx create mode 100644 bundles/admin/admin-announcements/publisher/AnnouncementsToolHandler.js diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx b/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx new file mode 100644 index 0000000000..1d7e7f9326 --- /dev/null +++ b/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { PropTypes } from 'prop-types'; +import { Message, Checkbox, Button } from 'oskari-ui'; +import styled from 'styled-components'; + +const Col = styled('div')` + display: flex; + flex-direction: column; +`; + +const SelectedAnnouncements = styled('div')` + display: flex; + flex-direction: column; + margin-top: 1em; +`; + +const SelectedAnnouncementsTitle = styled('div')` + font-weight: bold; +`; + +const SelectedAnnouncementsText = styled('div')` + font-style: italic; +`; + +const SelectedAnnouncement = ({ announcement, lang }) => { + return <SelectedAnnouncementsText>{ Oskari.getLocalized(announcement.locale, lang)?.title }</SelectedAnnouncementsText>; +}; + +const SelectButton = styled(Button)` + margin-top: 0.5em; + width: 50%; +`; + +SelectedAnnouncement.propTypes = { + announcement: PropTypes.object, + lang: PropTypes.string +}; + +export const AnnouncementToolComponent = ({ state, controller }) => { + const { noUI, announcements, selectedAnnouncements } = state; + + return ( + <Col> + <Checkbox checked={noUI} onChange={ evt => controller.setNoUI(evt.target.checked) }> + <Message bundleKey={'admin-announcements'} messageKey={'publisher.noUI'}/> + </Checkbox> + + <SelectedAnnouncements> + <SelectedAnnouncementsTitle>{Oskari.getMsg('admin-announcements', 'publisher.selectedAnnouncementsTitle')}</SelectedAnnouncementsTitle> + { announcements + .filter((ann) => selectedAnnouncements?.includes(ann.id)) + .map((announcement) => { + return <SelectedAnnouncement key={ announcement.id } announcement={announcement} lang={state?.lang}/>; + }) + } + </SelectedAnnouncements> + <SelectButton onClick={() => controller.showPopup()}> + <Message bundleKey={'admin-announcements'} messageKey={'tool.buttonLabel'}/> + </SelectButton> + + </Col>); +}; + +AnnouncementToolComponent.propTypes = { + state: PropTypes.object, + controller: PropTypes.object +}; diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementsPopup.jsx b/bundles/admin/admin-announcements/publisher/AnnouncementsPopup.jsx new file mode 100644 index 0000000000..6f18c1f5aa --- /dev/null +++ b/bundles/admin/admin-announcements/publisher/AnnouncementsPopup.jsx @@ -0,0 +1,103 @@ +import { showPopup } from 'oskari-ui/components/window'; +import React from 'react'; +import { Checkbox, Message, WarningIcon } from 'oskari-ui'; +import styled from 'styled-components'; +import { getDateRange, isOutdated } from '../../../framework/announcements/service/util'; +import { PrimaryButton } from 'oskari-ui/components/buttons'; + +const PopupContentContainer = styled('div')` + margin: 1em; + width: 25vw; + display: flex; + flex-direction: column; +`; + +const Disclaimer = styled('div')` + margin-bottom: 1em; + flex-grow: 0; +`; + +const PopupContent = styled('div')` + flex-grow: 1; + max-height: 50vh; + overflow-y: auto; +`; + +const Footer = styled('div')` + flex-grow: 0; + margin: 0 auto; +`; + +const Row = styled('div')` + display: flex; + flex-direction: row; + justify-content: space-between; +`; + +const ColHeading = styled('div')` + font-weight: bold; +`; + +const Col = styled('div')` +`; + +const getContent = (state, controller, onClose) => { + const { announcements, selectedAnnouncements, lang } = state; + /* + let { announcements } = state; + announcements = announcements.concat(announcements).concat(announcements).concat(announcements); + */ + const title = <Message bundleKey='admin-announcements' messageKey='tool.popup.title'/>; + + const content = <PopupContentContainer> + <Disclaimer> + <Message bundleKey='admin-announcements' messageKey='tool.popup.disclaimer'/> + </Disclaimer> + <PopupContent> + <Row> + <ColHeading> + <Message bundleKey='admin-announcements' messageKey='tool.announcementsName'></Message> + </ColHeading> + <ColHeading> + <Message bundleKey='admin-announcements' messageKey='tool.announcementsTime'></Message> + </ColHeading> + </Row> + {announcements.map((announcement) => { + const dateRange = getDateRange(announcement); + const daterangeOutdated = isOutdated(announcement); + + return <Row key={announcement.id}> + <Col> + <Checkbox + onChange={(e) => controller.updateSelectedAnnouncements(e.target.checked, announcement.id)} + checked = { !daterangeOutdated && !!selectedAnnouncements?.includes(announcement.id)} + disabled={daterangeOutdated}> + {Oskari.getLocalized(announcement.locale, lang)?.title} {daterangeOutdated && <WarningIcon/>} + </Checkbox> + </Col> + <Col>{dateRange}</Col> + </Row>; + })} + </PopupContent> + <Footer> + <PrimaryButton type={'close'} onClick={onClose}/> + </Footer> + </PopupContentContainer>; + + return { + title, + content + }; +}; + +export const showAnnouncementsPopup = (state, controller, onClose) => { + const { title, content } = getContent(state, controller, onClose); + const controls = showPopup(title, content, onClose, {}); + return { + ...controls, + update: (state) => { + const { title, content } = getContent(state, controller, onClose); + controls.update(title, content); + } + }; +}; diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js index fc22cbdc78..2a7590df00 100644 --- a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js +++ b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js @@ -1,320 +1,167 @@ import { Messaging } from 'oskari-ui/util'; -import { getDateRange, isOutdated } from '../../../framework/announcements/service/util'; +import { AbstractPublisherTool } from '../../../framework/publisher2/tools/AbstractPublisherTool'; +import { AnnouncementToolComponent } from './AnnouncementToolComponent'; +import { AnnouncementsToolHandler } from './AnnouncementsToolHandler'; + +class AnnouncementsTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + this.handler = new AnnouncementsToolHandler(this); -Oskari.clazz.define('Oskari.admin.bundle.admin-announcements.publisher.AnnouncementsTool', - function () { this.sandbox = Oskari.getSandbox(); this.lang = Oskari.getLang(); this.localization = Oskari.getLocalization('admin-announcements'); - this.announcements = {}; this.allowedLocations = ['top left', 'top center', 'top right']; this.lefthanded = 'top right'; this.righthanded = 'top left'; - this.index = 8; - this.pluginName = 'AnnouncementsPlugin'; - this.data = []; - this.selectedAnnouncements = []; - this.groupedSiblings = true; - this.announcementsPopup = null; - - this.templates = { - announcements: jQuery( - '<div class="publisher-layout-announcements" class="tool-options">' + - '<div>' + - '<input type="text" name="publisher-announcements" disabled />' + - '<button/>' + - '</div>' + - '</div>'), - announcementsPopup: jQuery( - '<div>' + - '<div class="publisher-announcements-disclaimer">' + this.localization.tool.popup.disclaimer + '</div>' + - '<div class="publisher-announcements-inputs">' + - '<h4>' + this.localization.tool.announcementsName + '</h4><h4>' + this.localization.tool.announcementsTime + '</h4>' + - '<div class="ann-column ann-title"></div>' + - '<div class="ann-column ann-time"/></div>' + - '</div>' + - '</div>'), - inputCheckbox: jQuery('<div><input type="checkbox" name="announcement"/><label></label></div>') - }; - this.noUI = false; - }, { - - init: function (data) { - const me = this; - me.data = data; - me.selectedAnnouncements = []; - - me.noUI = data?.configuration?.announcements?.conf?.plugin?.config?.noUI === true; - - const service = me.sandbox.getService('Oskari.framework.announcements.service.AnnouncementsService'); - - if (data.configuration && data.configuration.announcements && data.configuration.announcements.conf && data.configuration.announcements.conf.plugin) { - const myId = me.getTool().id; - const enabled = data.configuration.announcements.conf.plugin.id === myId; - me.setEnabled(enabled); + } + + init (data) { + this.data = data; + const selectedAnnouncements = []; + this.noUI = data?.configuration?.announcements?.conf?.plugin?.config?.noUI === true; + const service = this.sandbox.getService('Oskari.framework.announcements.service.AnnouncementsService'); + + if (data?.configuration?.announcements?.conf?.plugin) { + const enabled = data.configuration.announcements.conf.plugin.id === this.getTool()?.id; + this.setEnabled(enabled); + + const location = data?.configuration?.announcements?.conf?.plugin?.config?.location?.classes; + const plugin = this.getPlugin(); + if (plugin && location) { + plugin.setLocation(location); } + } - service.fetchAnnouncements((err, data) => { - if (err) { - Messaging.error(this.localization.messages.getAdminAnnouncementsFailed); - return; - } - me.announcements = data; - const toolPluginAnnouncementsConf = me._getToolPluginAnnouncementsConf(); - if (toolPluginAnnouncementsConf !== null) { - me.getPlugin().updateAnnouncements(toolPluginAnnouncementsConf.config.announcements); - toolPluginAnnouncementsConf.config.announcements.forEach(announcementId => { - const announcement = me.announcements.find(ann => ann.id === announcementId); - if (announcement !== undefined && me.isAnnouncementValid(announcement)) { - // make sure there are no duplicates - const alreadyAdded = me.selectedAnnouncements.some(ann => ann.id === announcement.id); - if (!alreadyAdded) { - me.selectedAnnouncements.push(announcement); - } - } - }); - } - me.updateSelectedInput(); - }); - }, - _setEnabledImpl: function (enabled) { - if (enabled && this.selectedAnnouncements) { - this.getPlugin().updateAnnouncements(this.selectedAnnouncements); - } - }, - - getName: function () { - return 'Oskari.framework.announcements.publisher.AnnouncementsTool'; - }, - - /** - * Check if Announcement is inside the given timeframe - * @method @private isAnnouncementValid - * @param {Object} announcement announcement - * @return {Boolean} true if announcement is valid - */ - isAnnouncementValid: function (announcement) { - const announcementEnd = new Date(announcement.endDate); - const currentDate = new Date(); - return currentDate.getTime() <= announcementEnd.getTime(); - }, - - /** - * @method onEvent - * @param {Oskari.mapframework.event.Event} event a Oskari event object - * Event is handled forwarded to correct #eventHandlers if found or discarded if not. - */ - onEvent: function (event) { - const handler = this.eventHandlers[event.getName()]; - if (!handler) { + service.fetchAnnouncements((err, data) => { + if (err) { + Messaging.error(this.localization.messages.getAdminAnnouncementsFailed); return; } - return handler.apply(this, [event]); - }, - - /** - * Get tool object. - * @method getTool - * - * @returns {Object} tool description - */ - getTool: function () { - return { - id: 'Oskari.framework.announcements.plugin.AnnouncementsPlugin', - title: 'AnnouncementsPlugin', - config: {} - }; - }, - - /** - * Get extra options. - * @method getExtraOptions - * @public - * - * @returns {Object} jQuery element - */ - getExtraOptions: function () { - const me = this; - const buttonLabel = me.localization.tool.buttonLabel; - const template = me.templates.announcements.clone(); - - // Set the button handler - template.find('button').html(buttonLabel).on('click', function () { - if (me.announcementsPopup === null) { - me._openAnnouncementsDialog(jQuery(this)); - } - }); - - const labelNoUI = me.localization.publisher.noUI; - - var input = Oskari.clazz.create( - 'Oskari.userinterface.component.CheckboxInput' - ); - - input.setTitle(labelNoUI); - input.setHandler(function (checked) { - if (checked === 'on') { - me.noUI = true; - me.getPlugin().teardownUI(); - } else { - me.noUI = false; - me.getPlugin().redrawUI(Oskari.util.isMobile()); - } - }); - - input.setChecked(me.noUI); - - var inputEl = input.getElement(); - if (inputEl.style) { - inputEl.style.width = 'auto'; - } - - template.append(inputEl); - - return template; - }, - - /** - * Creates and opens the dialog from which to choose the announcements. - * - * @method _openAnnouncementsDialog - */ - _openAnnouncementsDialog: function () { - const me = this; - const popup = Oskari.clazz.create('Oskari.userinterface.component.Popup'); - const closeButton = Oskari.clazz.create('Oskari.userinterface.component.Button'); - const title = me.localization.tool.popup.title; - const content = me.templates.announcementsPopup.clone(); - - closeButton.setTitle(me.localization.tool.popup.close); - closeButton.setHandler(function () { - popup.close(true); - me.announcementsPopup = null; - }); - - me.announcements.forEach(announcement => { - const { title } = Oskari.getLocalized(announcement.locale); - const dateRange = getDateRange(announcement); - - const announcementInput = me.templates.inputCheckbox.clone(); - const idPrefix = 'oskari_announcement_select_'; - announcementInput.find('input[type=checkbox]').attr({ - id: idPrefix + announcement.id, - value: announcement.id - }); - announcementInput.find('label').html(title).attr({ - 'for': idPrefix + announcement.id - }); - - if (isOutdated(announcement)) { - content.find('div.ann-time').append('<div>' + dateRange + '<div class="icon-warning-light"></div></div>'); - announcementInput.find('input[type=checkbox]').prop('disabled', true); - content.find('div.ann-title').append(announcementInput); - } else { - content.find('div.ann-time').append('<div>' + dateRange + '</div>'); - me.selectedAnnouncements.forEach(ann => { - if (ann.id === announcement.id) { - announcementInput.find('input[type=checkbox]').prop('checked', true); + const announcements = data; + const toolPluginAnnouncementsConf = this.getToolPluginAnnouncementsConf(); + if (toolPluginAnnouncementsConf !== null) { + this.getPlugin().updateAnnouncements(toolPluginAnnouncementsConf.config.announcements); + toolPluginAnnouncementsConf.config.announcements.forEach(announcementId => { + const announcement = announcements.find(ann => ann.id === announcementId); + if (announcement !== undefined && this.isAnnouncementValid(announcement)) { + // make sure there are no duplicates + const alreadyAdded = selectedAnnouncements.some(ann => ann.id === announcement.id); + if (!alreadyAdded) { + selectedAnnouncements.push(announcement.id); } - }); - content.find('div.ann-title').append(announcementInput); - } - }); - - // Announcement is selected - content.find('input[name=announcement]').on('change', function () { - const selectedId = parseInt(this.value); - const announcement = me.announcements.find(item => item.id === selectedId); - // check if announcement is already checked, if is, add/remove accordingly - if (!this.checked) { - me.selectedAnnouncements = me.selectedAnnouncements.filter(function (ann) { - return ann.id !== announcement.id; - }); - } else { - me.selectedAnnouncements.push(announcement); - } - me.getPlugin().updateAnnouncements(me.selectedAnnouncements); - me.updateSelectedInput(); - }); - - popup.show(title, content, [closeButton]); - me.announcementsPopup = popup; - }, - // Shows user the currently selected announcement titles next to the tool (informative input/non-functional) - updateSelectedInput: function () { - jQuery('div.basic_publisher').find('input[name=publisher-announcements]').val(this.selectedAnnouncements.map(i => Oskari.getLocalized(i.locale, this.lang)?.title).toString()); - }, - /** - * @private @method _getToolPluginAnnouncementsConf - * @return {Object / null} config or null if not found - */ - _getToolPluginAnnouncementsConf: function () { - var me = this; - var isConfig = !!((me.data && me.data.configuration)); - var isPlugin = !!((isConfig && me.data.configuration.announcements && - me.data.configuration.announcements.conf && me.data.configuration.announcements.conf.plugin)); - var toolPlugin = null; - if (isPlugin) { - toolPlugin = me.data.configuration.announcements.conf.plugin; + } + }); } - return toolPlugin; - }, - _getAnnouncementsSelection: function () { - const me = this; - const announcementsSelection = {}; - var pluginValues = me.getPlugin().getSelectedAnnouncements(); - if (pluginValues.announcements) { - announcementsSelection.announcements = pluginValues.announcements; - } - return announcementsSelection; - }, + this.handler.init({ + noUI: this.noUI, + announcements, + selectedAnnouncements + }); + }); + } + + stop () { + this.handler.closePopup(); + const plugin = this.getPlugin(); + if (plugin) { + plugin.teardownUI(); + } + } - /** - * Get values. - * @method getValues - * @public - * - * @returns {Object} tool value object - */ - getValues: function () { - const me = this; + getTool () { + return { + id: 'Oskari.framework.announcements.plugin.AnnouncementsPlugin', + title: Oskari.getMsg('admin-announcements', 'publisher.toolLabel'), + config: this.state.pluginConfig + }; + } - if (!this.isEnabled()) { - return null; - } - const pluginConfig = { id: me.getTool().id, config: me.getPlugin().getConfig() }; - const announcementsSelection = me._getAnnouncementsSelection(); + getComponent () { + return { + component: AnnouncementToolComponent, + handler: this.handler + }; + } + + /** + * Get values. + * @method getValues + * @public + * + * @returns {Object} tool value object + */ + getValues () { + if (!this.isEnabled()) { + return null; + } - if (announcementsSelection && !jQuery.isEmptyObject(announcementsSelection)) { - pluginConfig.config.announcements = announcementsSelection.announcements; - } + const pluginConfig = { id: this.getTool().id, config: this.getPlugin().getConfig() }; - if (me.noUI) { - pluginConfig.config.noUI = me.noUI; - } + const { noUI, selectedAnnouncements } = this.handler.getState(); + if (noUI) { + pluginConfig.config.noUI = noUI; + } + pluginConfig.config.announcements = selectedAnnouncements; - return { - configuration: { - announcements: { - conf: { - plugin: pluginConfig - } + return { + configuration: { + announcements: { + conf: { + plugin: pluginConfig } } - }; - }, - - /** - * Stop _stopImpl. - * @method _stopImpl - */ - _stopImpl: function () { - if (this.announcementsPopup) { - this.announcementsPopup.close(true); } + }; + } + + /** + * @private @method _getToolPluginAnnouncementsConf + * @return {Object / null} config or null if not found + */ + /* + getToolPluginAnnouncementsConf () { + const toolPlugin = !!this.data?.configuration?.announcements?.conf?.plugin || null; + return toolPlugin; + } + */ + + /** + * @private @method _getToolPluginAnnouncementsConf + * @return {Object / null} config or null if not found + */ + getToolPluginAnnouncementsConf () { + const isConfig = !!((this.data && this.data.configuration)); + const isPlugin = !!((isConfig && this.data?.configuration?.announcements?.conf?.plugin)); + let toolPlugin = null; + if (isPlugin) { + toolPlugin = this.data.configuration.announcements.conf.plugin; } - }, { - extend: ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], + return toolPlugin; + } + + /** + * Check if Announcement is inside the given timeframe + * @method @private isAnnouncementValid + * @param {Object} announcement announcement + * @return {Boolean} true if announcement is valid + */ + isAnnouncementValid (announcement) { + const announcementEnd = new Date(announcement.endDate); + const currentDate = new Date(); + return currentDate.getTime() <= announcementEnd.getTime(); + } +} + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.AnnouncementsTool', + AnnouncementsTool, + { protocol: ['Oskari.mapframework.publisher.Tool'] - }); + } +); + +export { AnnouncementsTool }; diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementsToolHandler.js b/bundles/admin/admin-announcements/publisher/AnnouncementsToolHandler.js new file mode 100644 index 0000000000..25763c81a7 --- /dev/null +++ b/bundles/admin/admin-announcements/publisher/AnnouncementsToolHandler.js @@ -0,0 +1,78 @@ +import { StateHandler, controllerMixin } from 'oskari-ui/util'; +import { showAnnouncementsPopup } from './AnnouncementsPopup'; +class UIHandler extends StateHandler { + constructor (tool) { + super(); + this.tool = tool; + + this.setState({ + lang: Oskari.getLang(), + noUI: false, + announcements: [], + selectedAnnouncements: [] + }); + }; + + init (config) { + this.updateState({ + noUI: config?.noUI, + announcements: config?.announcements, + selectedAnnouncements: config?.selectedAnnouncements || [] + }); + } + + setNoUI (value) { + this.updateState({ + noUI: value + }); + + const plugin = this.tool?.getPlugin(); + if (!plugin) { + return; + } + + if (value) { + plugin.teardownUI(); + } else { + plugin.redrawUI(Oskari.util.isMobile()); + } + } + + updateSelectedAnnouncements (checked, id) { + const { selectedAnnouncements } = this.state; + const newSelectedAnnouncements = selectedAnnouncements.filter(selectedId => selectedId !== id); + if (checked) { + newSelectedAnnouncements.push(id); + } + this.updateState({ + selectedAnnouncements: newSelectedAnnouncements + }); + + if (this.popupControls) { + this.popupControls.update(this.state, this.controller, this.closePopup); + } + } + + showPopup () { + if (this.popupControls) { + this.popupControls.close(); + return; + } + this.popupControls = showAnnouncementsPopup(this.state, this.controller, () => this.closePopup()); + } + + closePopup () { + if (this.popupControls) { + this.popupControls.close(); + } + this.popupControls = null; + } +} + +const wrapped = controllerMixin(UIHandler, [ + 'setNoUI', + 'updateSelectedAnnouncements', + 'showPopup' +]); + +export { wrapped as AnnouncementsToolHandler }; diff --git a/bundles/admin/admin-announcements/resources/locale/en.js b/bundles/admin/admin-announcements/resources/locale/en.js index b82192d09c..1b747c49f3 100644 --- a/bundles/admin/admin-announcements/resources/locale/en.js +++ b/bundles/admin/admin-announcements/resources/locale/en.js @@ -55,6 +55,8 @@ Oskari.registerLocalization( "announcementsTime": "Valid" }, "publisher": { + "toolLabel": "Announcements", + "selectedAnnouncementsTitle": "Selected announcements", "noUI": "Hide user interface (Use RPC interface)" } } diff --git a/bundles/admin/admin-announcements/resources/locale/fi.js b/bundles/admin/admin-announcements/resources/locale/fi.js index 0146becd1d..dd3136e1ec 100644 --- a/bundles/admin/admin-announcements/resources/locale/fi.js +++ b/bundles/admin/admin-announcements/resources/locale/fi.js @@ -55,6 +55,8 @@ Oskari.registerLocalization( "announcementsTime": "Voimasssa" }, "publisher": { + "toolLabel": "Ilmoitukset", + "selectedAnnouncementsTitle": "Valitut ilmoitukset", "noUI": "Piilota käyttöliittymä (käytä RPC-rajapinnan kautta)" } } diff --git a/bundles/admin/admin-announcements/resources/locale/sv.js b/bundles/admin/admin-announcements/resources/locale/sv.js index ad5b10c28b..ba54c7ff01 100644 --- a/bundles/admin/admin-announcements/resources/locale/sv.js +++ b/bundles/admin/admin-announcements/resources/locale/sv.js @@ -56,6 +56,8 @@ Oskari.registerLocalization( "announcementsTime": "Datumintervall" }, "publisher": { + "toolLabel": "Aviseringar", + "selectedAnnouncementsTitle": "Valda aviseringar", "noUI": "Dölj användargränsnittet (Använd via RPC gränssnitt)" } } diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index be588ba609..c153addf76 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "maptools": { "label": "Tools", "tooltip": "Select available map tools. Check a placement in the map preview.", - "AnnouncementsPlugin": "Announcements", "TimeseriesControlPlugin": "Time series player", "FeaturedataPlugin": "Feature data", "GetInfoPlugin": "Feature query tool", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 6d70e30b5d..e17ca65fca 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "maptools": { "label": "Kartalla näytettävät työkalut", "tooltip": "Valitse kartalla käytettävissä olevat työkalut. Tarkista asettelu esikatselukartasta.", - "AnnouncementsPlugin": "Ilmoitukset", "TimeseriesControlPlugin": "Aikasarjatoistin", "FeaturedataPlugin": "Kohdetietotaulukko", "GetInfoPlugin": "Kohdetietojen kyselytyökalu", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index aab2272b3b..79177cf1b4 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "maptools": { "label": "Verktyg", "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.", - "AnnouncementsPlugin": "Aviseringar", "TimeseriesControlPlugin": "Tidseriespelare", "FeaturedataPlugin": "Objektuppgifter", "GetInfoPlugin": "Frågverktyg för visande av objektuppgifter", From 62492f42c4252e1005ca32b566e4095929b8f71f Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Wed, 20 Nov 2024 16:16:43 +0200 Subject: [PATCH 157/181] add theme provider to react tools --- .../framework/publisher2/view/PanelReactTools.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/bundles/framework/publisher2/view/PanelReactTools.js b/bundles/framework/publisher2/view/PanelReactTools.js index 75c909ca9b..21ecfb2787 100644 --- a/bundles/framework/publisher2/view/PanelReactTools.js +++ b/bundles/framework/publisher2/view/PanelReactTools.js @@ -3,6 +3,7 @@ import { LocaleProvider } from 'oskari-ui/util'; import ReactDOM from 'react-dom'; import { ToolPanelHandler } from '../handler/ToolPanelHandler'; import { PublisherToolsList } from './form/PublisherToolsList'; +import { ThemeProvider } from 'oskari-ui/util'; /** * @class Oskari.mapframework.bundle.publisher.view.PanelAdditionalTools @@ -82,12 +83,14 @@ Oskari.clazz.define('Oskari.mapframework.bundle.publisher2.view.PanelReactTools' const contentPanel = this.panel.getContainer(); ReactDOM.render( - <LocaleProvider value={{ bundleKey: 'Publisher2' }}> - <PublisherToolsList - state={this.handler.getState()} - controller={this.handler.getController()} - /> - </LocaleProvider>, + <ThemeProvider> + <LocaleProvider value={{ bundleKey: 'Publisher2' }}> + <PublisherToolsList + state={this.handler.getState()} + controller={this.handler.getController()} + /> + </LocaleProvider> + </ThemeProvider>, contentPanel[0] ); }, From d6241dc41e0c052ecd02e330bd82d79fe4bfeb94 Mon Sep 17 00:00:00 2001 From: DenverCoder544 <131667037+DenverCoder544@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:09:04 +0200 Subject: [PATCH 158/181] Update bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sami Mäkinen <zakarfin@gmail.com> --- .../admin-announcements/publisher/AnnouncementToolComponent.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx b/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx index 1d7e7f9326..3f1743d793 100644 --- a/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx +++ b/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx @@ -46,7 +46,7 @@ export const AnnouncementToolComponent = ({ state, controller }) => { </Checkbox> <SelectedAnnouncements> - <SelectedAnnouncementsTitle>{Oskari.getMsg('admin-announcements', 'publisher.selectedAnnouncementsTitle')}</SelectedAnnouncementsTitle> +<Message LabelComponent={SelectedAnnouncementsTitle} bundleKey={'admin-announcements'} messageKey={'publisher.selectedAnnouncementsTitle'} /> { announcements .filter((ann) => selectedAnnouncements?.includes(ann.id)) .map((announcement) => { From 05ffb5d4016831da2b09cdef3523d598218be353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20M=C3=A4kinen?= <zakarfin@gmail.com> Date: Thu, 21 Nov 2024 15:17:30 +0200 Subject: [PATCH 159/181] Update bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx --- .../admin-announcements/publisher/AnnouncementToolComponent.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx b/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx index 3f1743d793..520d420f3e 100644 --- a/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx +++ b/bundles/admin/admin-announcements/publisher/AnnouncementToolComponent.jsx @@ -46,7 +46,7 @@ export const AnnouncementToolComponent = ({ state, controller }) => { </Checkbox> <SelectedAnnouncements> -<Message LabelComponent={SelectedAnnouncementsTitle} bundleKey={'admin-announcements'} messageKey={'publisher.selectedAnnouncementsTitle'} /> + <Message LabelComponent={SelectedAnnouncementsTitle} bundleKey={'admin-announcements'} messageKey={'publisher.selectedAnnouncementsTitle'} /> { announcements .filter((ann) => selectedAnnouncements?.includes(ann.id)) .map((announcement) => { From 94512db5e3346946636236a2c0b86fd48cf6e590 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Thu, 21 Nov 2024 15:22:48 +0200 Subject: [PATCH 160/181] fix stop-method --- .../admin/admin-announcements/publisher/AnnouncementsTool.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js index 2a7590df00..b7626b8e98 100644 --- a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js +++ b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js @@ -65,11 +65,8 @@ class AnnouncementsTool extends AbstractPublisherTool { } stop () { + super.stop(); this.handler.closePopup(); - const plugin = this.getPlugin(); - if (plugin) { - plugin.teardownUI(); - } } getTool () { From 586dc5b3d8887d1c74513972eb50b57c9f2a7cda Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Thu, 21 Nov 2024 15:45:36 +0200 Subject: [PATCH 161/181] timeseries tool reactification --- .../publisher2/resources/locale/en.js | 1 - .../publisher2/resources/locale/fi.js | 1 - .../publisher2/resources/locale/fr.js | 1 - .../publisher2/resources/locale/ru.js | 1 - .../publisher2/resources/locale/sv.js | 1 - .../timeseries/publisher/TimeseriesTool.js | 275 +++++++++--------- .../timeseries/resources/locale/en.js | 5 + .../timeseries/resources/locale/fi.js | 5 + .../timeseries/resources/locale/fr.js | 5 + .../timeseries/resources/locale/ru.js | 5 + .../timeseries/resources/locale/sv.js | 5 + 11 files changed, 163 insertions(+), 142 deletions(-) diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index c153addf76..32e9efca4a 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "maptools": { "label": "Tools", "tooltip": "Select available map tools. Check a placement in the map preview.", - "TimeseriesControlPlugin": "Time series player", "FeaturedataPlugin": "Feature data", "GetInfoPlugin": "Feature query tool", "selectDrawLayer": "Select map layer", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index e17ca65fca..82b050409b 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "maptools": { "label": "Kartalla näytettävät työkalut", "tooltip": "Valitse kartalla käytettävissä olevat työkalut. Tarkista asettelu esikatselukartasta.", - "TimeseriesControlPlugin": "Aikasarjatoistin", "FeaturedataPlugin": "Kohdetietotaulukko", "GetInfoPlugin": "Kohdetietojen kyselytyökalu", "selectDrawLayer": "Valitse tallennustaso", diff --git a/bundles/framework/publisher2/resources/locale/fr.js b/bundles/framework/publisher2/resources/locale/fr.js index e05fb6f997..cad5dc797b 100644 --- a/bundles/framework/publisher2/resources/locale/fr.js +++ b/bundles/framework/publisher2/resources/locale/fr.js @@ -62,7 +62,6 @@ Oskari.registerLocalization( "maptools": { "label": "Outils", "tooltip": "Sélectionner les outils de carte accessibles. Consulter une mise en place dans la prévisualisation de carte.", - "TimeseriesControlPlugin": "Lecteur chronologique", "FeaturedataPlugin": "Données de fonctionnalité", "GetInfoPlugin": "Outil d'interrogation de fonctionnalité", "selectDrawLayer": "Sélectionner la couche cartographique", diff --git a/bundles/framework/publisher2/resources/locale/ru.js b/bundles/framework/publisher2/resources/locale/ru.js index 59b40c5896..d3a411a3a0 100644 --- a/bundles/framework/publisher2/resources/locale/ru.js +++ b/bundles/framework/publisher2/resources/locale/ru.js @@ -63,7 +63,6 @@ Oskari.registerLocalization( "maptools": { "label": "Инструменты", "tooltip": "Выберите доступные инструменты карты. Проверка размещения в окне предварительного просмотра карты.", - "TimeseriesControlPlugin": "Временные ряды", "FeaturedataPlugin": "Данные объекта", "GetInfoPlugin": "Инструмент запроса объектов", "selectDrawLayer": "Выбрать слой карты", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 79177cf1b4..cc84aff1ff 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -64,7 +64,6 @@ Oskari.registerLocalization( "maptools": { "label": "Verktyg", "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.", - "TimeseriesControlPlugin": "Tidseriespelare", "FeaturedataPlugin": "Objektuppgifter", "GetInfoPlugin": "Frågverktyg för visande av objektuppgifter", "selectDrawLayer": "Välj lager för nya funktioner", diff --git a/bundles/framework/timeseries/publisher/TimeseriesTool.js b/bundles/framework/timeseries/publisher/TimeseriesTool.js index 9410f6037a..b2b380571b 100644 --- a/bundles/framework/timeseries/publisher/TimeseriesTool.js +++ b/bundles/framework/timeseries/publisher/TimeseriesTool.js @@ -1,145 +1,146 @@ -Oskari.clazz.define('Oskari.mapframework.bundle.timeseries.TimeseriesTool', - function () { - }, { - index: 0, - allowedLocations: ['top center'], - lefthanded: 'top center', - righthanded: 'top center', - allowedSiblings: [], - groupedSiblings: false, - activeTimeseries: null, - controlConfig: {}, - /** - * Initialize tool - * @params {} state data - * @method init - * @public - */ - init: function (pdata) { - this.controlConfig = { - showControl: true, - location: 'top center', - widthMargin: 200, - topMargin: '90px' - }; - if (pdata && pdata.configuration && pdata.configuration.timeseries && - pdata.configuration.timeseries.conf && - pdata.configuration.timeseries.conf.plugins) { - // Update control configuration according to app setup - var plugin = pdata.configuration.timeseries.conf.plugins.find(function (plugin) { - return plugin.id === 'Oskari.mapframework.bundle.timeseries.TimeseriesControlPlugin'; - }); - if (plugin) { - this.controlConfig = plugin.config; - } - } - // hide timeseries control if tool is disabled - if (this.isDisabled()) { - this.controlConfig.showControl = false; - } - if (this.isDisplayed()) { - // Apply configuration - this.setEnabled(this.controlConfig.showControl); - } - }, - _getTimeseriesService: function () { - if (!this.service) { - this.service = this.__sandbox.getService('Oskari.mapframework.bundle.timeseries.TimeseriesService'); +import { AbstractPublisherTool } from '../../publisher2/tools/AbstractPublisherTool'; + +class TimeSeriesTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 1; + this.group = 'additional'; + this.allowedLocations = ['top center']; + this.lefthanded = 'top center'; + this.righthanded = 'top center'; + } + + init (data) { + this.controlConfig = { + showControl: true, + location: 'top center', + widthMargin: 200, + topMargin: '90px' + }; + if (data && data.configuration && data.configuration.timeseries && + data.configuration.timeseries.conf && + data.configuration.timeseries.conf.plugins) { + // Update control configuration according to app setup + const plugin = data.configuration.timeseries.conf.plugins.find(function (plugin) { + return plugin.id === 'Oskari.mapframework.bundle.timeseries.TimeseriesControlPlugin'; + }); + if (plugin) { + this.controlConfig = plugin.config; } - return this.service; - }, - /** - * Sends configuration request to timeseries module - * - * @method _updateTimeseriesPluginConfig - * @private - */ - _updateTimeseriesPluginConfig: function () { - var requestBuilder = Oskari.requestBuilder('Timeseries.ConfigurationRequest'); - this.__sandbox.request('Publisher2', requestBuilder(this.controlConfig)); - }, - /** - * Get tool object. - * @method getTool - * @private - * - * @returns {Object} tool - */ - getTool: function () { + } + // hide timeseries control if tool is disabled + if (this.isDisabled()) { + this.controlConfig.showControl = false; + } + if (this.isDisplayed()) { + // Apply configuration + this.setEnabled(this.controlConfig.showControl); + } + } + + getTool () { + return { + id: 'Oskari.mapframework.bundle.timeseries.TimeseriesControlPlugin', + title: Oskari.getMsg('timeseries', 'publisher.TimeseriesControlPlugin.toolLabel') + }; + } + + /** + * Set enabled. + * @method setEnabled + * @public + * + * @param {Boolean} enabled is tool enabled or not + */ + setEnabled (enabled) { + this.state.enabled = enabled; + + // Set control visibility by updating it's config. + this.controlConfig.showControl = enabled; + this._updateTimeseriesPluginConfig(); + } + + /** + * Is this tool disabled. + * @method isDisabled + * @public + * + * @returns {Boolean} is tool disabled + */ + isDisabled () { + const service = this._getTimeseriesService(); + return typeof service === 'undefined' || typeof service.getActiveTimeseries() === 'undefined'; + } + + /** + * Don't show the tool if this code is loaded BUT the timeseries bundle is not started as part of the appsetup + */ + isDisplayed () { + return typeof this._getTimeseriesService() !== 'undefined'; + } + + /** + * Get values. + * @method getValues + * @public + * + * @returns {Object} tool value object + */ + getValues () { + if (this.state.enabled) { return { - id: 'Oskari.mapframework.bundle.timeseries.TimeseriesControlPlugin', - title: 'TimeseriesControlPlugin' + configuration: { + timeseries: { + conf: { + plugins: [{ id: this.getTool().id, config: this.controlConfig }] + }, + state: this.getSandbox().getStatefulComponents().timeseries.getState() + } + } }; - }, - /** - * Set enabled. - * @method setEnabled - * @public - * - * @param {Boolean} enabled is tool enabled or not - */ - setEnabled: function (enabled) { - this.state.enabled = enabled; + } else { + // Don't include timeseries at all + return null; + } + } - // Set control visibility by updating it's config. - this.controlConfig.showControl = enabled; + /** + * Stop tool. + * @method stop + * @public + */ + stop () { + if (this.controlConfig) { + this.controlConfig = null; this._updateTimeseriesPluginConfig(); - }, - /** - * Is this tool disabled. - * @method isDisabled - * @public - * - * @returns {Boolean} is tool disabled - */ - isDisabled: function (data) { - const service = this._getTimeseriesService(); - return typeof service === 'undefined' || typeof service.getActiveTimeseries() === 'undefined'; - }, - /** - * Don't show the tool if this code is loaded BUT the timeseries bundle is not started as part of the appsetup - */ - isDisplayed: function () { - return typeof this._getTimeseriesService() !== 'undefined'; - }, - /** - * Get values. - * @method getValues - * @public - * - * @returns {Object} tool value object - */ - getValues: function () { - if (this.state.enabled) { - return { - configuration: { - timeseries: { - conf: { - plugins: [{ id: this.getTool().id, config: this.controlConfig }] - }, - state: this.getSandbox().getStatefulComponents().timeseries.getState() - } - } - }; - } else { - // Don't include timeseries at all - return null; - } - }, - /** - * Stop tool. - * @method stop - * @public - */ - stop: function () { - var me = this; - if (me.controlConfig) { - me.controlConfig = null; - me._updateTimeseriesPluginConfig(); - } } - }, { - 'extend': ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], - 'protocol': ['Oskari.mapframework.publisher.Tool'] + } + + /** + * Sends configuration request to timeseries module + * + * @method _updateTimeseriesPluginConfig + * @private + */ + _updateTimeseriesPluginConfig () { + const requestBuilder = Oskari.requestBuilder('Timeseries.ConfigurationRequest'); + this.__sandbox.request('Publisher2', requestBuilder(this.controlConfig)); + } + + _getTimeseriesService () { + if (!this.service) { + this.service = this.__sandbox.getService('Oskari.mapframework.bundle.timeseries.TimeseriesService'); + } + return this.service; + } +} + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.TimeSeriesTool', + TimeSeriesTool, + { + protocol: ['Oskari.mapframework.publisher.Tool'] } ); + +export { TimeSeriesTool }; diff --git a/bundles/framework/timeseries/resources/locale/en.js b/bundles/framework/timeseries/resources/locale/en.js index 1108e94273..ba9890b479 100755 --- a/bundles/framework/timeseries/resources/locale/en.js +++ b/bundles/framework/timeseries/resources/locale/en.js @@ -38,6 +38,11 @@ Oskari.registerLocalization( "helpMsg_range": 'In this mode you can select a time range for the data. Note! The latest available image for the selected range is shown on any given location. You can switch to single year mode by clicking the near by icon:', "switchToRange": "Switch to time range mode", "switchToYear": "Switch to single year mode" + }, + "publisher": { + "TimeseriesControlPlugin": { + "toolLabel": "Time series player" + } } } }); diff --git a/bundles/framework/timeseries/resources/locale/fi.js b/bundles/framework/timeseries/resources/locale/fi.js index c1136201c7..3755922e44 100755 --- a/bundles/framework/timeseries/resources/locale/fi.js +++ b/bundles/framework/timeseries/resources/locale/fi.js @@ -48,6 +48,11 @@ Oskari.registerLocalization( "helpMsg_range": 'Tässä moodissa pääset tarkastelemaan valitun aikavälin sisällä tuotettua aineistoa. Huom! Näytettävä kuva on valitusta aikavälistä viimeisin saatavilla oleva. Jos haluat nähdä vain yksittäisen vuoden aineistoa vaihda moodia viereisestä ikonista:', "switchToRange": "Vaihda aikavälimoodiin", "switchToYear": "Vaihda yksittäisen vuoden valintamoodiin" + }, + "publisher": { + "TimeseriesControlPlugin": { + "toolLabel": "Aikasarjatoistin" + } } } }); diff --git a/bundles/framework/timeseries/resources/locale/fr.js b/bundles/framework/timeseries/resources/locale/fr.js index 5e872b1a10..637aa26f28 100644 --- a/bundles/framework/timeseries/resources/locale/fr.js +++ b/bundles/framework/timeseries/resources/locale/fr.js @@ -31,6 +31,11 @@ Oskari.registerLocalization( "layerFilter": { "tooltip": "Afficher uniquement les couches avec série chronologique", "timeseries": "Série chronologique" + }, + "publisher": { + "TimeseriesControlPlugin": { + "toolLabel": "Lecteur chronologique" + } } } }); diff --git a/bundles/framework/timeseries/resources/locale/ru.js b/bundles/framework/timeseries/resources/locale/ru.js index 5bd40f1f24..f8448367dc 100644 --- a/bundles/framework/timeseries/resources/locale/ru.js +++ b/bundles/framework/timeseries/resources/locale/ru.js @@ -31,6 +31,11 @@ Oskari.registerLocalization( "layerFilter": { "tooltip": "Показывать только слои с временными рядами", "timeseries": "Времянные ряды" + }, + "publisher": { + "TimeseriesControlPlugin": { + "toolLabel": "Временные ряды" + } } } }); diff --git a/bundles/framework/timeseries/resources/locale/sv.js b/bundles/framework/timeseries/resources/locale/sv.js index 419118a5ce..06737b3a14 100644 --- a/bundles/framework/timeseries/resources/locale/sv.js +++ b/bundles/framework/timeseries/resources/locale/sv.js @@ -48,6 +48,11 @@ Oskari.registerLocalization( "helpMsg_range": 'Med denna tidsseriefunktion kan du granska bilder från flera år. Obs! I kartvyn visas den sista flygbilden för varje område. Du kan returnera till tidspunktfunktionen med att klicka på ikonen i övre höga hörnet av panelen:', "switchToRange": "Byt till tidsseriefunktion", "switchToYear": "Byt till tidspunktfunktion" + }, + "publisher": { + "TimeseriesControlPlugin": { + "toolLabel": "Tidseriespelare" + } } } }); From c9fabad26325aa33f5935692653f0d661e38891c Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Thu, 21 Nov 2024 16:35:39 +0200 Subject: [PATCH 162/181] tool order --- .../admin/admin-announcements/publisher/AnnouncementsTool.js | 2 +- bundles/framework/coordinatetool/publisher/CoordinateTool.js | 2 +- bundles/framework/featuredata/publisher/FeaturedataTool.js | 2 +- bundles/framework/timeseries/publisher/TimeseriesTool.js | 2 +- bundles/mapping/layerswipe/publisher/SwipeTool.js | 2 +- bundles/mapping/mapmodule/publisher/controls/ControlsTool.js | 2 +- bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js | 2 +- bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js | 2 +- .../mapping/mapmodule/publisher/mylocation/MyLocationTool.js | 2 +- .../mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js | 2 +- bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js | 2 +- bundles/mapping/mapmodule/publisher/search/SearchTool.js | 2 +- bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js | 2 +- bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js | 2 +- bundles/mapping/maprotator/publisher/MapRotator.js | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js index b7626b8e98..fa70bbf0a0 100644 --- a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js +++ b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js @@ -6,7 +6,7 @@ import { AnnouncementsToolHandler } from './AnnouncementsToolHandler'; class AnnouncementsTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 160; this.group = 'additional'; this.handler = new AnnouncementsToolHandler(this); diff --git a/bundles/framework/coordinatetool/publisher/CoordinateTool.js b/bundles/framework/coordinatetool/publisher/CoordinateTool.js index d92d99695e..3b80d4abb9 100755 --- a/bundles/framework/coordinatetool/publisher/CoordinateTool.js +++ b/bundles/framework/coordinatetool/publisher/CoordinateTool.js @@ -4,7 +4,7 @@ import { CoordinateToolHandler } from './CoordinateToolHandler'; class CoordinateTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 120; this.group = 'additional'; this.bundleName = 'coordinatetool'; this.handler = new CoordinateToolHandler(this); diff --git a/bundles/framework/featuredata/publisher/FeaturedataTool.js b/bundles/framework/featuredata/publisher/FeaturedataTool.js index 3a7b1ba973..d76671d7c4 100644 --- a/bundles/framework/featuredata/publisher/FeaturedataTool.js +++ b/bundles/framework/featuredata/publisher/FeaturedataTool.js @@ -3,7 +3,7 @@ import { AbstractPublisherTool } from '../../publisher2/tools/AbstractPublisherT class FeaturedataTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 130; this.group = 'additional'; this.bundleName = 'featuredata'; } diff --git a/bundles/framework/timeseries/publisher/TimeseriesTool.js b/bundles/framework/timeseries/publisher/TimeseriesTool.js index b2b380571b..c2c90ba134 100644 --- a/bundles/framework/timeseries/publisher/TimeseriesTool.js +++ b/bundles/framework/timeseries/publisher/TimeseriesTool.js @@ -3,7 +3,7 @@ import { AbstractPublisherTool } from '../../publisher2/tools/AbstractPublisherT class TimeSeriesTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 150; this.group = 'additional'; this.allowedLocations = ['top center']; this.lefthanded = 'top center'; diff --git a/bundles/mapping/layerswipe/publisher/SwipeTool.js b/bundles/mapping/layerswipe/publisher/SwipeTool.js index dfb73dd58e..24d54ced02 100644 --- a/bundles/mapping/layerswipe/publisher/SwipeTool.js +++ b/bundles/mapping/layerswipe/publisher/SwipeTool.js @@ -6,7 +6,7 @@ export const SWIPE_ID = 'Oskari.mapframework.bundle.layerswipe.plugin.LayerSwipe class SwipeTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 5; + this.index = 40; this.group = 'additional'; } diff --git a/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js b/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js index 484d194f1c..2d0cbdf8cf 100644 --- a/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js +++ b/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js @@ -2,7 +2,7 @@ import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/Ab class ControlsTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 90; this.group = 'additional'; } diff --git a/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js index 484ae1f079..5120fc7d9c 100644 --- a/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js +++ b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js @@ -3,7 +3,7 @@ import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/Ab class CrosshairTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 110; this.group = 'additional'; } diff --git a/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js b/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js index a677687bc5..a1bdce1050 100644 --- a/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js +++ b/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js @@ -3,7 +3,7 @@ import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/Ab class IndexMapTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 30; this.group = 'additional'; } diff --git a/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js index 23f101bdbf..975e9aced5 100644 --- a/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js +++ b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js @@ -5,7 +5,7 @@ import { MyLocationToolHandler } from './MyLocationHandler'; class MyLocationTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 100; this.group = 'additional'; this.handler = new MyLocationToolHandler(this); }; diff --git a/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js index 9a4b1e1d26..36f70bbbc3 100644 --- a/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js +++ b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js @@ -4,7 +4,7 @@ import { PanButtonsHandler } from './PanButtonsHandler'; class PanButtonsTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 70; this.group = 'additional'; this.handler = new PanButtonsHandler(this); }; diff --git a/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js b/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js index a390c44623..ec3a3ba777 100644 --- a/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js +++ b/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js @@ -4,7 +4,7 @@ const SCALEBAR_TOOL_ID = 'Oskari.mapframework.bundle.mapmodule.plugin.ScaleBarPl class ScaleBarTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 60; this.group = 'additional'; } diff --git a/bundles/mapping/mapmodule/publisher/search/SearchTool.js b/bundles/mapping/mapmodule/publisher/search/SearchTool.js index 89db673e58..d47a20fba6 100644 --- a/bundles/mapping/mapmodule/publisher/search/SearchTool.js +++ b/bundles/mapping/mapmodule/publisher/search/SearchTool.js @@ -2,7 +2,7 @@ import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/Ab class SearchTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 10; this.group = 'additional'; } diff --git a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js index 3b4f5f271a..f68e38c40b 100644 --- a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js +++ b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js @@ -4,7 +4,7 @@ import { ToolbarToolHandler } from './ToolbarToolHandler'; class ToolbarTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 20; this.group = 'additional'; this.handler = new ToolbarToolHandler(this); } diff --git a/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js b/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js index 7ab0e3fbbc..b02b618921 100644 --- a/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js +++ b/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js @@ -3,7 +3,7 @@ import { AbstractPublisherTool } from '../../../../framework/publisher2/tools/Ab class ZoombarTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 50; this.group = 'additional'; }; diff --git a/bundles/mapping/maprotator/publisher/MapRotator.js b/bundles/mapping/maprotator/publisher/MapRotator.js index b127cbcca7..d86d0927d8 100755 --- a/bundles/mapping/maprotator/publisher/MapRotator.js +++ b/bundles/mapping/maprotator/publisher/MapRotator.js @@ -5,7 +5,7 @@ import { MapRotatorToolComponent } from './MapRotatorToolComponent'; class MapRotatorTool extends AbstractPublisherTool { constructor (...args) { super(...args); - this.index = 1; + this.index = 80; this.bundleName = 'maprotator'; this.group = 'additional'; this.handler = new MapRotatorToolHandler(this); From 57bd81ad147c532df699aa617c88bfc586e8bdd6 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Fri, 22 Nov 2024 10:51:27 +0200 Subject: [PATCH 163/181] rename panel 'additional' -> 'reactTools' --- .../admin/admin-announcements/publisher/AnnouncementsTool.js | 2 +- bundles/framework/coordinatetool/publisher/CoordinateTool.js | 2 +- bundles/framework/featuredata/publisher/FeaturedataTool.js | 2 +- bundles/framework/publisher2/resources/locale/en.js | 2 +- bundles/framework/publisher2/resources/locale/fi.js | 2 +- bundles/framework/publisher2/resources/locale/sv.js | 2 +- bundles/framework/publisher2/view/PublisherSidebar.js | 2 +- bundles/framework/timeseries/publisher/TimeseriesTool.js | 2 +- bundles/mapping/layerswipe/publisher/SwipeTool.js | 2 +- bundles/mapping/mapmodule/publisher/controls/ControlsTool.js | 2 +- bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js | 2 +- bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js | 2 +- bundles/mapping/mapmodule/publisher/logo/LogoTool.js | 2 +- .../mapping/mapmodule/publisher/mylocation/MyLocationTool.js | 2 +- .../mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js | 2 +- bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js | 2 +- bundles/mapping/mapmodule/publisher/search/SearchTool.js | 2 +- bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js | 2 +- bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js | 2 +- bundles/mapping/maprotator/publisher/MapRotator.js | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js index fa70bbf0a0..4ff94b3518 100644 --- a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js +++ b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js @@ -7,7 +7,7 @@ class AnnouncementsTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 160; - this.group = 'additional'; + this.group = 'reactTools'; this.handler = new AnnouncementsToolHandler(this); this.sandbox = Oskari.getSandbox(); diff --git a/bundles/framework/coordinatetool/publisher/CoordinateTool.js b/bundles/framework/coordinatetool/publisher/CoordinateTool.js index 3b80d4abb9..778b5be054 100755 --- a/bundles/framework/coordinatetool/publisher/CoordinateTool.js +++ b/bundles/framework/coordinatetool/publisher/CoordinateTool.js @@ -5,7 +5,7 @@ class CoordinateTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 120; - this.group = 'additional'; + this.group = 'reactTools'; this.bundleName = 'coordinatetool'; this.handler = new CoordinateToolHandler(this); } diff --git a/bundles/framework/featuredata/publisher/FeaturedataTool.js b/bundles/framework/featuredata/publisher/FeaturedataTool.js index d76671d7c4..7ba7539a58 100644 --- a/bundles/framework/featuredata/publisher/FeaturedataTool.js +++ b/bundles/framework/featuredata/publisher/FeaturedataTool.js @@ -4,7 +4,7 @@ class FeaturedataTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 130; - this.group = 'additional'; + this.group = 'reactTools'; this.bundleName = 'featuredata'; } diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 32e9efca4a..7c88067444 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -233,7 +233,7 @@ Oskari.registerLocalization( "noBaseLayers": "No selected background maps", "noLayers": "No selected map layers" }, - "additional": { + "reactTools": { "label": "Additional tools" }, "preview": "Map preview", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 82b050409b..ff067aae3d 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -233,7 +233,7 @@ Oskari.registerLocalization( "noBaseLayers": "Ei valittuja taustakarttoja", "noLayers": "Ei valittuja karttatasoja" }, - "additional": { + "reactTools": { "label": "Lisätyökalut" }, "preview": "Kartan esikatselu", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index cc84aff1ff..4f7353d9a7 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -233,7 +233,7 @@ Oskari.registerLocalization( "noBaseLayers": "Inga bakgrundskartor valda", "noLayers": "Inga kartlager valda" }, - "additional": { + "reactTools": { "label": "Ytterligare verktyg" }, "preview": "Den publicerade kartans förhandsgranskningsvy", diff --git a/bundles/framework/publisher2/view/PublisherSidebar.js b/bundles/framework/publisher2/view/PublisherSidebar.js index 7276309039..136f452d86 100755 --- a/bundles/framework/publisher2/view/PublisherSidebar.js +++ b/bundles/framework/publisher2/view/PublisherSidebar.js @@ -116,7 +116,7 @@ Oskari.clazz.define('Oskari.mapframework.bundle.publisher2.view.PublisherSidebar this.panels.push(mapLayersPanel); accordion.addPanel(mapLayersPanel.getPanel()); // separate tools that support react from ones that don't - const reactGroups = ['additional', 'data', 'statsgrid']; + const reactGroups = ['reactTools', 'data', 'statsgrid']; const reactGroupsTools = {}; // create panel for each tool group Object.keys(publisherTools.groups).forEach(group => { diff --git a/bundles/framework/timeseries/publisher/TimeseriesTool.js b/bundles/framework/timeseries/publisher/TimeseriesTool.js index c2c90ba134..5cb51c17aa 100644 --- a/bundles/framework/timeseries/publisher/TimeseriesTool.js +++ b/bundles/framework/timeseries/publisher/TimeseriesTool.js @@ -4,7 +4,7 @@ class TimeSeriesTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 150; - this.group = 'additional'; + this.group = 'reactTools'; this.allowedLocations = ['top center']; this.lefthanded = 'top center'; this.righthanded = 'top center'; diff --git a/bundles/mapping/layerswipe/publisher/SwipeTool.js b/bundles/mapping/layerswipe/publisher/SwipeTool.js index 24d54ced02..a684817411 100644 --- a/bundles/mapping/layerswipe/publisher/SwipeTool.js +++ b/bundles/mapping/layerswipe/publisher/SwipeTool.js @@ -7,7 +7,7 @@ class SwipeTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 40; - this.group = 'additional'; + this.group = 'reactTools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js b/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js index 2d0cbdf8cf..0674df31e1 100644 --- a/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js +++ b/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js @@ -3,7 +3,7 @@ class ControlsTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 90; - this.group = 'additional'; + this.group = 'reactTools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js index 5120fc7d9c..165cf980ef 100644 --- a/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js +++ b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js @@ -4,7 +4,7 @@ class CrosshairTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 110; - this.group = 'additional'; + this.group = 'reactTools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js b/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js index a1bdce1050..15a02a724c 100644 --- a/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js +++ b/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js @@ -4,7 +4,7 @@ class IndexMapTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 30; - this.group = 'additional'; + this.group = 'reactTools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/logo/LogoTool.js b/bundles/mapping/mapmodule/publisher/logo/LogoTool.js index 11cdb63f71..cd8f190200 100644 --- a/bundles/mapping/mapmodule/publisher/logo/LogoTool.js +++ b/bundles/mapping/mapmodule/publisher/logo/LogoTool.js @@ -4,7 +4,7 @@ class LogoTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 1; - this.group = 'additional'; + this.group = 'reactTools'; this.config = null; } diff --git a/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js index 975e9aced5..3553e18232 100644 --- a/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js +++ b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js @@ -6,7 +6,7 @@ class MyLocationTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 100; - this.group = 'additional'; + this.group = 'reactTools'; this.handler = new MyLocationToolHandler(this); }; diff --git a/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js index 36f70bbbc3..3b0a3a3c6c 100644 --- a/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js +++ b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js @@ -5,7 +5,7 @@ class PanButtonsTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 70; - this.group = 'additional'; + this.group = 'reactTools'; this.handler = new PanButtonsHandler(this); }; diff --git a/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js b/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js index ec3a3ba777..d64da4e2f1 100644 --- a/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js +++ b/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js @@ -5,7 +5,7 @@ class ScaleBarTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 60; - this.group = 'additional'; + this.group = 'reactTools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/search/SearchTool.js b/bundles/mapping/mapmodule/publisher/search/SearchTool.js index d47a20fba6..5762b235f4 100644 --- a/bundles/mapping/mapmodule/publisher/search/SearchTool.js +++ b/bundles/mapping/mapmodule/publisher/search/SearchTool.js @@ -3,7 +3,7 @@ class SearchTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 10; - this.group = 'additional'; + this.group = 'reactTools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js index f68e38c40b..af9b64df7b 100644 --- a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js +++ b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js @@ -5,7 +5,7 @@ class ToolbarTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 20; - this.group = 'additional'; + this.group = 'reactTools'; this.handler = new ToolbarToolHandler(this); } diff --git a/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js b/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js index b02b618921..0f6a3b1881 100644 --- a/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js +++ b/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js @@ -4,7 +4,7 @@ class ZoombarTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 50; - this.group = 'additional'; + this.group = 'reactTools'; }; /** diff --git a/bundles/mapping/maprotator/publisher/MapRotator.js b/bundles/mapping/maprotator/publisher/MapRotator.js index d86d0927d8..8b3519f103 100755 --- a/bundles/mapping/maprotator/publisher/MapRotator.js +++ b/bundles/mapping/maprotator/publisher/MapRotator.js @@ -7,7 +7,7 @@ class MapRotatorTool extends AbstractPublisherTool { super(...args); this.index = 80; this.bundleName = 'maprotator'; - this.group = 'additional'; + this.group = 'reactTools'; this.handler = new MapRotatorToolHandler(this); } From 685639b0741a7a4eafc517ac949473634e6063d9 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Fri, 22 Nov 2024 12:50:27 +0200 Subject: [PATCH 164/181] swap title maptools <-> reacttools --- bundles/framework/publisher2/resources/locale/en.js | 4 ++-- bundles/framework/publisher2/resources/locale/fi.js | 4 ++-- bundles/framework/publisher2/resources/locale/sv.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 7c88067444..64fc5a6f37 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -62,7 +62,7 @@ Oskari.registerLocalization( "tooltip": "Determine the size of the map. The recommendation is to use the option \"Scalable/ Responsive\". Otherwise, the width should be from {minWidth} to {maxWidth} pixels and the height from {minHeight} to {maxHeight} pixels." }, "maptools": { - "label": "Tools", + "label": "Additional tools", "tooltip": "Select available map tools. Check a placement in the map preview.", "FeaturedataPlugin": "Feature data", "GetInfoPlugin": "Feature query tool", @@ -234,7 +234,7 @@ Oskari.registerLocalization( "noLayers": "No selected map layers" }, "reactTools": { - "label": "Additional tools" + "label": "Tools" }, "preview": "Map preview", "location": "Location and zoom level", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index ff067aae3d..5ef9a069e4 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -62,7 +62,7 @@ Oskari.registerLocalization( "tooltip": "Valitse kartan koko. Suositus on käyttää skaalautuvaa vaihtoehtoa \"Skaalautuva / tilan täyttävä\". Muussa tapauksessa leveyden on oltava välillä {minWidth} ja {maxWidth} pikseliä ja korkeuden {minHeight} ja {maxHeight} pikseliä." }, "maptools": { - "label": "Kartalla näytettävät työkalut", + "label": "Lisätyökalut", "tooltip": "Valitse kartalla käytettävissä olevat työkalut. Tarkista asettelu esikatselukartasta.", "FeaturedataPlugin": "Kohdetietotaulukko", "GetInfoPlugin": "Kohdetietojen kyselytyökalu", @@ -234,7 +234,7 @@ Oskari.registerLocalization( "noLayers": "Ei valittuja karttatasoja" }, "reactTools": { - "label": "Lisätyökalut" + "label": "Kartalla näytettävät työkalut" }, "preview": "Kartan esikatselu", "location": "Sijainti ja mittakaavataso", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 4f7353d9a7..e4ec8f359b 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -62,7 +62,7 @@ Oskari.registerLocalization( "tooltip": "Välj kartans storlek. Vi rekommenderar den skalbara funktionen \"Skalbar / Fyll utrymmet\". Annars skall bredden vara minst {minWidth} och högst {maxWidth} pixel, och bredden mellan {minHeight} och {maxHeight} pixel." }, "maptools": { - "label": "Verktyg", + "label": "Ytterligare verktyg", "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.", "FeaturedataPlugin": "Objektuppgifter", "GetInfoPlugin": "Frågverktyg för visande av objektuppgifter", @@ -234,7 +234,7 @@ Oskari.registerLocalization( "noLayers": "Inga kartlager valda" }, "reactTools": { - "label": "Ytterligare verktyg" + "label": "Verktyg" }, "preview": "Den publicerade kartans förhandsgranskningsvy", "location": "Läge och skalnivå.", From d33c27c6c850212bbfc82f6f2835c41bd51ec83e Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Fri, 22 Nov 2024 15:27:12 +0200 Subject: [PATCH 165/181] move labels from publsiher to individual tools --- bundles/framework/coordinatetool/publisher/CoordinateTool.js | 2 +- bundles/framework/coordinatetool/resources/locale/en.js | 1 + bundles/framework/coordinatetool/resources/locale/fi.js | 1 + bundles/framework/coordinatetool/resources/locale/fr.js | 1 + bundles/framework/coordinatetool/resources/locale/is.js | 1 + bundles/framework/coordinatetool/resources/locale/ru.js | 1 + bundles/framework/coordinatetool/resources/locale/sv.js | 1 + bundles/framework/featuredata/publisher/FeaturedataTool.js | 2 +- bundles/framework/featuredata/resources/locale/en.js | 3 +++ bundles/framework/featuredata/resources/locale/fi.js | 3 +++ bundles/framework/featuredata/resources/locale/sv.js | 4 ++++ bundles/framework/publisher2/resources/locale/en.js | 2 -- bundles/framework/publisher2/resources/locale/fi.js | 2 -- bundles/framework/publisher2/resources/locale/fr.js | 1 - bundles/framework/publisher2/resources/locale/is.js | 1 - bundles/framework/publisher2/resources/locale/ru.js | 1 - bundles/framework/publisher2/resources/locale/sv.js | 2 -- 17 files changed, 18 insertions(+), 11 deletions(-) diff --git a/bundles/framework/coordinatetool/publisher/CoordinateTool.js b/bundles/framework/coordinatetool/publisher/CoordinateTool.js index 778b5be054..f0047b9588 100755 --- a/bundles/framework/coordinatetool/publisher/CoordinateTool.js +++ b/bundles/framework/coordinatetool/publisher/CoordinateTool.js @@ -42,7 +42,7 @@ class CoordinateTool extends AbstractPublisherTool { getTool () { return { id: 'Oskari.mapframework.bundle.coordinatetool.plugin.CoordinateToolPlugin', - title: Oskari.getMsg('Publisher2', 'BasicView.maptools.CoordinateToolPlugin'), + title: Oskari.getMsg('coordinatetool', 'display.publisher.toolLabel'), config: { ...(this.state.pluginConfig || {}) } diff --git a/bundles/framework/coordinatetool/resources/locale/en.js b/bundles/framework/coordinatetool/resources/locale/en.js index 168b78996b..23a34c42e2 100755 --- a/bundles/framework/coordinatetool/resources/locale/en.js +++ b/bundles/framework/coordinatetool/resources/locale/en.js @@ -99,6 +99,7 @@ Oskari.registerLocalization( "message": "Coordinates can not be transformed" }, "publisher": { + "toolLabel": "Coordinate tool", "showTransformationTools": "Show coordinate transformation tools", "noUI": "Hide user interface (Use RPC interface)" } diff --git a/bundles/framework/coordinatetool/resources/locale/fi.js b/bundles/framework/coordinatetool/resources/locale/fi.js index 2a24c5c4c6..8c46f00f62 100755 --- a/bundles/framework/coordinatetool/resources/locale/fi.js +++ b/bundles/framework/coordinatetool/resources/locale/fi.js @@ -99,6 +99,7 @@ Oskari.registerLocalization( "message": "Koordinaatteja ei saatu muunnettua" }, "publisher": { + "toolLabel": "Koordinaattityökalu", "showTransformationTools": "Näytä koordinaattimuunnostyökalut", "noUI": "Piilota käyttöliittymä (käytä RPC-rajapinnan kautta)" } diff --git a/bundles/framework/coordinatetool/resources/locale/fr.js b/bundles/framework/coordinatetool/resources/locale/fr.js index 688beafe9c..1c60419e5e 100644 --- a/bundles/framework/coordinatetool/resources/locale/fr.js +++ b/bundles/framework/coordinatetool/resources/locale/fr.js @@ -99,6 +99,7 @@ Oskari.registerLocalization( "message": "Impossible de transformer les coordonnées" }, "publisher": { + "toolLabel": "Coordonner l'outil", "showTransformationTools": "Afficher les outils de transformation des coordonnées", "noUI": "Masquer l'interface utilisateur (utiliser l'interface RPC)" } diff --git a/bundles/framework/coordinatetool/resources/locale/is.js b/bundles/framework/coordinatetool/resources/locale/is.js index a8148a2309..0682582c54 100755 --- a/bundles/framework/coordinatetool/resources/locale/is.js +++ b/bundles/framework/coordinatetool/resources/locale/is.js @@ -101,6 +101,7 @@ Oskari.registerLocalization( "message": "Ekki er hægt að breyta hnitkerfi" }, "publisher": { + "toolLabel": "Hnitatól", "showTransformationTools": "Sýna hnitabreytingatól", "noUI": "Fela notendaviðmót (Nota RPC viðmót)" } diff --git a/bundles/framework/coordinatetool/resources/locale/ru.js b/bundles/framework/coordinatetool/resources/locale/ru.js index 02ea09f6a0..0bf0f5ba41 100644 --- a/bundles/framework/coordinatetool/resources/locale/ru.js +++ b/bundles/framework/coordinatetool/resources/locale/ru.js @@ -99,6 +99,7 @@ Oskari.registerLocalization( "message": "Координаты не могут быть преобразованы" }, "publisher": { + "toolLabel": "Инструмент координат", "showTransformationTools": "Показать инструменты преобразования координат", "noUI": "Скрыть пользовательский интерфейс (использовать интерфейс RPC)" } diff --git a/bundles/framework/coordinatetool/resources/locale/sv.js b/bundles/framework/coordinatetool/resources/locale/sv.js index 54dfe1d3e2..524d08e5ec 100755 --- a/bundles/framework/coordinatetool/resources/locale/sv.js +++ b/bundles/framework/coordinatetool/resources/locale/sv.js @@ -99,6 +99,7 @@ Oskari.registerLocalization( "message": "Koordinaterna kunde inte omvandlas" }, "publisher": { + "toolLabel": "Koordinatverktyg", "showTransformationTools": "Visa verktyg för koordinattransformation", "noUI": "Dölj användargränsnittet (Använd via RPC gränssnitt)" } diff --git a/bundles/framework/featuredata/publisher/FeaturedataTool.js b/bundles/framework/featuredata/publisher/FeaturedataTool.js index 7ba7539a58..1da2d4fdaf 100644 --- a/bundles/framework/featuredata/publisher/FeaturedataTool.js +++ b/bundles/framework/featuredata/publisher/FeaturedataTool.js @@ -40,7 +40,7 @@ class FeaturedataTool extends AbstractPublisherTool { getTool () { return { id: 'Oskari.mapframework.bundle.featuredata.plugin.FeaturedataPlugin', - title: Oskari.getMsg('Publisher2', 'BasicView.maptools.FeaturedataPlugin'), + title: Oskari.getMsg('FeatureData', 'publisher.toolLabel'), config: this.state.pluginConfig || {} }; } diff --git a/bundles/framework/featuredata/resources/locale/en.js b/bundles/framework/featuredata/resources/locale/en.js index d69cc17990..b625d76167 100644 --- a/bundles/framework/featuredata/resources/locale/en.js +++ b/bundles/framework/featuredata/resources/locale/en.js @@ -101,5 +101,8 @@ Oskari.registerLocalization( } } }, + "publisher": { + "toolLabel": "Feature data" } + } }); diff --git a/bundles/framework/featuredata/resources/locale/fi.js b/bundles/framework/featuredata/resources/locale/fi.js index 6a7db53144..6bbc0864bf 100644 --- a/bundles/framework/featuredata/resources/locale/fi.js +++ b/bundles/framework/featuredata/resources/locale/fi.js @@ -100,6 +100,9 @@ Oskari.registerLocalization( "tooltip": "Piirrä geometria ja valitse sen avulla kohteet." } } + }, + "publisher": { + "toolLabel": "Kohdetietotaulukko" } } }); diff --git a/bundles/framework/featuredata/resources/locale/sv.js b/bundles/framework/featuredata/resources/locale/sv.js index 81fb96d7ea..2b56dfdc46 100644 --- a/bundles/framework/featuredata/resources/locale/sv.js +++ b/bundles/framework/featuredata/resources/locale/sv.js @@ -100,6 +100,10 @@ Oskari.registerLocalization( "tooltip": "Välj funktioner" } } + }, + "publisher": { + "toolLabel": "Objektuppgifter" } + } }); diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 64fc5a6f37..ae6ec4bedc 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -64,11 +64,9 @@ Oskari.registerLocalization( "maptools": { "label": "Additional tools", "tooltip": "Select available map tools. Check a placement in the map preview.", - "FeaturedataPlugin": "Feature data", "GetInfoPlugin": "Feature query tool", "selectDrawLayer": "Select map layer", "LayerSelectionPlugin": "Map layers menu", - "CoordinateToolPlugin": "Coordinate tool", "MapLegend": "Show map legend", "MapRotator": "Enable map rotation", "CameraControls3d": "Camera tools", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 5ef9a069e4..7569b4cb2d 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -64,11 +64,9 @@ Oskari.registerLocalization( "maptools": { "label": "Lisätyökalut", "tooltip": "Valitse kartalla käytettävissä olevat työkalut. Tarkista asettelu esikatselukartasta.", - "FeaturedataPlugin": "Kohdetietotaulukko", "GetInfoPlugin": "Kohdetietojen kyselytyökalu", "selectDrawLayer": "Valitse tallennustaso", "LayerSelectionPlugin": "Karttatasovalikko", - "CoordinateToolPlugin": "Koordinaattityökalu", "MapLegend": "Näytä karttaselitteet", "MapRotator": "Salli kartan pyörittäminen", "CameraControls3d": "Kameratyökalut", diff --git a/bundles/framework/publisher2/resources/locale/fr.js b/bundles/framework/publisher2/resources/locale/fr.js index cad5dc797b..f8d8433121 100644 --- a/bundles/framework/publisher2/resources/locale/fr.js +++ b/bundles/framework/publisher2/resources/locale/fr.js @@ -66,7 +66,6 @@ Oskari.registerLocalization( "GetInfoPlugin": "Outil d'interrogation de fonctionnalité", "selectDrawLayer": "Sélectionner la couche cartographique", "LayerSelectionPlugin": "Menu des couches cartographiques", - "CoordinateToolPlugin": "Coordonner l'outil", "FeedbackServiceTool": "Service de rétroaction (Open311)", "MapLegend": "Afficher la légende de la carte", "MapRotator": "Activer la rotation de la carte", diff --git a/bundles/framework/publisher2/resources/locale/is.js b/bundles/framework/publisher2/resources/locale/is.js index f98853d500..805a671d63 100755 --- a/bundles/framework/publisher2/resources/locale/is.js +++ b/bundles/framework/publisher2/resources/locale/is.js @@ -54,7 +54,6 @@ Oskari.registerLocalization( "GetInfoPlugin": "Fyrirspurnatól fyrir fitjur", "selectDrawLayer": "Velja kortalag", "LayerSelectionPlugin": "Valmynd fyrir kortalög", - "CoordinateToolPlugin": "Hnitatól", "FeedbackServiceTool": "", "toolbarToolNames": { "history_back": "Færa í fyrri sýn", diff --git a/bundles/framework/publisher2/resources/locale/ru.js b/bundles/framework/publisher2/resources/locale/ru.js index d3a411a3a0..8e8f575343 100644 --- a/bundles/framework/publisher2/resources/locale/ru.js +++ b/bundles/framework/publisher2/resources/locale/ru.js @@ -67,7 +67,6 @@ Oskari.registerLocalization( "GetInfoPlugin": "Инструмент запроса объектов", "selectDrawLayer": "Выбрать слой карты", "LayerSelectionPlugin": "Меню слоев карты", - "CoordinateToolPlugin": "Инструмент координат", "FeedbackServiceTool": "Обратная связь (Open311) ", "MapLegend": "Показать условные знаки", "MapRotator": "Включить поворот карты", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index e4ec8f359b..a32bba3e90 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -64,11 +64,9 @@ Oskari.registerLocalization( "maptools": { "label": "Ytterligare verktyg", "tooltip": "Välj verktygen som visas på kartan. Du kan se deras placering på den förhandsvisade kartan.", - "FeaturedataPlugin": "Objektuppgifter", "GetInfoPlugin": "Frågverktyg för visande av objektuppgifter", "selectDrawLayer": "Välj lager för nya funktioner", "LayerSelectionPlugin": "Kartlagermeny", - "CoordinateToolPlugin": "Koordinatverktyg", "MapLegend": "Visa kartförklaringen", "MapRotator": "Tillåt kartrotation", "CameraControls3d": "Kameraverktyg", From 0bb120e78454ede3b3c07f9e979a17dfd972d9e6 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 25 Nov 2024 12:16:20 +0200 Subject: [PATCH 166/181] rename reactTools -> tools --- .../admin/admin-announcements/publisher/AnnouncementsTool.js | 2 +- bundles/framework/coordinatetool/publisher/CoordinateTool.js | 2 +- bundles/framework/featuredata/publisher/FeaturedataTool.js | 2 +- bundles/framework/publisher2/resources/locale/en.js | 2 +- bundles/framework/publisher2/resources/locale/fi.js | 2 +- bundles/framework/publisher2/resources/locale/sv.js | 2 +- bundles/framework/publisher2/view/PublisherSidebar.js | 2 +- bundles/framework/timeseries/publisher/TimeseriesTool.js | 2 +- bundles/mapping/layerswipe/publisher/SwipeTool.js | 2 +- bundles/mapping/mapmodule/publisher/controls/ControlsTool.js | 2 +- bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js | 2 +- bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js | 2 +- bundles/mapping/mapmodule/publisher/logo/LogoTool.js | 2 +- .../mapping/mapmodule/publisher/mylocation/MyLocationTool.js | 2 +- .../mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js | 2 +- bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js | 2 +- bundles/mapping/mapmodule/publisher/search/SearchTool.js | 2 +- bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js | 2 +- bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js | 2 +- bundles/mapping/maprotator/publisher/MapRotator.js | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js index 4ff94b3518..0a7a6f345c 100644 --- a/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js +++ b/bundles/admin/admin-announcements/publisher/AnnouncementsTool.js @@ -7,7 +7,7 @@ class AnnouncementsTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 160; - this.group = 'reactTools'; + this.group = 'tools'; this.handler = new AnnouncementsToolHandler(this); this.sandbox = Oskari.getSandbox(); diff --git a/bundles/framework/coordinatetool/publisher/CoordinateTool.js b/bundles/framework/coordinatetool/publisher/CoordinateTool.js index f0047b9588..95180ece2c 100755 --- a/bundles/framework/coordinatetool/publisher/CoordinateTool.js +++ b/bundles/framework/coordinatetool/publisher/CoordinateTool.js @@ -5,7 +5,7 @@ class CoordinateTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 120; - this.group = 'reactTools'; + this.group = 'tools'; this.bundleName = 'coordinatetool'; this.handler = new CoordinateToolHandler(this); } diff --git a/bundles/framework/featuredata/publisher/FeaturedataTool.js b/bundles/framework/featuredata/publisher/FeaturedataTool.js index 1da2d4fdaf..d06c8564d3 100644 --- a/bundles/framework/featuredata/publisher/FeaturedataTool.js +++ b/bundles/framework/featuredata/publisher/FeaturedataTool.js @@ -4,7 +4,7 @@ class FeaturedataTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 130; - this.group = 'reactTools'; + this.group = 'tools'; this.bundleName = 'featuredata'; } diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index ae6ec4bedc..7f33d860cf 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -231,7 +231,7 @@ Oskari.registerLocalization( "noBaseLayers": "No selected background maps", "noLayers": "No selected map layers" }, - "reactTools": { + "tools": { "label": "Tools" }, "preview": "Map preview", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 7569b4cb2d..03c12dc07a 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -231,7 +231,7 @@ Oskari.registerLocalization( "noBaseLayers": "Ei valittuja taustakarttoja", "noLayers": "Ei valittuja karttatasoja" }, - "reactTools": { + "tools": { "label": "Kartalla näytettävät työkalut" }, "preview": "Kartan esikatselu", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index a32bba3e90..a90c4ce71b 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -231,7 +231,7 @@ Oskari.registerLocalization( "noBaseLayers": "Inga bakgrundskartor valda", "noLayers": "Inga kartlager valda" }, - "reactTools": { + "tools": { "label": "Verktyg" }, "preview": "Den publicerade kartans förhandsgranskningsvy", diff --git a/bundles/framework/publisher2/view/PublisherSidebar.js b/bundles/framework/publisher2/view/PublisherSidebar.js index 136f452d86..bdad775ac9 100755 --- a/bundles/framework/publisher2/view/PublisherSidebar.js +++ b/bundles/framework/publisher2/view/PublisherSidebar.js @@ -116,7 +116,7 @@ Oskari.clazz.define('Oskari.mapframework.bundle.publisher2.view.PublisherSidebar this.panels.push(mapLayersPanel); accordion.addPanel(mapLayersPanel.getPanel()); // separate tools that support react from ones that don't - const reactGroups = ['reactTools', 'data', 'statsgrid']; + const reactGroups = ['tools', 'data', 'statsgrid']; const reactGroupsTools = {}; // create panel for each tool group Object.keys(publisherTools.groups).forEach(group => { diff --git a/bundles/framework/timeseries/publisher/TimeseriesTool.js b/bundles/framework/timeseries/publisher/TimeseriesTool.js index 5cb51c17aa..9db021e9c6 100644 --- a/bundles/framework/timeseries/publisher/TimeseriesTool.js +++ b/bundles/framework/timeseries/publisher/TimeseriesTool.js @@ -4,7 +4,7 @@ class TimeSeriesTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 150; - this.group = 'reactTools'; + this.group = 'tools'; this.allowedLocations = ['top center']; this.lefthanded = 'top center'; this.righthanded = 'top center'; diff --git a/bundles/mapping/layerswipe/publisher/SwipeTool.js b/bundles/mapping/layerswipe/publisher/SwipeTool.js index a684817411..27fbadcbf5 100644 --- a/bundles/mapping/layerswipe/publisher/SwipeTool.js +++ b/bundles/mapping/layerswipe/publisher/SwipeTool.js @@ -7,7 +7,7 @@ class SwipeTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 40; - this.group = 'reactTools'; + this.group = 'tools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js b/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js index 0674df31e1..12607d3edd 100644 --- a/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js +++ b/bundles/mapping/mapmodule/publisher/controls/ControlsTool.js @@ -3,7 +3,7 @@ class ControlsTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 90; - this.group = 'reactTools'; + this.group = 'tools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js index 165cf980ef..6d82feee10 100644 --- a/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js +++ b/bundles/mapping/mapmodule/publisher/crosshair/CrosshairTool.js @@ -4,7 +4,7 @@ class CrosshairTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 110; - this.group = 'reactTools'; + this.group = 'tools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js b/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js index 15a02a724c..1bfbea9fde 100644 --- a/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js +++ b/bundles/mapping/mapmodule/publisher/indexmap/IndexMapTool.js @@ -4,7 +4,7 @@ class IndexMapTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 30; - this.group = 'reactTools'; + this.group = 'tools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/logo/LogoTool.js b/bundles/mapping/mapmodule/publisher/logo/LogoTool.js index cd8f190200..81798891bd 100644 --- a/bundles/mapping/mapmodule/publisher/logo/LogoTool.js +++ b/bundles/mapping/mapmodule/publisher/logo/LogoTool.js @@ -4,7 +4,7 @@ class LogoTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 1; - this.group = 'reactTools'; + this.group = 'tools'; this.config = null; } diff --git a/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js index 3553e18232..b51d404126 100644 --- a/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js +++ b/bundles/mapping/mapmodule/publisher/mylocation/MyLocationTool.js @@ -6,7 +6,7 @@ class MyLocationTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 100; - this.group = 'reactTools'; + this.group = 'tools'; this.handler = new MyLocationToolHandler(this); }; diff --git a/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js index 3b0a3a3c6c..debfcb8d38 100644 --- a/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js +++ b/bundles/mapping/mapmodule/publisher/panbuttons/PanButtonsTool.js @@ -5,7 +5,7 @@ class PanButtonsTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 70; - this.group = 'reactTools'; + this.group = 'tools'; this.handler = new PanButtonsHandler(this); }; diff --git a/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js b/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js index d64da4e2f1..8ad7a08ef4 100644 --- a/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js +++ b/bundles/mapping/mapmodule/publisher/scalebar/ScalebarTool.js @@ -5,7 +5,7 @@ class ScaleBarTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 60; - this.group = 'reactTools'; + this.group = 'tools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/search/SearchTool.js b/bundles/mapping/mapmodule/publisher/search/SearchTool.js index 5762b235f4..5e27cfa2f5 100644 --- a/bundles/mapping/mapmodule/publisher/search/SearchTool.js +++ b/bundles/mapping/mapmodule/publisher/search/SearchTool.js @@ -3,7 +3,7 @@ class SearchTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 10; - this.group = 'reactTools'; + this.group = 'tools'; } getTool () { diff --git a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js index af9b64df7b..d819ea7b1e 100644 --- a/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js +++ b/bundles/mapping/mapmodule/publisher/toolbar/ToolbarTool.js @@ -5,7 +5,7 @@ class ToolbarTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 20; - this.group = 'reactTools'; + this.group = 'tools'; this.handler = new ToolbarToolHandler(this); } diff --git a/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js b/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js index 0f6a3b1881..02f1fbacdc 100644 --- a/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js +++ b/bundles/mapping/mapmodule/publisher/zoombar/ZoombarTool.js @@ -4,7 +4,7 @@ class ZoombarTool extends AbstractPublisherTool { constructor (...args) { super(...args); this.index = 50; - this.group = 'reactTools'; + this.group = 'tools'; }; /** diff --git a/bundles/mapping/maprotator/publisher/MapRotator.js b/bundles/mapping/maprotator/publisher/MapRotator.js index 8b3519f103..9aea7e5575 100755 --- a/bundles/mapping/maprotator/publisher/MapRotator.js +++ b/bundles/mapping/maprotator/publisher/MapRotator.js @@ -7,7 +7,7 @@ class MapRotatorTool extends AbstractPublisherTool { super(...args); this.index = 80; this.bundleName = 'maprotator'; - this.group = 'reactTools'; + this.group = 'tools'; this.handler = new MapRotatorToolHandler(this); } From 2dd1e664682d36f607089b3f535d68826ff24b56 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 25 Nov 2024 15:24:18 +0200 Subject: [PATCH 167/181] camera tools reactification --- .../publisher2/resources/locale/en.js | 1 - .../publisher2/resources/locale/fi.js | 1 - .../publisher2/resources/locale/sv.js | 1 - .../camera-controls-3d/resources/locale/en.js | 6 +- .../camera-controls-3d/resources/locale/fi.js | 7 +- .../camera-controls-3d/resources/locale/sv.js | 6 +- .../tool/CameraControls3dTool.js | 118 ++++++++---------- 7 files changed, 67 insertions(+), 73 deletions(-) diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index 7f33d860cf..bf8924c507 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -69,7 +69,6 @@ Oskari.registerLocalization( "LayerSelectionPlugin": "Map layers menu", "MapLegend": "Show map legend", "MapRotator": "Enable map rotation", - "CameraControls3d": "Camera tools", "TimeControl3d": "Time control", "toolbarToolNames": { "history": "Move to previous or next view", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index 03c12dc07a..d6e904b2b7 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -69,7 +69,6 @@ Oskari.registerLocalization( "LayerSelectionPlugin": "Karttatasovalikko", "MapLegend": "Näytä karttaselitteet", "MapRotator": "Salli kartan pyörittäminen", - "CameraControls3d": "Kameratyökalut", "TimeControl3d": "Ajanhetken säädin", "toolbarToolNames": { "history": "Siirtyminen edelliseen ja seuraavaan näkymään", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index a90c4ce71b..51d254c153 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -69,7 +69,6 @@ Oskari.registerLocalization( "LayerSelectionPlugin": "Kartlagermeny", "MapLegend": "Visa kartförklaringen", "MapRotator": "Tillåt kartrotation", - "CameraControls3d": "Kameraverktyg", "TimeControl3d": "Tidskontroll", "toolbarToolNames": { "history": "Gå bakåt eller framåt", diff --git a/bundles/mapping/camera-controls-3d/resources/locale/en.js b/bundles/mapping/camera-controls-3d/resources/locale/en.js index cec553f64c..b4103099ba 100644 --- a/bundles/mapping/camera-controls-3d/resources/locale/en.js +++ b/bundles/mapping/camera-controls-3d/resources/locale/en.js @@ -11,13 +11,15 @@ Oskari.registerLocalization( }, "rotateModeFailure": { "message" : "Setting to rotate map mode failed", - "description": "Camera should be pointed towards earth surface when mode is set" + "description": "Camera should be pointed towards earth surface when mode is set" }, 'guidedTour': { 'title': '3D camera tools', 'message': 'In 3D view, the map can be rotated and camera angle and height changed.<br/><br/>The pan and rotate buttons control how the map acts when dragging with the mouse. By default, the pan button is active and the map can be rotated and camera angle changed by pressing Shift / Alt / Ctrl and dragging with a mouse. By choosing the rotate button instead of the pan button, the map can be rotated and camera angle changed by dragging with the mouse. Map can be panned by selecting the pan button again.<br/><br/>The camera height can be changed with the arrow buttons.' + }, + "publisher": { + "toolLabel": "Camera tools" } } } ); - \ No newline at end of file diff --git a/bundles/mapping/camera-controls-3d/resources/locale/fi.js b/bundles/mapping/camera-controls-3d/resources/locale/fi.js index 23c3e91bfc..0a0ff2f521 100644 --- a/bundles/mapping/camera-controls-3d/resources/locale/fi.js +++ b/bundles/mapping/camera-controls-3d/resources/locale/fi.js @@ -11,13 +11,16 @@ Oskari.registerLocalization( }, "rotateModeFailure": { "message" : "Kiertotilaan asetus epäonnistui", - "description": "Kameran tulee osoittaa kohti maanpintaa kun tila asetetaan" + "description": "Kameran tulee osoittaa kohti maanpintaa kun tila asetetaan" }, 'guidedTour': { 'title': '3D-kameratyökalut', 'message': '3D-näkymässä karttaa voi liikutuksen lisäksi pyörittää sekä katsoa eri kulmista ja korkeuksilta.<br/><br/>Suuntanuoli- ja pyörityspainikkeet määräävät kumpaa hiirellä liikuttamalla tehdään. Oletusarvoisesti suuntanuolipainike on valittuna, jolloin hiirellä liikutetaan karttaa ja yhdistelmällä Shift/Alt/Ctrl + hiiri pyöritetään karttaa sekä säädetään kameran kulmaa. Valitsemalla suuntanuolipainikkeen sijaan pyörityspainikkeen, voi karttaa pyörittää ja kulmaa säätää pelkällä hiirellä. Kartan liikutus tapahtuu valitsemalla jälleen suuntanuolipainike.<br/><br/>Katselukorkeutta voi säätää lähemmäs ja kauemmas maanpinnasta nuolipainikkeilla.' + }, + "publisher": { + "toolLabel": "Kameratyökalut" } + } } ); - \ No newline at end of file diff --git a/bundles/mapping/camera-controls-3d/resources/locale/sv.js b/bundles/mapping/camera-controls-3d/resources/locale/sv.js index 913d163897..5d1aae1806 100644 --- a/bundles/mapping/camera-controls-3d/resources/locale/sv.js +++ b/bundles/mapping/camera-controls-3d/resources/locale/sv.js @@ -11,13 +11,15 @@ Oskari.registerLocalization( }, "rotateModeFailure": { "message" : "Inställningen för att rotera kartläget misslyckades", - "description": "Kameran bör riktas mot jordytan när läget är inställt" + "description": "Kameran bör riktas mot jordytan när läget är inställt" }, 'guidedTour': { 'title': '3D kamera verktyg', 'message': 'I 3D kan kartan också roteras och visas i olika vinklar och höjder.<br/><br/>Panorering- och rotationsknapparna styr hur kartan fungerar när du drar med musen. Som standard är panorknappen aktiv och kartan kan roteras och kameravinkeln ändras genom att trycka på Shift / Alt / Ctrl + musen. Genom att välja rotationsknappen i stället för panoreringsknappen kan kartan roteras och kameravinkeln ändras med musen. Kartan kan panoreras genom att välja panoreringsknappen igen.<br/><br/>Kamerahöjden kan ändras med pilknapparna.' + }, + "publisher": { + "toolLabel": "Kameraverktyg" } } } ); - \ No newline at end of file diff --git a/bundles/mapping/camera-controls-3d/tool/CameraControls3dTool.js b/bundles/mapping/camera-controls-3d/tool/CameraControls3dTool.js index e8074fba8f..cff21c6852 100644 --- a/bundles/mapping/camera-controls-3d/tool/CameraControls3dTool.js +++ b/bundles/mapping/camera-controls-3d/tool/CameraControls3dTool.js @@ -1,75 +1,65 @@ -Oskari.clazz.define('Oskari.mapping.cameracontrols3d.CameraControls3dTool', - function () { - }, { - index: 3, - lefthanded: 'top left', - righthanded: 'top right', +import { AbstractPublisherTool } from '../../../framework/publisher2/tools/AbstractPublisherTool'; - groupedSiblings: true, - - /** - * Get tool object. - * @method getTool - * - * @returns {Object} tool description - */ - getTool: function () { - return { - id: 'Oskari.mapping.cameracontrols3d.CameraControls3dPlugin', - title: 'CameraControls3d', - config: {} - }; - }, +class CameraControls3dTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 170; + this.group = 'tools'; + this.lefthanded = 'top left'; + this.righthanded = 'top right'; + this.groupedSiblings = true; // Key in view config non-map-module-plugin tools (for returning the state when modifying an existing published map). - bundleName: 'camera-controls-3d', + this.bundleName = 'camera-controls-3d'; + } - /** - * Initialise tool - * @method init - */ - init: function (data) { - if (data.configuration[this.bundleName]) { - this.setEnabled(true); + init (data) { + if (data.configuration[this.bundleName]) { + this.setEnabled(true); + const location = data.configuration[this.bundleName]?.conf?.location?.classes; + if (location) { + this.getPlugin().setLocation(location); } - }, - /** + } + } + + getTool () { + return { + id: 'Oskari.mapping.cameracontrols3d.CameraControls3dPlugin', + title: Oskari.getMsg('CameraControls3d', 'publisher.toolLabel'), + config: {} + }; + } + + /** * Get values. * @method getValues * @public * * @returns {Object} tool value object */ - getValues: function () { - if (this.state.enabled) { - var pluginConfig = this.getPlugin().getConfig(); - var json = { - configuration: {} - }; - json.configuration[this.bundleName] = { - conf: pluginConfig, - state: {} - }; - return json; - } else { - return null; - } - }, - /** - * Stop tool. - * @method stop - * @public - */ - stop: function () { - this.__started = false; - if (!this.__plugin) { - return; - } - if (this.getSandbox()) { - this.__plugin.stopPlugin(this.getSandbox()); - } - this.__mapmodule.unregisterPlugin(this.__plugin); + getValues () { + if (this.state.enabled) { + const pluginConfig = this.getPlugin().getConfig(); + const json = { + configuration: {} + }; + json.configuration[this.bundleName] = { + conf: pluginConfig, + state: {} + }; + return json; + } else { + return null; } - }, { - 'extend': ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], - 'protocol': ['Oskari.mapframework.publisher.Tool'] - }); + } +} + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.CameraControls3dTool', + CameraControls3dTool, + { + protocol: ['Oskari.mapframework.publisher.Tool'] + } +); + +export { CameraControls3dTool }; From b6d1453fa00414dd3bfb8ada5ea9a6167b47c8eb Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Mon, 25 Nov 2024 15:56:46 +0200 Subject: [PATCH 168/181] time controls 3d es-class --- .../publisher2/resources/locale/en.js | 1 - .../publisher2/resources/locale/fi.js | 1 - .../publisher2/resources/locale/sv.js | 1 - .../time-control-3d/resources/locale/en.js | 3 + .../time-control-3d/resources/locale/fi.js | 3 + .../time-control-3d/resources/locale/sv.js | 3 + .../time-control-3d/tool/TimeControl3dTool.js | 97 +++++++++++-------- 7 files changed, 64 insertions(+), 45 deletions(-) diff --git a/bundles/framework/publisher2/resources/locale/en.js b/bundles/framework/publisher2/resources/locale/en.js index bf8924c507..95fa5c84c8 100755 --- a/bundles/framework/publisher2/resources/locale/en.js +++ b/bundles/framework/publisher2/resources/locale/en.js @@ -69,7 +69,6 @@ Oskari.registerLocalization( "LayerSelectionPlugin": "Map layers menu", "MapLegend": "Show map legend", "MapRotator": "Enable map rotation", - "TimeControl3d": "Time control", "toolbarToolNames": { "history": "Move to previous or next view", "history_back": "Move to previous view", diff --git a/bundles/framework/publisher2/resources/locale/fi.js b/bundles/framework/publisher2/resources/locale/fi.js index d6e904b2b7..ca2b832d39 100755 --- a/bundles/framework/publisher2/resources/locale/fi.js +++ b/bundles/framework/publisher2/resources/locale/fi.js @@ -69,7 +69,6 @@ Oskari.registerLocalization( "LayerSelectionPlugin": "Karttatasovalikko", "MapLegend": "Näytä karttaselitteet", "MapRotator": "Salli kartan pyörittäminen", - "TimeControl3d": "Ajanhetken säädin", "toolbarToolNames": { "history": "Siirtyminen edelliseen ja seuraavaan näkymään", "history_back": "Siirtyminen edelliseen näkymään", diff --git a/bundles/framework/publisher2/resources/locale/sv.js b/bundles/framework/publisher2/resources/locale/sv.js index 51d254c153..6fc82de51d 100755 --- a/bundles/framework/publisher2/resources/locale/sv.js +++ b/bundles/framework/publisher2/resources/locale/sv.js @@ -69,7 +69,6 @@ Oskari.registerLocalization( "LayerSelectionPlugin": "Kartlagermeny", "MapLegend": "Visa kartförklaringen", "MapRotator": "Tillåt kartrotation", - "TimeControl3d": "Tidskontroll", "toolbarToolNames": { "history": "Gå bakåt eller framåt", "history_back": "Gå bakåt", diff --git a/bundles/mapping/time-control-3d/resources/locale/en.js b/bundles/mapping/time-control-3d/resources/locale/en.js index da8b088500..d7798128be 100644 --- a/bundles/mapping/time-control-3d/resources/locale/en.js +++ b/bundles/mapping/time-control-3d/resources/locale/en.js @@ -10,6 +10,9 @@ Oskari.registerLocalization( "slow": "Slow", "normal": "Normal", "fast": "Fast" + }, + "publisher": { + "toolLabel": "Time control" } } } diff --git a/bundles/mapping/time-control-3d/resources/locale/fi.js b/bundles/mapping/time-control-3d/resources/locale/fi.js index bf2f624f47..46bb13f277 100644 --- a/bundles/mapping/time-control-3d/resources/locale/fi.js +++ b/bundles/mapping/time-control-3d/resources/locale/fi.js @@ -10,6 +10,9 @@ Oskari.registerLocalization( "slow": "Hidas", "normal": "Normaali", "fast": "Nopea" + }, + "publisher": { + "toolLabel": "Ajanhetken säädin" } } } diff --git a/bundles/mapping/time-control-3d/resources/locale/sv.js b/bundles/mapping/time-control-3d/resources/locale/sv.js index 1e4351cdd8..b7439addd4 100644 --- a/bundles/mapping/time-control-3d/resources/locale/sv.js +++ b/bundles/mapping/time-control-3d/resources/locale/sv.js @@ -10,6 +10,9 @@ Oskari.registerLocalization( "slow": "Långsam", "normal": "Normal", "fast": "Snabb" + }, + "publisher": { + "toolLabel": "Tidskontroll" } } } diff --git a/bundles/mapping/time-control-3d/tool/TimeControl3dTool.js b/bundles/mapping/time-control-3d/tool/TimeControl3dTool.js index 71280288bf..a4f72ef01f 100644 --- a/bundles/mapping/time-control-3d/tool/TimeControl3dTool.js +++ b/bundles/mapping/time-control-3d/tool/TimeControl3dTool.js @@ -1,46 +1,59 @@ -Oskari.clazz.define('Oskari.mapping.time-control-3d.TimeControl3dTool', - function () { - }, { - index: 2, - lefthanded: 'top left', - righthanded: 'top right', - groupedSiblings: true, - /** - * Get tool object. - * @method getTool - * - * @returns {Object} tool description - */ - getTool: function () { +import { AbstractPublisherTool } from '../../../framework/publisher2/tools/AbstractPublisherTool'; + +class TimeControl3dTool extends AbstractPublisherTool { + constructor (...args) { + super(...args); + this.index = 180; + this.group = 'tools'; + this.lefthanded = 'top left'; + this.righthanded = 'top right'; + this.groupedSiblings = true; + }; + + /** + * Get tool object. + * @method getTool + * + * @returns {Object} tool description + */ + getTool () { + return { + id: 'Oskari.mapping.time-control-3d.TimeControl3dPlugin', + title: Oskari.getMsg('TimeControl3d', 'publisher.toolLabel'), + config: this.state?.pluginConfig || {} + }; + } + + /** + * Get values. + * @method getValues + * @public + * + * @returns {Object} tool value object + */ + getValues () { + if (this.state.enabled) { return { - id: 'Oskari.mapping.time-control-3d.TimeControl3dPlugin', - title: 'TimeControl3d', - config: {} - }; - }, - /** - * Get values. - * @method getValues - * @public - * - * @returns {Object} tool value object - */ - getValues: function () { - if (this.state.enabled) { - return { - configuration: { - mapfull: { - conf: { - plugins: [{ id: this.getTool().id, config: this.getPlugin().getConfig() }] - } + configuration: { + mapfull: { + conf: { + plugins: [{ id: this.getTool().id, config: this.getPlugin().getConfig() }] } } - }; - } else { - return null; - } + } + }; + } else { + return null; } - }, { - 'extend': ['Oskari.mapframework.publisher.tool.AbstractPluginTool'], - 'protocol': ['Oskari.mapframework.publisher.Tool'] - }); + } +} + +// Attach protocol to make this discoverable by Oskari publisher +Oskari.clazz.defineES('Oskari.publisher.TimeControl3dTool', + TimeControl3dTool, + { + protocol: ['Oskari.mapframework.publisher.Tool'] + } +); + +export { TimeControl3dTool }; From 4727d77b8015d529249b75461bee125f54853ceb Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 26 Nov 2024 09:56:56 +0200 Subject: [PATCH 169/181] check array before length --- bundles/statistics/statsgrid/handler/SearchHandler.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index c5c8715b1a..4b5b657036 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -251,10 +251,13 @@ class SearchController extends AsyncStateHandler { const hasValidSelection = (id, allowed, time) => { const cur = current[id]; if (time) { + if (!Array.isArray(cur)) { + return false; + } if (searchTimeseries) { return cur.length === 2 && cur[0] < cur[1]; } - return Array.isArray(cur) && cur.some(value => allowed.includes(value)); + return cur.some(value => allowed.includes(value)); } return allowed.includes(cur); }; From 7ef2b0d5df0942bdba09bfa6521a9ba8ae821deb Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 26 Nov 2024 10:00:10 +0200 Subject: [PATCH 170/181] don't require selected value --- bundles/statistics/statsgrid/view/search/IndicatorParams.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx index 8d90cfd831..c79e816e92 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorParams.jsx @@ -24,7 +24,7 @@ const StyledSelect = styled(Select)` width: 100%; `; -const TimeSeriesParams = ({ id, options, selectedValues, controller }) => ( +const TimeSeriesParams = ({ id, options, selectedValues = [], controller }) => ( <Timeseries> <TimeseriesField> <b><Message messageKey='parameters.from' /></b> @@ -47,7 +47,7 @@ const TimeSeriesParams = ({ id, options, selectedValues, controller }) => ( TimeSeriesParams.propTypes = { id: PropTypes.string.isRequired, options: PropTypes.array.isRequired, - selectedValues: PropTypes.array.isRequired, + selectedValues: PropTypes.array, controller: PropTypes.object.isRequired }; From 21a6bb3b30f765e8dfc03e15859a7541efc4d03d Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 26 Nov 2024 10:00:32 +0200 Subject: [PATCH 171/181] sort time values --- bundles/statistics/statsgrid/handler/IndicatorHelper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/bundles/statistics/statsgrid/handler/IndicatorHelper.js b/bundles/statistics/statsgrid/handler/IndicatorHelper.js index 341b0ef44b..c296bb5d80 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorHelper.js +++ b/bundles/statistics/statsgrid/handler/IndicatorHelper.js @@ -127,6 +127,7 @@ const processMetadata = (meta) => { const label = locParams[id] || id; const selector = { id, values, time, label }; if (time) { + values.sort((a, b) => Oskari.util.naturalSort(a.value, b.value, true)); selectors.unshift(selector); } else { selectors.push(selector); From 1a0009354d2134a87be4c055ddddf724de14ab74 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 26 Nov 2024 10:05:42 +0200 Subject: [PATCH 172/181] add badge and show remove all for one selected --- .../view/search/IndicatorCollapse.jsx | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx b/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx index fa469d1386..1fdd3277d0 100644 --- a/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx +++ b/bundles/statistics/statsgrid/view/search/IndicatorCollapse.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Message, Collapse } from 'oskari-ui'; +import { Message, Collapse, Badge } from 'oskari-ui'; import { IconButton } from 'oskari-ui/components/buttons'; import { IndicatorRow } from './IndicatorRow'; import styled from 'styled-components'; @@ -8,8 +8,14 @@ import styled from 'styled-components'; const StyledCollapse = styled(Collapse)` margin-top: 20px; `; -const RemoveAll = styled(IconButton)` +const Extra = styled.div` height: 20px; + display: flex; + flex-flow: row nowrap; + align-items: center; + > * { + margin-left: 5px; + } `; const Content = ({ indicators = [], removeIndicator, showMetadata }) => { @@ -31,17 +37,18 @@ Content.propTypes = { }; const PanelExtra = ({ indicators = [], removeAll }) => { - if (indicators.length < 2) { - return null; - } + const showDelete = indicators.length !== 0; return ( - <div onClick={e => e.stopPropagation()}> - <RemoveAll + <Extra onClick={e => e.stopPropagation()}> + { showDelete && <IconButton type='delete' + iconSize={18} confirm={{ title: <Message messageKey='indicatorList.removeAll' /> }} title={<Message messageKey='indicatorList.removeAll' />} onConfirm={() => removeAll()}/> - </div> + } + <Badge count={indicators.length}/> + </Extra> ); }; PanelExtra.propTypes = { From c3db5aafe30782698a7b0dabb59c2773045c703d Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Tue, 26 Nov 2024 10:15:30 +0200 Subject: [PATCH 173/181] remove padding from content and increase z-index --- src/react/components/window/Modal.jsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/react/components/window/Modal.jsx b/src/react/components/window/Modal.jsx index 265d808033..a0cdf93e2a 100644 --- a/src/react/components/window/Modal.jsx +++ b/src/react/components/window/Modal.jsx @@ -1,19 +1,22 @@ import React from 'react'; import { Modal as AntModal } from 'antd'; import { ThemeConsumer } from '../../util/contexts'; -import styled from 'styled-components'; import { Header } from 'oskari-ui/components/window/Header'; -const StyledModal = styled(AntModal)` - .ant-modal-body { - padding: 0 0 0.5em 0; + +const styles = { + body: { + padding: '0 0 0.5em 0' + }, + content: { + padding: '0' } -`; +}; export const Modal = ThemeConsumer(({children, title, options={}, theme={}}) => { - return <StyledModal open={true} closable={false} centered={true} footer={null}> + return <AntModal zIndex={100000} styles={styles} open={true} closable={false} centered={true} footer={null}> <div> <Header title={title} isDraggable={false}/> {children} </div> - </StyledModal>; + </AntModal>; }); \ No newline at end of file From a69ce868492319a9ce35194fdc61e8f00fd37976 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Tue, 26 Nov 2024 10:24:49 +0200 Subject: [PATCH 174/181] fix storing the map rotation in publisher --- .../mapping/maprotator/publisher/MapRotator.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/bundles/mapping/maprotator/publisher/MapRotator.js b/bundles/mapping/maprotator/publisher/MapRotator.js index 9aea7e5575..4691173062 100755 --- a/bundles/mapping/maprotator/publisher/MapRotator.js +++ b/bundles/mapping/maprotator/publisher/MapRotator.js @@ -21,12 +21,27 @@ class MapRotatorTool extends AbstractPublisherTool { } // saved configuration -> restore. - const conf = data.configuration[this.bundleName].conf || {}; + const bundleData = data.configuration[this.bundleName]; + const conf = bundleData?.conf || {}; this.handler.init(conf); this.storePluginConf(conf); + this.storePluginState(bundleData?.state || {}); this.setEnabled(true); } + storePluginState (state) { + this.state.pluginState = state || {}; + } + + setEnabled (enabled) { + super.setEnabled(enabled); + if (enabled && this.state.pluginState?.degrees) { + this.getPlugin().setRotation(this.state.pluginState?.degrees); + } else { + this.getMapmodule().getMap().getView().setRotation(0); + } + } + isDisplayed () { // shouldn't be shown if bundle is not started // otherwise results in js errors From 6da7655db63d41c16dc6e4a20d83e9f3aa26cc77 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Tue, 26 Nov 2024 11:42:28 +0200 Subject: [PATCH 175/181] add zindex for antd tabs dropdown --- resources/css/portal.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/css/portal.css b/resources/css/portal.css index 326ae040bd..51ababeb39 100755 --- a/resources/css/portal.css +++ b/resources/css/portal.css @@ -532,6 +532,10 @@ This seems to only happen on the Firefox browser z-index: 30050 !important; } +.ant-tabs-dropdown { + z-index: 30050 !important; +} + .ant-popover { z-index: 30030 !important; } From 328064ce7a7d626933bd0a7a146f2edccc2d0768 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Tue, 26 Nov 2024 12:41:01 +0200 Subject: [PATCH 176/181] projection change dialog modal --- bundles/framework/mydata/handler/PublishedMapsHandler.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundles/framework/mydata/handler/PublishedMapsHandler.js b/bundles/framework/mydata/handler/PublishedMapsHandler.js index d451aa285b..c314a82f0a 100644 --- a/bundles/framework/mydata/handler/PublishedMapsHandler.js +++ b/bundles/framework/mydata/handler/PublishedMapsHandler.js @@ -174,7 +174,7 @@ class MapsHandler extends StateHandler { dialog.close(true); }); dialog.show(this.loc('projectionError').title, this.loc('projectionError').msg, [cancel, btn]); - dialog.makeDraggable(); + dialog.makeModal(); } editView (data) { @@ -186,6 +186,7 @@ class MapsHandler extends StateHandler { this.createProjectionChangeDialog(() => { window.location.href = this.constructUrlWithUuid(srs, embeddedMapUuid, data); }); + return; } const resp = this.viewService.isViewLayersLoaded(data, sandbox); From a508918d1f34b5e5e259660d370371d54b38ce44 Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Tue, 26 Nov 2024 14:05:27 +0200 Subject: [PATCH 177/181] do not limit dropdown menu width --- .../publisher2/view/form/GeneralInfoForm.jsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bundles/framework/publisher2/view/form/GeneralInfoForm.jsx b/bundles/framework/publisher2/view/form/GeneralInfoForm.jsx index 56023d29a1..bf116130c0 100644 --- a/bundles/framework/publisher2/view/form/GeneralInfoForm.jsx +++ b/bundles/framework/publisher2/view/form/GeneralInfoForm.jsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { LabeledInput, Select, Option, Message, Label } from 'oskari-ui'; import styled from 'styled-components'; import { InfoIcon } from 'oskari-ui/components/icons'; - +import { PropTypes } from 'prop-types'; const FieldWithInfo = styled('div')` display: flex; flex-direction: row; @@ -13,12 +13,11 @@ const FieldWithInfo = styled('div')` const BUNDLE_KEY = 'Publisher2'; export const GeneralInfoForm = ({ onChange, data }) => { - const [state, setState] = useState({ name: data.name.value ? data.name.value : null, domain: data.domain.value ? data.domain.value : null, language: data.language.value ? data.language.value : Oskari.getLang() - }) + }); const languages = Oskari.getSupportedLanguages(); @@ -73,6 +72,7 @@ export const GeneralInfoForm = ({ onChange, data }) => { }); onChange('language', lang); }} + popupMatchSelectWidth={false} > {languages.map(lang => ( <Option value={lang} key={lang}> @@ -85,3 +85,8 @@ export const GeneralInfoForm = ({ onChange, data }) => { </div> ); }; + +GeneralInfoForm.propTypes = { + onChange: PropTypes.func, + data: PropTypes.obj +}; From 30878734fefc945f14a5dde723eeb8828f4c2a1d Mon Sep 17 00:00:00 2001 From: Pekka Helesuo <pekka.helesuo@siili.com> Date: Tue, 26 Nov 2024 14:24:48 +0200 Subject: [PATCH 178/181] change padding to margin --- .../scss/announcementsplugin.ol.scss | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/bundles/framework/announcements/resources/scss/announcementsplugin.ol.scss b/bundles/framework/announcements/resources/scss/announcementsplugin.ol.scss index e39ff75fe4..e4e7622c22 100644 --- a/bundles/framework/announcements/resources/scss/announcementsplugin.ol.scss +++ b/bundles/framework/announcements/resources/scss/announcementsplugin.ol.scss @@ -26,7 +26,7 @@ div.announcements-content { } div.mapplugin.announcements { - padding: 10px 10px 0 10px; + margin: 10px 10px 0 10px; clear:both; height: 37px; overflow: visible; @@ -38,21 +38,21 @@ div.mapplugin.announcements { div.content { background-color: white; max-height: 95%; - + div.announcements-content { overflow: hidden; width:100%; background-color: #fff; - + } - + &.mobile { position:fixed; top:36px; left:0; width:100%; } - + div.content-header { /* This is some sort of a theme color... leave it be */ background-color: #333438; @@ -60,26 +60,26 @@ div.mapplugin.announcements { text-align: left; height: 25px; padding: 5px; - + .content-close { display: inline-block; float: right; cursor: pointer; } - + div.content-header-title { display: inline-block; float: left; padding: 5px; } } - + div.baseLayerHeader, div.layerHeader { background-color: #E6E6E6; padding: 10px; text-align: left; } - + div.layer { margin: 5px 0; text-align: left; @@ -88,20 +88,20 @@ div.mapplugin.announcements { margin-right: 10px; float: left; } - + span { display: block; margin-left: 30px; word-break: break-word; } - + .style-selector select{ max-width: 120px; display: inline-block; - margin-left: 5px; + margin-left: 5px; } } - + div.styled-header-arrow { width: 0px; height: 0px; @@ -113,11 +113,11 @@ div.mapplugin.announcements { border-bottom: 10px solid #333438; } } - + div.layers, div.baselayers { padding: 10px; } - + div.announcements-header { background-color: #3c3c3c; color: #FFF; @@ -129,7 +129,7 @@ div.mapplugin.announcements { margin-right: 4px; vertical-align: middle; } - + &.mobile { position:fixed; top:0; @@ -152,11 +152,11 @@ div.mapplugin.announcements { font-weight: lighter !important; text-shadow: 0px 0px #fff !important; } - + .active, .collapsible:hover { background-color: #555 !important; } - + .announcement-content { text-align: left; max-height: 0; @@ -170,11 +170,11 @@ div.mapplugin.announcements { padding-bottom: 0px !important; } } - + div.announcements-header:hover { color: #ff9100; } - + .oskari-publisher-layers-rounded-dark, .oskari-publisher-layers-rounded-light { -webkit-border-radius: 15px; -moz-border-radius: 15px; @@ -185,7 +185,7 @@ div.mapplugin.announcements { -moz-background-clip: padding-box; background-clip: padding-box; } - + .oskari-publisher-layers-sharp-dark, .oskari-publisher-layers-sharp-light { -webkit-border-radius: 0; -moz-border-radius: 0; @@ -193,7 +193,7 @@ div.mapplugin.announcements { -ms-border-radius: 0; border-radius: 0; } - + .oskari-publisher-layers-3d-dark, .oskari-publisher-layers-3d-light { -webkit-border-radius: 3px; -moz-border-radius: 3px; @@ -204,7 +204,7 @@ div.mapplugin.announcements { -moz-background-clip: padding-box; background-clip: padding-box; } - + .oskari-publisher-layers-header-rounded-dark, .oskari-publisher-layers-header-rounded-light { -webkit-border-radius: 13px 13px 0 0; -moz-border-radius: 13px 13px 0 0; @@ -215,7 +215,7 @@ div.mapplugin.announcements { -moz-background-clip: padding-box; background-clip: padding-box; } - + .oskari-publisher-layers-header-sharp-dark, .oskari-publisher-layers-header-sharp-light { -webkit-border-radius: 0; -moz-border-radius: 0; @@ -223,7 +223,7 @@ div.mapplugin.announcements { -ms-border-radius: 0; border-radius: 0; } - + .oskari-publisher-layers-header-3d-dark, .oskari-publisher-layers-header-3d-light { -webkit-border-radius: 1px 1px 0 0; -moz-border-radius: 1px 1px 0 0; @@ -235,11 +235,11 @@ div.mapplugin.announcements { background-clip: padding-box; } } - + .published-styled-layerselector { position: relative !important; } - + .published-styled-layerselector-header { width: 38px !important; height: 38px !important; @@ -247,7 +247,7 @@ div.mapplugin.announcements { background-color: transparent !important; float: right !important; } - + .published-styled-layerselector-content { position: absolute; top: 44px !important; @@ -256,7 +256,7 @@ div.mapplugin.announcements { top: 0 !important; } } - + /* Hacketyhack, we need to keep the popup on screen */ .mapplugins.left div.mapplugin.announcements div.published-styled-layerselector-content { left: 0px !important; @@ -265,11 +265,11 @@ div.mapplugin.announcements { top: 0 !important; } } - + .mapplugins.left div.mapplugin.announcements div.published-styled-layerselector-content div.styled-header-arrow { margin-left: 12px !important; } - + div.mapplugin { &.announcementspopup { div.content { @@ -277,13 +277,13 @@ div.mapplugin.announcements { font-weight: bold; padding-bottom: 6px; } - + .style-selector select{ max-width: 120px; display: inline-block; - margin-left: 5px; + margin-left: 5px; } - + div.layerHeader { padding-top: 10px; } From e937a23cca60759e80d7a76d93dc7604e9ee4056 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 27 Nov 2024 12:12:05 +0200 Subject: [PATCH 179/181] set loading after data exists check --- bundles/statistics/statsgrid/handler/IndicatorFormHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js index 385cf57451..e7fe17d1fd 100644 --- a/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js +++ b/bundles/statistics/statsgrid/handler/IndicatorFormHandler.js @@ -163,7 +163,6 @@ class IndicatorFormController extends StateHandler { } async saveData () { - this.updateState({ loading: true }); const { dataByRegions, regionset, selection } = this.getState(); const data = {}; @@ -182,6 +181,7 @@ class IndicatorFormController extends StateHandler { return; } try { + this.updateState({ loading: true }); const indicator = this.getFullIndicator(); await saveIndicatorData(indicator, data, regionset); const indicatorInfo = `Indicator: ${indicator.id}, selection: ${selection}, regionset: ${regionset}.`; From 1678a9426006db96b0a8925d164276fa8af045b4 Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 27 Nov 2024 13:02:34 +0200 Subject: [PATCH 180/181] create copy from seletor --- bundles/statistics/statsgrid/handler/SearchHandler.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 4b5b657036..9920d03757 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -199,8 +199,9 @@ class SearchController extends AsyncStateHandler { params.regionsets.forEach(rs => regionsets.add(rs)); params.selectors.forEach((selector) => { const existing = combinedSelectors.find(s => s.id === selector.id); + // Note: selectors may come from metadata cache, don't mess up cached data if (!existing) { - combinedSelectors.push(selector); + combinedSelectors.push({ ...selector }); } else { const values = existing.values.map(s => s.value); const newValues = selector.values.filter(v => !values.includes(v.value)); From 61c97aae48ddd32a0156753e73cf6ee6d9977e8b Mon Sep 17 00:00:00 2001 From: Olli Kauppinen <olli.kauppinen@nls.fi> Date: Wed, 27 Nov 2024 14:20:24 +0200 Subject: [PATCH 181/181] don't add error if metadata selector is missing --- bundles/statistics/statsgrid/handler/SearchHandler.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bundles/statistics/statsgrid/handler/SearchHandler.js b/bundles/statistics/statsgrid/handler/SearchHandler.js index 9920d03757..deb39b07eb 100644 --- a/bundles/statistics/statsgrid/handler/SearchHandler.js +++ b/bundles/statistics/statsgrid/handler/SearchHandler.js @@ -375,12 +375,15 @@ class SearchController extends AsyncStateHandler { const multiSelections = []; Object.keys(selections).forEach(key => { const metaSelector = metadata.selectors.find(selector => selector.id === key); - // use metadata for validity check. Params have combined selectors. - const checkAllowed = value => metaSelector.values.find(obj => obj.value === value); if (!metaSelector) { - indSearchValues.error = 'indicatorMetadataError'; + // Only allowed selections are used for get/add indicator + // multi indicator selections may have selection which is missing from single indicator selectors + // skip selection and don't add error return; } + // use metadata for validity check. Params have combined selectors. + const checkAllowed = value => metaSelector.values.find(obj => obj.value === value); + const values = selections[key]; // single if (!Array.isArray(values)) {