diff --git a/.distignore b/.distignore index eab5009..0109218 100644 --- a/.distignore +++ b/.distignore @@ -11,6 +11,7 @@ /components/sass /config.rb /deploy +/docs /DEVELOPERS.md /Gruntfile.js /node_modules diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..200b724 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,55 @@ +name: Deploy Documentation + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + name: Build docs + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build docs + run: npm run build:docs + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vuepress/dist + + deploy: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 8b1cd31..2ab11ee 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,8 @@ node_modules/ deploy/ build/ package-lock.json -.vscode/ \ No newline at end of file +.vscode/ +.php-cs-fixer.cache +docs/.vuepress/dist +docs/.vuepress/.temp +docs/.vuepress/.cache \ No newline at end of file diff --git a/DEVELOPERS.md b/DEVELOPERS.md index 73d2748..2ea7561 100755 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -1,8 +1,16 @@ # Developer Instructions # +## Documentation ## + +Full user and developer documentation is available at **https://methnen.github.io/m-chart/**. The developer section covers PHP hooks, JavaScript events, and the admin UI hooks API. + ## Build Environment Install ## -`npm install` +``` +npm install +``` + +> **Note:** `package.json` includes a `postinstall` script that runs `npm run build` automatically after `npm install` completes. This means a full build will fire on first install — this is expected. ## Build Commands ## @@ -18,7 +26,7 @@ Build CSS only: npm run build:css ``` -Build JS (minify helpers): +Build JS (minifies `components/js/m-chart-chartjs-helper.js`): ``` npm run build:js @@ -30,6 +38,12 @@ Build block only: npm run build:block ``` +Build admin React app only: + +``` +npm run build:admin-ui +``` + Convert readme.txt to README.md: ``` @@ -38,14 +52,143 @@ npm run build:readme ## Watch Commands ## -Watch everything (CSS, JS, and block): +Watch everything (CSS, JS, block, admin app, and readme): ``` npm run watch ``` +Individual watch targets are also available: + +| Command | Watches | +|---------|---------| +| `npm run watch:admin-ui` | React admin app | +| `npm run watch:block` | Gutenberg block | +| `npm run watch:css` | SCSS → CSS | +| `npm run watch:js` | `m-chart-chartjs-helper.js` | +| `npm run watch:readme` | `readme.txt` → `README.md` | + +## Translations (i18n) ## + +PHP translations use `.po` / `.mo` files managed in Poedit. JavaScript translations require additional steps because `wp-scripts` bundles multiple source files into a single compiled file, and WordPress needs handle-named JSON files to load them. + +All locale files (`.po`, `.mo`, `.l10n.php`) live in `components/languages/`. + +### Workflow + +1. Open the `.po` file for the locale you are updating (e.g. `components/languages/m-chart-zh_CN.po`) in Poedit. +2. **Catalog > Update from Sources** to scan for new/changed translatable strings in both PHP and JS source files. +3. Translate any new or updated strings. +4. Save in Poedit (this generates the `.mo` and `.l10n.php` files automatically). +5. Generate per-source-file JSON translation files: + +``` +wp i18n make-json components/languages/m-chart-zh_CN.po --no-purge +``` + +6. Merge the hash-based JSON files into handle-named files that WordPress can find: + +``` +npm run build:i18n +``` + +Repeat steps 1–6 for each locale. + +### Why the merge step is needed + +`wp i18n make-json` generates one JSON file per source file, named with the md5 hash of the source path (e.g. `components/admin-ui-src/components/AxisRows.js`). However, WordPress looks up translations using the md5 hash of the *compiled* file path (e.g. `components/admin-ui/index.js`). Since these hashes don't match, WordPress falls back to looking for `{domain}-{locale}-{handle}.json`. The `build:i18n` script merges the per-source-file JSONs into these handle-named files: + +- `m-chart-{locale}-m-chart-admin-ui.json` — admin UI translations +- `m-chart-{locale}-m-chart-editor.json` — block editor translations + +### Poedit configuration + +Each `.po` file includes Poedit search path headers so that source scanning works correctly. These should exclude: + +- `*.min.js` — minified files (duplicates of source) +- `node_modules` — third-party dependencies +- `components/external` — vendored libraries + +If creating a new locale, copy these headers from an existing `.po` file (e.g. `m-chart-en_US.po`). + ## Deployment ## Deploy to WordPress.org via GitHub Actions: Actions tab → "Deploy to WordPress.org" → "Run workflow" + +Before triggering the workflow: +- Bump the version number in `m-chart.php`, `class-m-chart.php`, and `readme.txt` +- Run `npm run build` and commit all compiled assets +- Run `npm run build:readme` and commit the updated `README.md` +- Target the `main` branch when running the workflow + +--- + +## Admin UI Architecture ## + +The chart post-edit screen uses a React app (`components/admin-ui-src/`) compiled to `components/admin-ui/` by `@wordpress/scripts`. As of v2.0 the React admin UI is used for all charting libraries — the previous jQuery + Handlebars stack has been removed. + +### Source layout + +``` +components/admin-ui-src/ + index.js Entry point — mounts portals into each meta box div + context/ + ChartAdminContext.js Single shared reducer (all components read/write here) + hooks/ + useChartRefresh.js Debounced AJAX fetch for updated chart args + useFormSubmissionGuard.js Gates Save/Publish buttons on state.formEnabled; blocks + form submission while a chart refresh is in flight + useImageGeneration.js Captures Chart.js canvas → base64 PNG → hidden textarea + useLongPress.js 500ms pointer-event long-press (tab rename on mobile) + utils/ + measureTextWidth.js Canvas-based text measurement utility + components/ + ChartMetaBox.js Root for the Chart meta box (preview + settings) + ChartPreview.js Imperative Chart.js instance managed via refs + ChartSettings.js Settings form container + TypeAndThemeRow.js Type / Theme / Height inputs + ParseAndFlagsRow.js Parse direction, Labels, Legend, Shared tooltip + AxisRows.js Vertical/horizontal axis title + units, Y-min + ShortcodeAndImageRow.js Shortcode display, image URL, library hidden input + SpreadsheetMetaBox.js Root for the Data meta box + SheetTabs.js Tab bar (conditionally shown for multi-sheet types) + SheetTab.js Individual tab — click/dblclick/long-press rename, delete + JspreadsheetWrapper.js Thin imperative wrapper around a Jspreadsheet worksheet + CsvControls.js CSV import (fetch + FormData) and export (temp form POST) + SubtitleField.js Controlled subtitle input (replaces subtitle-field.php) +``` + +### Data flow + +1. PHP localises initial state into `window.m_chart_admin` via `wp_localize_script` (see `current_screen()` in `class-m-chart-admin.php`). The object contains: plugin metadata (`slug`, `version`), settings (`performance`, `image_support`, `image_multiplier`, etc.), the active `library`, chart post meta, spreadsheet data, available chart types and themes, a nonce, the AJAX URL, and initial chart args. +2. `ChartAdminContext` seeds a `useReducer` from that object — all components share one context. +3. User changes (settings, spreadsheet, title, subtitle) update context state. +4. `useChartRefresh` debounces 300 ms then POSTs to `admin-ajax.php?action=m_chart_get_chart_args`. +5. The response updates `chartArgs` in context; `ChartPreview` patches its Chart.js instance. +6. On form submit, `SpreadsheetMetaBox` serialises all sheet data to the hidden `textarea[name="m-chart[data]"]`. + +### Mount points (PHP) + +| PHP method | Mount div | Component | +|---|---|---| +| `edit_form_before_permalink()` | `#m-chart-subtitle-root` | `` | +| `spreadsheet_meta_box()` | `#m-chart-spreadsheet-root` | `` | +| `chart_meta_box()` | `#m-chart-chart-root` | `` | + +All three share a single `ChartAdminProvider` rendered into a hidden container appended to `
`, with portals projecting into each mount div. + +### Library plugins + +Library plugins (e.g. M Chart Highcharts Library) integrate with the React admin UI via the `wp.hooks` API: + +- **`m_chart_admin_scripts` action** — enqueue library-specific scripts after M Chart's scripts are loaded +- **`m_chart.render_chart` filter** — handle chart rendering in the admin preview, returning `true` to prevent the default Chart.js renderer from running +- **`m_chart.settings_component` filter** — replace the default Chart.js settings UI with a library-specific React component + +See `version-2-notes.md` for the full list of available hooks. + +### Extensibility (wp.hooks) + +See `version-2-notes.md` for the full list of JavaScript hooks available to library plugin authors. diff --git a/LICENSE.md b/LICENSE.md index c853d0f..ac30968 100755 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2021 Jamie Poitra +Copyright (c) 2015-2026 Jamie Poitra Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -21,11 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. License info for libraries found in `components/external`: -- [canvg](https://github.com/gabelerner/canvg/) - - License at the time of writing: MIT -- [handlebars](https://github.com/wycats/handlebars.js/) - - License at the time of writing: MIT -- [handsontable](https://github.com/handsontable/handsontable) +- [Jspreadsheet CE](https://github.com/jspreadsheet/ce) - License at the time of writing: MIT - [Chart.js](https://github.com/chartjs/Chart.js) - License at the time of writing: MIT diff --git a/README.md b/README.md index 5942858..f1484e0 100755 --- a/README.md +++ b/README.md @@ -2,30 +2,25 @@ **Contributors:** [methnen](https://profiles.wordpress.org/methnen/) **Tags:** chartjs, highcharts, graphs, charts, tables, data **Tested up to:** 6.9.1 -**Stable tag:** 1.12 +**Stable tag:** 2.0 **License:** MIT Manage data sets and display them as charts in WordPress. ## Description ## -Allows you to manage data sets via a spreadsheet interface and present that data in chart form via the Chart.js or [Highcharts](https://github.com/methnen/m-chart-highcharts-library/) chart libraries. The charts can then be embedded into a regular post via a handy shortcode. +Manage data sets via a spreadsheet interface and display them as charts via the Chart.js or [Highcharts](https://github.com/methnen/m-chart-highcharts-library/) chart libraries and embed them via a shortcode or WordPress block. -**Note:** Starting with version 1.8 the Chart.js library is no longer on the 2.x.x branch which introduces some [breaking changes](https://www.chartjs.org/docs/latest/getting-started/v3-migration.html). This will probably only affect you if you were modifying the default Chart.js behavior in some way. +**Note:*** Starting with M Chart version 2.0 the plugin uses a new React based UI as well as updated data parsing class. This means any third party libraries will need to be refactored to support this. Version 1.3 of the [M Chart Highcharts Library](https://github.com/methnen/m-chart-highcharts-library/) has the necssary changes implemented. + +**Note:** Starting with version 1.8 the Chart.js library is no longer on the 2.x.x branch which introduces some [breaking changes](https://www.chartjs.org/docs/latest/migration/v3-migration.html). This will probably only affect you if you were modifying the default Chart.js behavior in some way. **Note:** Starting with version 1.7 Highcharts is no longer included with this plugin by default. If you'd still like to use the features that require Higcharts please install the [M Chart Highcharts Library](https://github.com/methnen/m-chart-highcharts-library/) plugin before installing this update. -For full documentation please see the [Wiki](https://github.com/methnen/m-chart/wiki). +For full documentation please see the [Documentation](https://methnen.github.io/m-chart/). To contribute, report issues, or make feature requests use [Github](https://github.com/methnen/m-chart). -## Installation ## - -1. Put the m-chart directory into your plugins directory -2. Click 'Activate' in the Plugins admin panel -3. Adjust the M Chart Settings to your preference - - WordPress Admin -> Charts -> Settings - ## Screenshots ## ### 1. M Chart UI ### @@ -40,6 +35,26 @@ To contribute, report issues, or make feature requests use [Github](https://gith ## Changelog ## +### 2.0 ### + +* Refactored the Admin UI to use React for all of the interface which results in some UI performance and functionality improvements + * Added a Copy button to the Shortcode input field + * Height and Force vertical axis minimum fields now resize to fit their contents +* Refactored PHP data parsing code to improve reliability and flexibility with differently formatted data + * Number values are now parsed to extract prefix and suffix strings allowing data to always be displayed with formatting and localization while still preserving prefixes and suffixes +* Refactored helper code as a Chart.js plugin instead of jQuery + * Helper code now formats and parses labels as well as tooltips +* Refactored Chart.js template to no longer be reliant on jQuery +* Refactored the Block interface to make some performance and UI improvements + * Reduced the number of queries required for the interface to function + * Charts are now lazy loaded into the UI and subsequent charts are loaded as user scrolls + * Search now accepts any reasonable values you could expect in a post title + * Show attribute can now be controlled from the block UI + * Better handling of charts when Chart is missing an image or M Chart performance settings have disabled images +* Lots of additional code modernization and style improvements that didn't effect functionality but should make code easier to maintain going forward +* Fixed an issue where a duplicate localization call could cause localization in the Block ui to not always load +* Fixed an issue where changing a cell value and then clicking Update could result in lost data and/or an incorrect image + ### 1.12 ### * Switched from the old version (6.2.2) of [Handsontable](https://handsontable.com/blog/handsontable-drops-open-source-for-a-non-commercial-license) to [Jspreadsheet CE](https://bossanova.uk/jspreadsheet/) (5.0.0) @@ -68,7 +83,7 @@ To contribute, report issues, or make feature requests use [Github](https://gith * Added a Chart Block for the WordPress Block Editor * Send your thanks to [webconstructor](https://github.com/webconstructor) for the lion's share of the work on this -* Added a [CSV Delimiter](https://github.com/methnen/m-chart/wiki/csv-importing-exporting) control and setting to allow for differences in CSV files from different regions +* Added a [CSV Delimiter](https://methnen.github.io/m-chart/guide/csv) control and setting to allow for differences in CSV files from different regions * Fixed an issue where data points weren't being cleaned well enough and it could confuse Chart.js * Fixed an issue where Chart.js tooltips sometimes duplicated a label * Fixed an issue with entities inside of labels @@ -119,7 +134,7 @@ To contribute, report issues, or make feature requests use [Github](https://gith ### 1.9 ### -* Added support for [stacked column](https://github.com/methnen/m-chart/wiki/Types-of-charts#stacked-column), [stacked bar](https://github.com/methnen/m-chart/wiki/Types-of-charts#stacked-bar), and [doughnut](https://github.com/methnen/m-chart/wiki/Types-of-charts#doughnut) charts when using Chart.js +* Added support for [stacked column](https://methnen.github.io/m-chart/guide/chart-types), [stacked bar](https://methnen.github.io/m-chart/guide/chart-types), and [doughnut](https://methnen.github.io/m-chart/guide/chart-types) charts when using Chart.js * Added support for data point labels when using Chart.js * Uses the [chartjs-plugin-datalabels](https://github.com/chartjs/chartjs-plugin-datalabels) plugin * Added better number formatting for Chart.js @@ -140,15 +155,15 @@ To contribute, report issues, or make feature requests use [Github](https://gith ### 1.8 ### -* Added support for [spline](https://github.com/methnen/m-chart/wiki/Types-of-charts#spline), [area](https://github.com/methnen/m-chart/wiki/Types-of-charts#area), [scatter](https://github.com/methnen/m-chart/wiki/Types-of-charts#scatter), [bubble](https://github.com/methnen/m-chart/wiki/Types-of-charts#bubble), [radar](https://github.com/methnen/m-chart/wiki/Types-of-charts#radar), [radar area](https://github.com/methnen/m-chart/wiki/Types-of-charts#radar-area), and [polar](https://github.com/methnen/m-chart/wiki/Types-of-charts#polar) charts when using Chart.js -* Chart.js can now use [themes](https://github.com/methnen/m-chart/wiki/Themes) +* Added support for [spline](https://methnen.github.io/m-chart/guide/chart-types), [area](https://methnen.github.io/m-chart/guide/chart-types), [scatter](https://methnen.github.io/m-chart/guide/chart-types), [bubble](https://methnen.github.io/m-chart/guide/chart-types), [radar](https://methnen.github.io/m-chart/guide/chart-types), [radar area](https://methnen.github.io/m-chart/guide/chart-types), and [polar](https://methnen.github.io/m-chart/guide/chart-types) charts when using Chart.js +* Chart.js can now use [themes](https://methnen.github.io/m-chart/guide/themes) * Default themes: * Chart.js (Default) * Based on the Chart.js homepage colors * Color Blind Safe * Highcharts 4.x * Many additions/tweaks to Chart.js support - * Charts can now use the [Vertical axis minimum value field](https://github.com/methnen/m-chart/wiki/Creating-a-chart#user-content-vertical-axis-minimum-note) + * Charts can now use the [Vertical axis minimum value field](https://methnen.github.io/m-chart/guide/creating-a-chart) * Charts can now use the Shared tooltip setting * Charts use different symbols for each data set when possible * Circle, Diamond, Square, Triangle, etc... @@ -255,7 +270,7 @@ To contribute, report issues, or make feature requests use [Github](https://gith ### 1.6 ### -* Added support for [scatter](https://github.com/methnen/m-chart/wiki/Types-of-charts#scatter) and [bubble](https://github.com/methnen/m-chart/wiki/Types-of-charts#bubble) charts +* Added support for [scatter](https://methnen.github.io/m-chart/guide/chart-types) and [bubble](https://methnen.github.io/m-chart/guide/chart-types) charts * Charts can now be loaded via iframes (this enables remote embedding among other things) * Line, spline and area charts can now use shared tool tips * Added language settings to allow things like setting the thousands seperator and decimal symbols @@ -276,7 +291,7 @@ To contribute, report issues, or make feature requests use [Github](https://gith ### 1.5 ### * Added support for the [AMP plugin](https://wordpress.org/plugins/amp/) -* Shortcode can now output a [HTML table](https://github.com/methnen/m-chart/wiki/Chart-shortcode#html-table) instead of a chart +* Shortcode can now output a [HTML table](https://methnen.github.io/m-chart/guide/shortcode) instead of a chart * Updated Handsontable to the latest stable version (0.29) * Updated Highcharts to the latest stable version (5.0.2) * Tweaked how taxonomies are assigned so we don't unintentionally inherit them from other plugins @@ -287,7 +302,7 @@ To contribute, report issues, or make feature requests use [Github](https://gith ### 1.4 ### -* Added [Vertical axis minimum value field](https://github.com/methnen/m-chart/wiki/Creating-a-chart#user-content-vertical-axis-minimum-note) +* Added [Vertical axis minimum value field](https://methnen.github.io/m-chart/guide/creating-a-chart) * Fixed an issue where a notice error could occur when chart caches are being refreshed ### 1.3.2 ### @@ -313,7 +328,7 @@ To contribute, report issues, or make feature requests use [Github](https://gith ### 1.2 ### -* Added [themes](https://github.com/methnen/m-chart/wiki/Themes) +* Added [themes](https://methnen.github.io/m-chart/guide/themes) * Default themes: * Highcharts 4.x (Default) * Color Blind Safe @@ -353,8 +368,8 @@ To contribute, report issues, or make feature requests use [Github](https://gith ### 1.1 ### * Added a Shortcake shortcode ui -* Added [`canvas_done`](https://github.com/methnen/m-chart/wiki/Javascript-events#canvas_done) Javascript event -* Added [`m_chart_admin_footer_javascript`](https://github.com/methnen/m-chart/wiki/Action-and-filter-hooks#m_chart_admin_footer_javascript) Action hook +* Added [`canvas_done`](https://methnen.github.io/m-chart/developer/javascript-events) Javascript event +* Added [`m_chart_admin_footer_javascript`](https://methnen.github.io/m-chart/developer/hooks) Action hook * Fixed a PHP warning that occured when adding a new chart that had no data yet ### 1.0 ### diff --git a/assets/screenshot-1.png b/assets/screenshot-1.png index d4da4bd..baf1148 100644 Binary files a/assets/screenshot-1.png and b/assets/screenshot-1.png differ diff --git a/assets/screenshot-3.png b/assets/screenshot-3.png index 5754a0e..79c5334 100644 Binary files a/assets/screenshot-3.png and b/assets/screenshot-3.png differ diff --git a/components/admin-ui-src/components/AxisRows.js b/components/admin-ui-src/components/AxisRows.js new file mode 100644 index 0000000..30cb8be --- /dev/null +++ b/components/admin-ui-src/components/AxisRows.js @@ -0,0 +1,147 @@ +import { Fragment, useState, useCallback } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { useChartAdmin } from '../context/ChartAdminContext'; +import { measureTextWidth } from '../utils/measureTextWidth'; + +// Chart types that show y-min controls (line, spline, area only) +const YMIN_TYPES = new Set( [ + 'line', + 'spline', + 'area', +] ); + +// Chart types that show axis title/unit rows +const AXIS_TYPES = new Set( [ + 'line', + 'spline', + 'area', + 'column', + 'stacked-column', + 'bar', + 'stacked-bar', + 'scatter', + 'bubble', +] ); + +export default function AxisRows() { + const { state, dispatch } = useChartAdmin(); + const { postMeta, unitTerms } = state; + + const showAxis = AXIS_TYPES.has( postMeta.type ); + const showYMin = YMIN_TYPES.has( postMeta.type ); + + // Callback ref triggers a re-render when the input mounts, so the canvas measurement runs with the real element instead of the fallback + const [ yMinEl, setYMinEl ] = useState( null ); + const yMinRef = useCallback( node => setYMinEl( node ), [] ); + const yMinValue = String( postMeta.y_min_value ?? 0 ); + const yMinWidth = yMinEl ? ( measureTextWidth( yMinValue, yMinEl ) + 20 ) + 'px' : '73px'; + + function handleChange( field, value ) { + dispatch( { type: 'SET_POST_META', payload: { [ field ]: value } } ); + } + + function handleYMinCheck( checked ) { + dispatch( { type: 'SET_POST_META', payload: { y_min: checked } } ); + } + + // Always render axis rows so field values survive type switches on form save. + // Only hide them visually when the chart type doesn't need them. + const axisStyle = showAxis ? {} : { display: 'none' }; + const yMinStyle = showAxis && showYMin ? {} : { display: 'none' }; + + const unitOptions = ( + <> + + { unitTerms.map( ( { group, units } ) => ( + + + { units.map( ( unit ) => ( + + ) ) } + + ) ) } + + ); + + return ( + <> +
+

+
+ handleChange( 'y_title', e.target.value ) } + /> +

+

+
+ +

+
+
+

+ + handleChange( 'y_min_value', e.target.value ) } + style={ { width: yMinWidth, minWidth: 0 } } + /> +

+
+
+

+
+ handleChange( 'x_title', e.target.value ) } + /> +

+

+
+ +

+
+ + ); +} diff --git a/components/admin-ui-src/components/ChartMetaBox.js b/components/admin-ui-src/components/ChartMetaBox.js new file mode 100644 index 0000000..dd4dcce --- /dev/null +++ b/components/admin-ui-src/components/ChartMetaBox.js @@ -0,0 +1,44 @@ +import { useState, useEffect } from '@wordpress/element'; +import { useChartRefresh } from '../hooks/useChartRefresh'; +import { useFormSubmissionGuard } from '../hooks/useFormSubmissionGuard'; +import ChartPreview from './ChartPreview'; +import ChartSettings from './ChartSettings'; + +/** + * Root component for the chart meta box. + * + * Owns the title state (read from the classic WP #title input) and wires useChartRefresh so chart args are re-fetched whenever settings or data change + * The subtitle input is now a React-controlled SubtitleField component mounted via a separate portal — no DOM bridge needed here. + */ +export default function ChartMetaBox() { + const [ title, setTitle ] = useState( () => { + const el = document.getElementById( 'title' ); + return el ? el.value : ''; + } ); + + // Keep the React title state in sync with the native WP title input + // Needed because React doesn't own this input since it's created by core WordPress + useEffect( () => { + const el = document.getElementById( 'title' ); + + if ( ! el ) { + return; + } + + const handler = ( e ) => setTitle( e.target.value ); + + el.addEventListener( 'input', handler ); + + return () => el.removeEventListener( 'input', handler ); + }, [] ); + + useChartRefresh( title ); + useFormSubmissionGuard(); + + return ( + <> + + + + ); +} diff --git a/components/admin-ui-src/components/ChartPreview.js b/components/admin-ui-src/components/ChartPreview.js new file mode 100644 index 0000000..1845a71 --- /dev/null +++ b/components/admin-ui-src/components/ChartPreview.js @@ -0,0 +1,175 @@ +import { useEffect, useRef } from '@wordpress/element'; +import { useChartAdmin } from '../context/ChartAdminContext'; +import { useImageGeneration } from '../hooks/useImageGeneration'; + +/** + * Shallow-copies chart args to avoid mutating React state when Chart.js or MChartHelper modifies the chart config during initialization + * Tooltip callbacks and datalabels formatter are applied by MChartHelper via its beforeUpdate hook (runs each render) + * Bubble preprocessing runs once via beforeInit + */ +function prepareArgs( args ) { + if ( ! args ) { + return args; + } + + return { + ...args, + data: { ...args.data }, + options: { + ...args.options, + plugins: { + ...args.options?.plugins, + tooltip: { + ...args.options?.plugins?.tooltip, + }, + datalabels: { + ...args.options?.plugins?.datalabels, + }, + }, + }, + }; +} + +/** + * Default Chart.js renderer — create or update the Chart.js instance + * + * Applies chartjs-specific arg preparation before rendering + * Returned instance is stored in chartRef by the caller + * + * @param {HTMLCanvasElement} canvas Target canvas element + * @param {Object} args Raw chart args from state + * @param {Function} onComplete Callback to fire after render completes + * @param {Object|null} existingInstance Existing Chart.js instance, or null on first render + * + * @return {Object} The Chart.js instance + */ +function defaultChartjsRender( canvas, args, onComplete, existingInstance ) { + const prepared = prepareArgs( args ); + + // Guard against null/undefined datasets or labels (Chart.js requires arrays). + if ( ! prepared.data?.datasets ) { + prepared.data = { ...prepared.data, datasets: [] }; + } + + if ( null === prepared.data?.labels ) { + prepared.data = { ...prepared.data, labels: [] }; + } + + const options = { + ...prepared.options, + animation: { onComplete }, + }; + + // Only create the new chart if there isn't an existing one already + if ( ! existingInstance ) { + return new window.Chart( canvas, { + type: prepared.type, + data: prepared.data, + options, + } ); + } + + existingInstance.data = prepared.data; + existingInstance.config.type = prepared.type; + existingInstance.options = options; + + existingInstance.update(); + + return existingInstance; +} + +/** + * React-managed chart preview for the admin meta box + * + * The chart instance is managed imperatively via refs and is never recreated on re-render — only updated when chartArgs changes + * + * Rendering is delegated via the 'm_chart.render_chart' wp.hooks filter so library plugins can replace the default Chart.js renderer + * The filter receives ( canvas, args, onComplete, existingInstance ) as extra arguments + * If no filter handles rendering (i.e. returns false), Chart.js is used + * + * The onComplete callback must be called by the renderer once the chart has finished which will fire 'm_chart.render_done' to trigger image generation and/or re-enable the form + */ +export default function ChartPreview() { + const { state, dispatch } = useChartAdmin(); + const { postId, chartArgs, performance, imageSupport, postMeta } = state; + + const canvasRef = useRef( null ); + const chartRef = useRef( null ); + const renderFlagRef = useRef( false ); + const isFirstRender = useRef( true ); + + // Keep a ref so onComplete closures always see the latest values + const needsImagesRef = useRef( false ); + needsImagesRef.current = ( 'default' === performance && 'yes' === imageSupport ); + + const generateImage = useImageGeneration( chartRef ); + + // Cleanup — destroy chart instance on unmount + useEffect( () => { + return () => { + if ( chartRef.current ) { + chartRef.current.destroy(); + chartRef.current = null; + } + }; + }, [] ); + + // Create or update the chart instance whenever chartArgs changes + useEffect( () => { + if ( ! chartArgs || ! canvasRef.current ) { + return; + } + + function onComplete() { + // Only fire once per update cycle + if ( ! renderFlagRef.current ) { + return; + } + + renderFlagRef.current = false; + + if ( window.wp?.hooks ) { + window.wp.hooks.doAction( 'm_chart.render_done', postId, 1, chartRef.current ); + } + + if ( needsImagesRef.current ) { + generateImage(); + } else { + // No image generation — enable form submission immediately + // This also covers the initial page load where useChartRefresh skips its first run + dispatch( { type: 'SET_FORM_ENABLED', payload: true } ); + isFirstRender.current = false; + } + } + + renderFlagRef.current = true; + + // Allow library plugins to replace the renderer via wp.hooks + // Plugins hook 'm_chart.render_chart' and return their chart instance + // Returning false (the default) falls through to the built-in Chart.js renderer + let instance = false; + + if ( window.wp?.hooks ) { + // See defaultChartjsRender for the filter arguments + instance = window.wp.hooks.applyFilters( + 'm_chart.render_chart', + false, + canvasRef.current, + chartArgs, + onComplete, + chartRef.current + ); + } + + chartRef.current = ( false !== instance ) + ? instance + : defaultChartjsRender( canvasRef.current, chartArgs, onComplete, chartRef.current ); + + }, [ chartArgs ] ); // eslint-disable-line react-hooks/exhaustive-deps + + return ( +
+ +
+ ); +} diff --git a/components/admin-ui-src/components/ChartSettings.js b/components/admin-ui-src/components/ChartSettings.js new file mode 100644 index 0000000..36e2833 --- /dev/null +++ b/components/admin-ui-src/components/ChartSettings.js @@ -0,0 +1,34 @@ +import { useMemo } from '@wordpress/element'; +import TypeAndThemeRow from './TypeAndThemeRow'; +import ParseAndFlagsRow from './ParseAndFlagsRow'; +import AxisRows from './AxisRows'; +import ShortcodeAndImageRow from './ShortcodeAndImageRow'; + +function DefaultSettings() { + return ( + <> + + + + + + ); +} + +export default function ChartSettings() { + // Allow library plugins to replace the settings component via wp.hooks + // useMemo with [] ensures the filter runs once — filters are registered at load time, + // so calling applyFilters on every render would return a new function reference each + // time and cause React to unmount/remount the settings UI + const Settings = useMemo( () => { + return window.wp?.hooks + ? wp.hooks.applyFilters( 'm_chart.settings_component', DefaultSettings ) + : DefaultSettings; + }, [] ); + + return ( +
+ +
+ ); +} diff --git a/components/admin-ui-src/components/CsvControls.js b/components/admin-ui-src/components/CsvControls.js new file mode 100644 index 0000000..9c1a51c --- /dev/null +++ b/components/admin-ui-src/components/CsvControls.js @@ -0,0 +1,267 @@ +import { useState, useRef } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import { useChartAdmin } from '../context/ChartAdminContext'; +import { spreadsheetAutoWidth } from './JspreadsheetWrapper'; + +/** + * CSV import and export controls for the active spreadsheet sheet + * + * Import uses fetch + FormData (replaces the hidden #m-chart-csv-import-form) + * Export uses a dynamically-created temporary form POST to trigger a file download (replaces the hidden #m-chart-csv-export-form) + * + * Props: + * getActiveWorksheet {Function} Returns the active Jspreadsheet worksheet instance + */ +export default function CsvControls( { getActiveWorksheet } ) { + const { state, dispatch } = useChartAdmin(); + const { + postId, + nonce, + ajaxUrl, + setNames, + activeSheet, + csvDelimiters, + defaultDelimiter, + } = state; + + const [ selectedFile, setSelectedFile ] = useState( null ); + const [ csvDelimiter, setCsvDelimiter ] = useState( defaultDelimiter ); + const [ fileError, setFileError ] = useState( false ); + const [ importError, setImportError ] = useState( '' ); + const [ isImporting, setIsImporting ] = useState( false ); + + const fileInputRef = useRef( null ); + + function handleSelectFile( e ) { + e.preventDefault(); + + setFileError( false ); + setImportError( '' ); + + fileInputRef.current?.click(); + } + + function handleFileChange( e ) { + const file = e.target.files[ 0 ]; + + // Make sure it's a CSV file + if ( ! file || ! /\.csv$/i.test( file.name ) ) { + setFileError( true ); + setSelectedFile( null ); + return; + } + + setFileError( false ); + setSelectedFile( file ); + } + + function handleCancel( e ) { + e.preventDefault(); + + setSelectedFile( null ); + + // We're hiding the actual file input so we need to reset it for the user + if ( fileInputRef.current ) { + fileInputRef.current.value = ''; + } + } + + async function handleImport( e ) { + e.preventDefault(); + + if ( ! selectedFile ) { + return; + } + + // Save the file value so we can reset the iput + const file = selectedFile; + + // Set the UI to show we're importing the file + setSelectedFile( null ); + setIsImporting( true ); + setImportError( '' ); + + // Reset the actual file input back to empty + if ( fileInputRef.current ) { + fileInputRef.current.value = ''; + } + + // Create a form data object so we can submit it to the endpoint + const formData = new FormData(); + + formData.append( 'import_csv_file', file ); + formData.append( 'post_id', postId ); + formData.append( 'csv_delimiter', csvDelimiter ); + formData.append( 'nonce', nonce ); + + // Try submitting the data to the endpoint + try { + const response = await fetch( `${ ajaxUrl }?action=m_chart_import_csv`, { + method: 'POST', + body: formData, + } ); + + const json = await response.json(); + + if ( ! json.success ) { + setImportError( json.data || __( 'Import failed', 'm-chart' ) ); + return; + } + + // Get the active worksheet + const worksheet = getActiveWorksheet(); + + if ( worksheet ) { + // Set the active worksheet to the new data + worksheet.setData( json.data ); + + // setData() does not trigger onafterchanges so we need to run spreadsheetAutoWidth ourselves + spreadsheetAutoWidth( worksheet ); + + dispatch( { + type: 'SET_SHEET_DATA', + payload: { index: activeSheet, data: worksheet.getData() }, + } ); + } + } catch ( err ) { + setImportError( sprintf( __( 'Import error: %s', 'm-chart' ), err.message ) ); + } finally { + // When we're done reset everything in the CSV ui back to default + setIsImporting( false ); + } + } + + function handleExport( e ) { + e.preventDefault(); + + // Get the active worksheet + const worksheet = getActiveWorksheet(); + + if ( ! worksheet ) { + return; + } + + const data = worksheet.getData(); + const title = document.getElementById( 'title' )?.value || ''; + const setName = setNames[ activeSheet ] || ''; + + // Build a FormData object so we can submit it to the endpoint + const formData = new FormData(); + + formData.append( 'post_id', postId ); + formData.append( 'data', JSON.stringify( data ) ); + formData.append( 'title', title ); + formData.append( 'set_name', setName ); + + // Create a temporary form and submit it + // We have to do it this way to trigger a download + const form = document.createElement( 'form' ); + form.action = `${ ajaxUrl }?action=m_chart_export_csv`; + form.method = 'post'; + form.style.display = 'none'; + + // Loop through the formData and append it to the temporary form + for ( const [ name, value ] of formData.entries() ) { + const input = document.createElement( 'input' ); + input.type = 'hidden'; + input.name = name; + input.value = value; + form.appendChild( input ); + } + + // Do the thing + document.body.appendChild( form ); + form.submit(); + document.body.removeChild( form ); + } + + const showConfirmation = selectedFile && ! isImporting; + + return ( +
+ +
+ { __( 'CSV Import/Export', 'm-chart' ) }
+
+ { /* Hidden native file input — triggered programmatically */ } + + { /* Select File button — shown when no file is selected */ } + { ! showConfirmation && ! isImporting && ( + + { __( 'Select File', 'm-chart' ) } + + ) } + { /* Confirmation row: Import button + delimiter select */ } + { showConfirmation && ( +
+ + { __( 'Import', 'm-chart' ) } + + +
+ ) } + { fileError && ( +

{ __( 'You can only import CSV files', 'm-chart' ) }

+ ) } + { importError && ( +

{ importError }

+ ) } + { isImporting && ( +

{ __( 'Importing file', 'm-chart' ) }

+ ) } + { /* File info + cancel — shown while a file is selected */ } + { showConfirmation && ( + + ) } +
+
+
+ ); +} diff --git a/components/admin-ui-src/components/JspreadsheetWrapper.js b/components/admin-ui-src/components/JspreadsheetWrapper.js new file mode 100644 index 0000000..c233d9c --- /dev/null +++ b/components/admin-ui-src/components/JspreadsheetWrapper.js @@ -0,0 +1,141 @@ +import { useEffect, useRef } from '@wordpress/element'; +import { useChartAdmin } from '../context/ChartAdminContext'; + +// Jspreadsheet CE has a bunch of default menu items this is the list of the ones we actually want +const CONTEXT_MENU_ITEMS = [ + 'Insert a new row before', + 'Insert a new row after', + 'Delete selected rows', + 'Insert a new column before', + 'Insert a new column after', + 'Delete selected columns', +]; + +/** + * Resizes columns to fit their content using canvas-based text measurement + * + * @param {object} worksheet Jspreadsheet CE worksheet instance + * @param {Array} [records] Subset of changed records; omit for a full refresh + */ +export function spreadsheetAutoWidth( worksheet, records = false ) { + // If no records to refresh were passed we'll just do all of them + if ( ! records ) { + records = worksheet.records[ 0 ]; + } + + // If there are no records even after the above we stop here + if ( ! records || ! records.length ) { + return; + } + + const columns = [ ...new Set( records.map( ( r ) => r.x ) ) ]; + const canvas = document.createElement( 'canvas' ); + const context = canvas.getContext( '2d' ); + + columns.forEach( ( column ) => { + let maxWidth = 0; + const padding = 13; + const minWidth = 100 - padding; + + for ( let i = 0; i < worksheet.records.length; i++ ) { + const cell = worksheet.records[ i ]?.[ column ]?.element; + + if ( cell ) { + context.font = window.getComputedStyle( cell ).font; + const metrics = context.measureText( cell.innerText ); + + if ( metrics.width > maxWidth ) { + maxWidth = metrics.width; + } + } + } + + maxWidth = minWidth > maxWidth ? minWidth : maxWidth; + + worksheet.setWidth( column, maxWidth + padding ); + } ); +} + +/** + * Thin React wrapper around a Jspreadsheet CE worksheet + * + * The Jspreadsheet instance is created once on mount and never recreated on re-render + * Show/hide between active/inactive sheets is done via CSS so that DOM state and undo history are preserved + * + * Props: + * sheetId {number} Stable identity key (used for registration) + * sheetIndex {number} Current position in the sheets array (may change after deletes) + * isActive {boolean} Whether this sheet is currently displayed + * data {Array} Initial 2-D array of cell values + * onMounted {Function} Called with (sheetId, worksheetInstance) after init + * onUnmounted {Function} Called with (sheetId) before unmount + */ +export default function JspreadsheetWrapper( { + sheetId, + sheetIndex, + isActive, + data, + onMounted, + onUnmounted, +} ) { + const { dispatch } = useChartAdmin(); + const containerRef = useRef( null ); + const worksheetRef = useRef( null ); + + // Keep a ref so the onafterchanges closure always dispatches the current index + const sheetIndexRef = useRef( sheetIndex ); + sheetIndexRef.current = sheetIndex; + + useEffect( () => { + if ( ! containerRef.current || worksheetRef.current ) { + return; + } + + // Need to load an empty data array if there's none to start already + const initialData = data && data.length ? data : [ [ '' ] ]; + + // Create the sheet instance + const instance = window.jspreadsheet( containerRef.current, { + worksheets: [ { + data: initialData, + allowComments: false, + minDimensions: [ 37, 17 ], + } ], + // Filter out all of the contextual menu items we don't want + contextMenu( obj, x, y, e, items ) { + return items.filter( ( item ) => + CONTEXT_MENU_ITEMS.includes( item.title ) + ); + }, + // Run spreadsheetAutoWidth on the intiial load + onload( spreadsheet ) { + const ws = spreadsheet.worksheets[ spreadsheet.getWorksheetActive() ]; + spreadsheetAutoWidth( ws ); + }, + // Run spreadsheetAutoWidth on changed recrds and also push any changes to the chart + onafterchanges( worksheet, records ) { + spreadsheetAutoWidth( worksheet, records ); + dispatch( { + type: 'SET_SHEET_DATA', + payload: { index: sheetIndexRef.current, data: worksheet.getData() }, + } ); + }, + } ); + + worksheetRef.current = instance[ 0 ]; + onMounted( sheetId, worksheetRef.current ); + + return () => { + onUnmounted( sheetId ); + worksheetRef.current = null; + }; + }, [] ); // eslint-disable-line react-hooks/exhaustive-deps + + return ( +
+ ); +} diff --git a/components/admin-ui-src/components/ParseAndFlagsRow.js b/components/admin-ui-src/components/ParseAndFlagsRow.js new file mode 100644 index 0000000..adfd4b7 --- /dev/null +++ b/components/admin-ui-src/components/ParseAndFlagsRow.js @@ -0,0 +1,93 @@ +import { __ } from '@wordpress/i18n'; +import { useChartAdmin } from '../context/ChartAdminContext'; + +const PARSE_OPTION_NAMES = { + columns: __( 'Columns', 'm-chart' ), + rows: __( 'Rows', 'm-chart' ), +}; + +// Chart types that support the shared tooltip option +const SHARED_TYPES = new Set( [ + 'line', + 'spline', + 'area', + 'radar', + 'radar-area' +] ); + +export default function ParseAndFlagsRow() { + const { state, dispatch } = useChartAdmin(); + const { postMeta } = state; + + const showShared = SHARED_TYPES.has( postMeta.type ); + + function handleChange( field, value ) { + dispatch( { type: 'SET_POST_META', payload: { [ field ]: value } } ); + } + + function handleCheckbox( field, checked ) { + dispatch( { type: 'SET_POST_META', payload: { [ field ]: checked } } ); + } + + return ( +
+

+
+ +

+

+ { '\u00a0' }
+ +

+

+ { '\u00a0' }
+ +

+ { /* Always render shared in DOM so its value survives type switches on save */ } +

+ { '\u00a0' }
+ +

+
+ ); +} diff --git a/components/admin-ui-src/components/SheetTab.js b/components/admin-ui-src/components/SheetTab.js new file mode 100644 index 0000000..4355f01 --- /dev/null +++ b/components/admin-ui-src/components/SheetTab.js @@ -0,0 +1,154 @@ +import { useState, useEffect, useRef } from '@wordpress/element'; +import { useChartAdmin } from '../context/ChartAdminContext'; +import { useLongPress } from '../hooks/useLongPress'; +import { measureTextWidth } from '../utils/measureTextWidth'; + +/** + * A single sheet tab in the spreadsheet tab bar + * + * Supports: + * - Click to activate + * - Double-click or long-press (500ms) to enter rename mode + * - Enter / blur to commit rename + * - Dismiss icon to delete (guarded by window.confirm) + */ +export default function SheetTab( { + sheetId, + sheetIndex, + name, + isActive, + isSingle, + isNew, +} ) { + const { state, dispatch } = useChartAdmin(); + const [ isRenaming, setIsRenaming ] = useState( () => !! isNew ); + const [ inputValue, setInputValue ] = useState( name ); + const inputRef = useRef( null ); + + const longPress = useLongPress( () => setIsRenaming( true ) ); + + // Clear the newSheetId flag once this tab has consumed it + useEffect( () => { + if ( isNew ) { + dispatch( { type: 'CLEAR_NEW_SHEET_ID' } ); + } + }, [] ); // eslint-disable-line react-hooks/exhaustive-deps + + // Sync local input value and focus when entering rename mode + useEffect( () => { + if ( isRenaming ) { + setInputValue( name ); + + if ( inputRef.current ) { + inputRef.current.focus(); + inputRef.current.select(); + } + } + }, [ isRenaming ] ); // eslint-disable-line react-hooks/exhaustive-deps + + function handleClick( e ) { + e.preventDefault(); + + if ( ! isActive ) { + dispatch( { type: 'SET_ACTIVE_SHEET', payload: sheetIndex } ); + } + } + + function handleDoubleClick( e ) { + e.preventDefault(); + + setIsRenaming( true ); + } + + function handleDelete( e ) { + e.preventDefault(); + e.stopPropagation(); + + // If there's only one tab we don't let the user delete it + if ( isSingle ) { + return; + } + + // If user rejects teh confirmation we stop + if ( ! window.confirm( state.deleteConfirm ) ) { + return; + } + + // Activate a neighbouring sheet before deleting so the active index stays valid. + if ( isActive ) { + const newActive = sheetIndex > 0 ? sheetIndex - 1 : 1; + + dispatch( { type: 'SET_ACTIVE_SHEET', payload: newActive } ); + } + + dispatch( { type: 'DELETE_SHEET', payload: { index: sheetIndex } } ); + } + + function handleNameChange( e ) { + setInputValue( e.target.value ); + } + + function commitRename() { + dispatch( { + type: 'RENAME_SHEET', + payload: { index: sheetIndex, name: inputValue }, + } ); + + setIsRenaming( false ); + } + + function handleKeyDown( e ) { + if ( e.key === 'Enter' ) { + e.preventDefault(); + commitRename(); + } + } + + const inputWidth = inputRef.current + ? measureTextWidth( inputValue, inputRef.current ) + 'px' + : Math.max( 40, inputValue.length * 8 + 16 ) + 'px'; + + const className = [ + 'nav-tab', + isActive ? 'nav-tab-active' : '', + isSingle ? 'do-not-delete' : '', + ].filter( Boolean ).join( ' ' ); + + return ( +
+ { ! isSingle && ( + + ) } + + { name } + + + + ); +} diff --git a/components/admin-ui-src/components/SheetTabs.js b/components/admin-ui-src/components/SheetTabs.js new file mode 100644 index 0000000..be0dfcd --- /dev/null +++ b/components/admin-ui-src/components/SheetTabs.js @@ -0,0 +1,67 @@ +import { useMemo } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import { useChartAdmin } from '../context/ChartAdminContext'; +import SheetTab from './SheetTab'; + +/** + * Chart types that support multiple data sets (multiple spreadsheet sheets) + * All other types use a single sheet and the tab bar is hidden + */ +const MULTI_SHEET_TYPES = new Set( [ + 'scatter', + 'bubble', + 'radar', + 'radar-area', +] ); + +/** + * The spreadsheet tab bar + * Renders one SheetTab per sheet and an Add Sheet button + * The entire bar is hidden when the current chart type only supports a single data set + */ +export default function SheetTabs() { + const { state, dispatch } = useChartAdmin(); + const { postMeta, sheetIds, setNames, activeSheet, newSheetId } = state; + + // Allow library plugins to customize which chart types support multiple sheets + // useMemo with [] ensures the filter runs once — filters are registered at load time + // and the resulting Set is stable, so showTabs only re-evaluates when postMeta.type changes + const multiSheetTypes = useMemo( () => { + const types = window.wp?.hooks + ? wp.hooks.applyFilters( 'm_chart.multi_sheet_types', [ ...MULTI_SHEET_TYPES ] ) + : [ ...MULTI_SHEET_TYPES ]; + return new Set( types ); + }, [] ); + + const showTabs = multiSheetTypes.has( postMeta.type ); + + function handleAddSheet( e ) { + e.preventDefault(); + + dispatch( { type: 'ADD_SHEET', payload: {} } ); + } + + return ( + + ); +} diff --git a/components/admin-ui-src/components/ShortcodeAndImageRow.js b/components/admin-ui-src/components/ShortcodeAndImageRow.js new file mode 100644 index 0000000..5dd942c --- /dev/null +++ b/components/admin-ui-src/components/ShortcodeAndImageRow.js @@ -0,0 +1,73 @@ +import { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { useChartAdmin } from '../context/ChartAdminContext'; + +export default function ShortcodeAndImageRow() { + const { state } = useChartAdmin(); + const { postId, postMeta, imageUrl, performance, imageSupport } = state; + + const shortcode = `[chart id="${ postId }"]`; + + const showImageField = 'default' === performance && 'yes' === imageSupport; + const imageDisabled = ! showImageField; + + const [ copied, setCopied ] = useState( false ); + + function handleCopy() { + navigator.clipboard.writeText( shortcode ).then( () => { + setCopied( true ); + setTimeout( () => setCopied( false ), 2000 ); + } ); + } + + return ( +
+

+
+ e.target.select() } + readOnly + /> + +

+

+
+ { imageUrl ? ( + <> + e.target.select() } + readOnly + /> + + { __( 'View', 'm-chart' ) } + + + ) : imageDisabled ? ( + { __( 'Image generation is disabled', 'm-chart' ) } + ) : ( + { __( 'Save/Update this post to generate the image version', 'm-chart' ) } + ) } +

+ +
+ ); +} diff --git a/components/admin-ui-src/components/SpreadsheetMetaBox.js b/components/admin-ui-src/components/SpreadsheetMetaBox.js new file mode 100644 index 0000000..058c419 --- /dev/null +++ b/components/admin-ui-src/components/SpreadsheetMetaBox.js @@ -0,0 +1,156 @@ +import { useEffect, useRef, useCallback } from '@wordpress/element'; +import { useChartAdmin } from '../context/ChartAdminContext'; +import JspreadsheetWrapper from './JspreadsheetWrapper'; +import SheetTabs from './SheetTabs'; +import CsvControls from './CsvControls'; + +// WordPress submit button IDs that should trigger save behavior +const SUBMIT_BUTTON_IDS = [ 'publish', 'save-post' ]; + +/** + * Container for the spreadsheet meta box + * + * Manages Jspreadsheet worksheet instances via a ref map keyed by stable sheet ID + * Handles form submission: writes all sheet data to the hidden textarea[name="m-chart[data]"] before the post form is submitted + */ +export default function SpreadsheetMetaBox() { + const { state, dispatch } = useChartAdmin(); + const { sheetIds, spreadsheetData, activeSheet, formEnabled, pendingSubmit } = state; + + // Map of stable sheetId → worksheet instance (Jspreadsheet worksheet object) + const worksheetInstances = useRef( {} ); + + // Refs so event handlers always see the latest values without needing to be recreated + const formEnabledRef = useRef( formEnabled ); + const sheetIdsRef = useRef( sheetIds ); + + formEnabledRef.current = formEnabled; + sheetIdsRef.current = sheetIds; + + // Called by JspreadsheetWrapper after it initialises its jspreadsheet instance + const handleMounted = useCallback( ( sheetId, worksheet ) => { + worksheetInstances.current[ sheetId ] = worksheet; + }, [] ); + + // Called by JspreadsheetWrapper just before it unmounts + const handleUnmounted = useCallback( ( sheetId ) => { + delete worksheetInstances.current[ sheetId ]; + }, [] ); + + // Returns the worksheet instance for the currently active sheet + const getActiveWorksheet = useCallback( () => { + const activeId = sheetIdsRef.current[ state.activeSheet ]; + return worksheetInstances.current[ activeId ] ?? null; + }, [ state.activeSheet ] ); + + // Writes all sheet data to the hidden textarea so the form POST includes it + const writeDataToForm = useCallback( () => { + const form = document.getElementById( 'post' ); + + if ( ! form ) { + return; + } + + const allData = sheetIdsRef.current.map( + ( id ) => worksheetInstances.current[ id ]?.getData() ?? [ [ '' ] ] + ); + + const dataTextarea = form.querySelector( 'textarea[name="m-chart[data]"]' ); + + if ( dataTextarea ) { + dataTextarea.value = JSON.stringify( allData ); + } + }, [] ); + + // When formEnabled becomes true while a submit is pending, submit the form + // Uses form.submit() to bypass event handlers since the data textarea is already written + useEffect( () => { + if ( formEnabled && pendingSubmit ) { + dispatch( { type: 'SET_PENDING_SUBMIT', payload: false } ); + + // Write latest data right before submitting + writeDataToForm(); + + const form = document.getElementById( 'post' ); + + if ( form ) { + form.submit(); + } + } + }, [ formEnabled, pendingSubmit, dispatch, writeDataToForm ] ); + + // Detect submit intent at the click event on WP submit buttons + // Click fires AFTER blur but BEFORE the form's submit event + // This is the earliest reliable detection point + useEffect( () => { + function handleClick( e ) { + // If the chart is still refreshing, intercept the click to defer submission + // The form submit event hasn't fired yet so we prevent the default click behavior + if ( ! formEnabledRef.current ) { + e.preventDefault(); + writeDataToForm(); + dispatch( { type: 'SET_PENDING_SUBMIT', payload: true } ); + return; + } + + // Form is ready — write data and let the normal submit flow proceed + writeDataToForm(); + } + + const buttons = SUBMIT_BUTTON_IDS + .map( ( id ) => document.getElementById( id ) ) + .filter( Boolean ); + + buttons.forEach( ( btn ) => btn.addEventListener( 'click', handleClick ) ); + + return () => { + buttons.forEach( ( btn ) => btn.removeEventListener( 'click', handleClick ) ); + }; + }, [ dispatch, writeDataToForm ] ); + + // Intercept the form submit event as a fallback + // Ensures the data textarea is always written before submission regardless of how + // the submit was triggered (keyboard, other plugins, etc) + useEffect( () => { + const form = document.getElementById( 'post' ); + + if ( ! form ) { + return; + } + + function handleSubmit( e ) { + // If chart is still refreshing, block this submit — the click handler + // already set pendingSubmit so it will auto-submit when ready + if ( ! formEnabledRef.current ) { + e.preventDefault(); + return; + } + + // Write data in case the submit wasn't triggered via our click handler + writeDataToForm(); + } + + form.addEventListener( 'submit', handleSubmit ); + return () => form.removeEventListener( 'submit', handleSubmit ); + }, [ writeDataToForm ] ); + + return ( + <> + +
+ { sheetIds.map( ( id, index ) => ( + + ) ) } +
+ + + ); +} diff --git a/components/admin-ui-src/components/SubtitleField.js b/components/admin-ui-src/components/SubtitleField.js new file mode 100644 index 0000000..676d11c --- /dev/null +++ b/components/admin-ui-src/components/SubtitleField.js @@ -0,0 +1,27 @@ +import { __ } from '@wordpress/i18n'; +import { useChartAdmin } from '../context/ChartAdminContext'; + +/** + * Reach controlled subtitle input + * + * Renders with the m-chart[subtitle] name attribute so the value is submitted with the native WordPress post form and the existing PHP save logic still works + */ +export default function SubtitleField() { + const { state, dispatch } = useChartAdmin(); + const value = state.postMeta?.subtitle ?? ''; + + return ( + + dispatch( { type: 'SET_SUBTITLE', payload: e.target.value } ) + } + /> + ); +} diff --git a/components/admin-ui-src/components/TypeAndThemeRow.js b/components/admin-ui-src/components/TypeAndThemeRow.js new file mode 100644 index 0000000..111abe9 --- /dev/null +++ b/components/admin-ui-src/components/TypeAndThemeRow.js @@ -0,0 +1,70 @@ +import { useState, useCallback } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { useChartAdmin } from '../context/ChartAdminContext'; +import { measureTextWidth } from '../utils/measureTextWidth'; + +export default function TypeAndThemeRow() { + const { state, dispatch } = useChartAdmin(); + const { postMeta, typeOptions, typeOptionNames, themes } = state; + + const [ heightEl, setHeightEl ] = useState( null ); + const heightRef = useCallback( node => setHeightEl( node ), [] ); + const heightValue = String( postMeta.height ?? '' ); + const heightWidth = heightEl + ? ( measureTextWidth( heightValue, heightEl ) + 20 ) + 'px' + : '73px'; + + function handleChange( field, value ) { + dispatch( { type: 'SET_POST_META', payload: { [ field ]: value } } ); + } + + return ( +
+

+
+ +

+

+
+ +

+

+
+ handleChange( 'height', e.target.value ) } + style={ { width: heightWidth, minWidth: 0 } } + /> +

+
+ ); +} diff --git a/components/admin-ui-src/context/ChartAdminContext.js b/components/admin-ui-src/context/ChartAdminContext.js new file mode 100644 index 0000000..2549b8a --- /dev/null +++ b/components/admin-ui-src/context/ChartAdminContext.js @@ -0,0 +1,190 @@ +import { createContext, useContext, useReducer } from '@wordpress/element'; + +const ChartAdminStateContext = createContext( null ); +const ChartAdminDispatchContext = createContext( null ); + +const { m_chart_admin } = window; + +/** + * Initial state populated from the PHP-localised window.m_chart_admin object + * + * post_meta contains all chart meta fields except 'data' (spreadsheetData holds that) + * Fields mirror the PHP $chart_meta_fields defaults in class-m-chart.php + */ + +// Stable sheet IDs — never change once a sheet is created, survive deletion of siblings +const initialSheetCount = ( m_chart_admin.spreadsheet_data || [ [] ] ).length; +const initialSheetIds = Array.from( { length: initialSheetCount }, ( _, i ) => i ); + +const initialState = { + slug: m_chart_admin.slug, + postId: m_chart_admin.post_id, + nonce: m_chart_admin.nonce, + ajaxUrl: m_chart_admin.ajax_url, + library: m_chart_admin.library, + performance: m_chart_admin.performance, + imageSupport: m_chart_admin.image_support, + instantPreview: m_chart_admin.instant_preview_support, + imageMultiplier: m_chart_admin.image_multiplier, + imageWidth: m_chart_admin.image_width, + deleteConfirm: m_chart_admin.delete_confirm, + csvDelimiters: m_chart_admin.csv_delimiters || { ',': 'Comma' }, + defaultDelimiter: m_chart_admin.default_delimiter || ',', + + // Chart meta fields (type, theme, height, parse_in, labels, legend, etc.) + postMeta: m_chart_admin.post_meta, + + // Array of 2D arrays, one per sheet (matches Jspreadsheet's getData() format) + spreadsheetData: m_chart_admin.spreadsheet_data, + + // Array of human-readable names for each sheet tab + setNames: m_chart_admin.set_names || [], + + // Stable IDs for each sheet — used as React keys to avoid spurious remounts + sheetIds: initialSheetIds, + + // Counter for the next sheet ID to assign + nextSheetId: initialSheetCount, + + // ID of the most recently added sheet — SheetTab uses this to auto-enter rename mode + newSheetId: null, + + // Index of the currently active sheet tab + activeSheet: 0, + + // Chart.js args object — seeded from PHP on load, updated by useChartRefresh + chartArgs: m_chart_admin.chart_args || null, + + // True while waiting for ajax_get_chart_args to respond + isRefreshing: false, + + // Controls whether the WordPress Save/Publish buttons are enabled + formEnabled: false, + + // True when the user clicked Save/Update while the chart was still refreshing + pendingSubmit: false, + + // Static config from PHP — library-specific options for the settings form + typeOptions: m_chart_admin.type_options || [], + typeOptionNames: m_chart_admin.type_option_names || {}, + themes: m_chart_admin.themes || [], + unitTerms: m_chart_admin.unit_terms || [], + imageUrl: m_chart_admin.image_url || '', +}; + +function reducer( state, action ) { + switch ( action.type ) { + case 'SET_POST_META': + return { + ...state, + postMeta: { ...state.postMeta, ...action.payload }, + }; + + case 'SET_SHEET_DATA': { + const spreadsheetData = [ ...state.spreadsheetData ]; + spreadsheetData[ action.payload.index ] = action.payload.data; + // Disable form immediately so a submit during the refresh window is gated + return { ...state, spreadsheetData, formEnabled: false }; + } + + case 'ADD_SHEET': { + const setNames = [ ...state.setNames, action.payload.name || '' ]; + const spreadsheetData = [ ...state.spreadsheetData, [ [ '' ] ] ]; + const sheetIds = [ ...state.sheetIds, state.nextSheetId ]; + return { + ...state, + setNames, + spreadsheetData, + sheetIds, + nextSheetId: state.nextSheetId + 1, + activeSheet: spreadsheetData.length - 1, + newSheetId: state.nextSheetId, + }; + } + + case 'CLEAR_NEW_SHEET_ID': + return { ...state, newSheetId: null }; + + case 'DELETE_SHEET': { + if ( state.spreadsheetData.length <= 1 ) { + return state; + } + const idx = action.payload.index; + const spreadsheetData = state.spreadsheetData.filter( ( _, i ) => i !== idx ); + const setNames = state.setNames.filter( ( _, i ) => i !== idx ); + const sheetIds = state.sheetIds.filter( ( _, i ) => i !== idx ); + const activeSheet = Math.min( state.activeSheet, spreadsheetData.length - 1 ); + return { ...state, spreadsheetData, setNames, sheetIds, activeSheet }; + } + + case 'RENAME_SHEET': { + const setNames = [ ...state.setNames ]; + setNames[ action.payload.index ] = action.payload.name; + return { ...state, setNames }; + } + + case 'SET_ACTIVE_SHEET': + return { ...state, activeSheet: action.payload }; + + case 'SET_CHART_ARGS': + return { ...state, chartArgs: action.payload }; + + case 'SET_REFRESHING': + return { ...state, isRefreshing: action.payload }; + + case 'SET_FORM_ENABLED': + return { ...state, formEnabled: action.payload }; + + case 'SET_PENDING_SUBMIT': + return { ...state, pendingSubmit: action.payload }; + + case 'SET_SUBTITLE': + return { + ...state, + postMeta: { ...state.postMeta, subtitle: action.payload }, + }; + + default: + return state; + } +} + +export function ChartAdminProvider( { children } ) { + const [ state, dispatch ] = useReducer( reducer, initialState ); + + return ( + + + { children } + + + ); +} + +/** + * Convenience hook — returns { state, dispatch } from the nearest provider + */ +export function useChartAdmin() { + const state = useContext( ChartAdminStateContext ); + const dispatch = useContext( ChartAdminDispatchContext ); + + if ( ! state ) { + throw new Error( 'useChartAdmin must be used within a ChartAdminProvider' ); + } + + return { state, dispatch }; +} + +/** + * Dispatch-only hook — subscribes only to the dispatch context + * Components using this hook will not re-render on state changes + */ +export function useChartDispatch() { + const dispatch = useContext( ChartAdminDispatchContext ); + + if ( ! dispatch ) { + throw new Error( 'useChartDispatch must be used within a ChartAdminProvider' ); + } + + return dispatch; +} diff --git a/components/admin-ui-src/hooks/useChartRefresh.js b/components/admin-ui-src/hooks/useChartRefresh.js new file mode 100644 index 0000000..9b7ad25 --- /dev/null +++ b/components/admin-ui-src/hooks/useChartRefresh.js @@ -0,0 +1,132 @@ +import { useEffect, useRef } from '@wordpress/element'; +import { useChartAdmin } from '../context/ChartAdminContext'; + +/** + * Fires an AJAX request to get updated chart args whenever postMeta, spreadsheetData, setNames, or title changes + * We pass title as a parameter because it's core WP and not present in the React environment + * + * @param {string} title The current post title (read from #title DOM input). + */ +export function useChartRefresh( title ) { + const { state, dispatch } = useChartAdmin(); + const { + postId, nonce, ajaxUrl, library, + postMeta, spreadsheetData, setNames, + performance, imageSupport, + chartArgs, + } = state; + + const timerRef = useRef( null ); + const abortRef = useRef( null ); + const isFirstRun = useRef( true ); + + // Keep a ref to the values that aren't in the effect deps so the async callback + // always reads the latest version without needing them in the deps array + const latestRef = useRef( null ); + latestRef.current = { postId, nonce, ajaxUrl, library, performance, imageSupport }; + + useEffect( () => { + // On first run we want to skip rendering since the chart will already be rendered + // But only if it's not a brand new chart (chartArgs being null indcates chart is new) + if ( isFirstRun.current && null !== chartArgs ) { + isFirstRun.current = false; + + return; + } + + // Cancel any pending debounce + if ( timerRef.current ) { + clearTimeout( timerRef.current ); + } + + timerRef.current = setTimeout( async () => { + // This should cancel any currently running requests so only the most recent request is run + if ( abortRef.current ) { + abortRef.current.abort(); + } + + abortRef.current = new AbortController(); + + // Read from the ref so the async body always has the latest values even if + // the component re-rendered between when the timeout was scheduled and when it fires + const { postId, nonce, ajaxUrl, library, performance, imageSupport } = latestRef.current; + + dispatch( { type: 'SET_REFRESHING', payload: true } ); + dispatch( { type: 'SET_FORM_ENABLED', payload: false } ); + + try { + // Start buidling the values we'll send to the m_chart_get_chart_args endpoint + const body = new URLSearchParams(); + body.append( 'post_id', postId ); + body.append( 'nonce', nonce ); + body.append( 'library', library ); + body.append( 'title', title || '' ); + + // Build post_meta matching the format the m_chart_get_chart_args expects + // Exclude set_names since it is sent separately as indexed PHP array values + const meta = { ...postMeta }; + delete meta.set_names; + meta.data = JSON.stringify( spreadsheetData ); + + Object.entries( meta ).forEach( ( [ key, val ] ) => { + let serialized; + + if ( typeof val === 'boolean' ) { + // PHP's (boolean) cast treats any non-empty string as true, including "false" + // Use 1/0 so unchecked checkboxes are correctly read as false + serialized = val ? '1' : '0'; + } else if ( typeof val === 'object' && val !== null ) { + serialized = JSON.stringify( val ); + } else { + serialized = val ?? ''; + } + + body.append( `post_meta[${ key }]`, serialized ); + } ); + + // set_names must arrive in PHP as an array, not a JSON string + // Sending post_meta[set_names][0], [1], … lets PHP parse it as an array + ( setNames || [] ).forEach( ( name, i ) => { + body.append( `post_meta[set_names][${ i }]`, name ); + } ); + + // Make the actual request to the endpoint + const response = await fetch( ajaxUrl + '?action=m_chart_get_chart_args', { + method: 'POST', + body, + signal: abortRef.current.signal, + } ); + + const json = await response.json(); + + // If the request succeeded we dispatch the returned data nd then trigger the m_chart.chart_args_success hook and pass it the new data and postId + if ( json.success ) { + dispatch( { type: 'SET_CHART_ARGS', payload: json.data } ); + + if ( window.wp && window.wp.hooks ) { + window.wp.hooks.doAction( 'm_chart.chart_args_success', json.data, postId ); + } + + // If no image generation is needed, enable the form now + // Otherwise ChartPreview's animation.onComplete enables it after capture + if ( 'default' !== performance || 'yes' !== imageSupport ) { + dispatch( { type: 'SET_FORM_ENABLED', payload: true } ); + } + } + } catch ( err ) { + if ( err.name !== 'AbortError' ) { + // eslint-disable-next-line no-console + console.error( 'm-chart: chart refresh failed', err ); + } + } finally { + dispatch( { type: 'SET_REFRESHING', payload: false } ); + } + }, 300 ); + + return () => { + if ( timerRef.current ) { + clearTimeout( timerRef.current ); + } + }; + }, [ postMeta, spreadsheetData, setNames, title ] ); +} diff --git a/components/admin-ui-src/hooks/useFormSubmissionGuard.js b/components/admin-ui-src/hooks/useFormSubmissionGuard.js new file mode 100644 index 0000000..3221e4e --- /dev/null +++ b/components/admin-ui-src/hooks/useFormSubmissionGuard.js @@ -0,0 +1,50 @@ +import { useEffect } from '@wordpress/element'; +import { useChartAdmin } from '../context/ChartAdminContext'; + +const BUTTON_IDS = [ 'save-post', 'wp-preview', 'post-preview', 'publish' ]; + +/** + * Keeps the WordPress Save/Publish buttons and form submission gated on state.formEnabled + * + * When formEnabled is false: + * - Adds the 'disabled' class to the WP submit buttons + * - Blocks form submission via a submit event listener + * + * When formEnabled is true: + * - Removes the 'disabled' class from those buttons + * - Allows form submission through normally + */ +export function useFormSubmissionGuard() { + const { state } = useChartAdmin(); + const { formEnabled } = state; + + // Toggle disabled class on WP buttons + useEffect( () => { + BUTTON_IDS.forEach( ( id ) => { + const el = document.getElementById( id ); + + if ( el ) { + el.classList.toggle( 'disabled', ! formEnabled ); + } + } ); + }, [ formEnabled ] ); + + // Block form submission when not ready + useEffect( () => { + const form = document.getElementById( 'post' ); + + if ( ! form ) { + return; + } + + function handleSubmit( e ) { + if ( ! formEnabled ) { + e.preventDefault(); + } + } + + form.addEventListener( 'submit', handleSubmit ); + + return () => form.removeEventListener( 'submit', handleSubmit ); + }, [ formEnabled ] ); +} diff --git a/components/admin-ui-src/hooks/useImageGeneration.js b/components/admin-ui-src/hooks/useImageGeneration.js new file mode 100644 index 0000000..7dc95d9 --- /dev/null +++ b/components/admin-ui-src/hooks/useImageGeneration.js @@ -0,0 +1,70 @@ +import { useCallback, useRef } from '@wordpress/element'; +import { useChartAdmin } from '../context/ChartAdminContext'; + +/** + * Returns a stable `generateImage` callback that captures the current Chart.js instance as a PNG, writes it to the hidden img textarea, then re-enables the form + * + * @param {React.MutableRefObject} chartRef Ref holding the Chart.js instance + */ +export function useImageGeneration( chartRef ) { + const { state, dispatch } = useChartAdmin(); + + // Keep a ref so the callback always sees the latest state without being recreated + const stateRef = useRef( state ); + stateRef.current = state; + + const generateImage = useCallback( () => { + const chart = chartRef.current; + + if ( ! chart ) { + return; + } + + // Non-canvas libraries (e.g. Highcharts) handle image generation + // via the m_chart.render_done action hook instead. + if ( ! chart.canvas ) { + dispatch( { type: 'SET_FORM_ENABLED', payload: true } ); + return; + } + + const { imageWidth, postMeta } = stateRef.current; + const chartWidth = parseInt( imageWidth, 10 ); + const chartHeight = parseInt( postMeta.height, 10 ); + + const canvas = chart.canvas; + const container = canvas.parentElement; + + // Resize container to image dimensions so chart fills the right area + container.style.width = chartWidth + 'px'; + container.style.height = chartHeight + 'px'; + chart.resize(); + + // Fill solid white background (canvas is transparent by default) + const ctx = canvas.getContext( '2d' ); + ctx.save(); + ctx.globalCompositeOperation = 'destination-over'; + ctx.fillStyle = 'white'; + ctx.fillRect( 0, 0, chart.width, chart.height ); + ctx.restore(); + + // Capture PNG. + const img = chart.toBase64Image( 'image/png', 1 ); + + // Restore container to natural dimensions + container.style.width = ''; + container.style.height = ''; + chart.resize(); + + // Write base64 string to the hidden textarea for form POST + const imgEl = document.getElementById( 'm-chart-img' ); + + if ( imgEl ) { + imgEl.value = img; + } + + // Re-enable form submission. + dispatch( { type: 'SET_FORM_ENABLED', payload: true } ); + }, [ chartRef, dispatch ] ); // eslint-disable-line react-hooks/exhaustive-deps + + return generateImage; +} diff --git a/components/admin-ui-src/hooks/useLongPress.js b/components/admin-ui-src/hooks/useLongPress.js new file mode 100644 index 0000000..d414faa --- /dev/null +++ b/components/admin-ui-src/hooks/useLongPress.js @@ -0,0 +1,37 @@ +import { useCallback, useRef } from '@wordpress/element'; + +const LONG_PRESS_DELAY = 500; + +/** + * Returns pointer-event handlers that fire `callback` after a sustained press + * Spread the returned object onto any element:
+ */ +export function useLongPress( callback ) { + const timerRef = useRef( null ); + + const cancel = useCallback( () => { + if ( timerRef.current ) { + clearTimeout( timerRef.current ); + timerRef.current = null; + } + }, [] ); + + const start = useCallback( ( e ) => { + // Only respond to primary pointer (left-click / first touch) + if ( e.button !== undefined && e.button !== 0 ) { + return; + } + cancel(); + timerRef.current = setTimeout( () => { + timerRef.current = null; + callback( e ); + }, LONG_PRESS_DELAY ); + }, [ callback, cancel ] ); + + return { + onPointerDown: start, + onPointerUp: cancel, + onPointerLeave: cancel, + onPointerCancel: cancel, + }; +} diff --git a/components/admin-ui-src/index.js b/components/admin-ui-src/index.js new file mode 100644 index 0000000..c0af1ee --- /dev/null +++ b/components/admin-ui-src/index.js @@ -0,0 +1,60 @@ +import { createRoot, createPortal } from '@wordpress/element'; +import { ChartAdminProvider, useChartAdmin } from './context/ChartAdminContext'; +import ChartMetaBox from './components/ChartMetaBox'; +import SpreadsheetMetaBox from './components/SpreadsheetMetaBox'; +import SubtitleField from './components/SubtitleField'; +import TypeAndThemeRow from './components/TypeAndThemeRow'; +import ParseAndFlagsRow from './components/ParseAndFlagsRow'; +import AxisRows from './components/AxisRows'; +import ShortcodeAndImageRow from './components/ShortcodeAndImageRow'; + +// Expose shared context hook and settings row components for library plugins +// that implement the m_chart.settings_component filter without a build step +window.m_chart = { + useChartAdmin, + TypeAndThemeRow, + ParseAndFlagsRow, + AxisRows, + ShortcodeAndImageRow, +}; + +/** + * The admin UI spans multiple meta boxes and the title area, so we use a single + * React root (in a hidden container) with portals to render into each mount point + * This ensures all components share a single ChartAdminContext instance + */ + +// Register Chart.js plugins before any chart instances are created +if ( window.Chart && window.ChartDataLabels ) { + window.Chart.register( window.ChartDataLabels ); +} + +if ( window.Chart && window.MChartHelper ) { + window.Chart.register( window.MChartHelper ); +} + +const subtitleRoot = document.getElementById( 'm-chart-subtitle-root' ); +const spreadsheetRoot = document.getElementById( 'm-chart-spreadsheet-root' ); +const chartRoot = document.getElementById( 'm-chart-chart-root' ); + +if ( subtitleRoot || spreadsheetRoot || chartRoot ) { + const App = () => ( + + { subtitleRoot && createPortal( , subtitleRoot ) } + { spreadsheetRoot && createPortal( , spreadsheetRoot ) } + { chartRoot && createPortal( , chartRoot ) } + + ); + + // Mount the app into a hidden container appended to the post form + // All visible content is rendered via portals into the actual meta box divs + const postForm = document.getElementById( 'post' ); + + if ( postForm ) { + const container = document.createElement( 'div' ); + container.id = 'm-chart-admin-ui'; + container.hidden = true; + postForm.appendChild( container ); + createRoot( container ).render( ); + } +} diff --git a/components/admin-ui-src/utils/measureTextWidth.js b/components/admin-ui-src/utils/measureTextWidth.js new file mode 100644 index 0000000..ab18923 --- /dev/null +++ b/components/admin-ui-src/utils/measureTextWidth.js @@ -0,0 +1,27 @@ +/** + * Measures the pixel width needed to display a string inside a given input element, using a canvas and the input's computed font style + * + * Falls back to a character-count estimate if the input element is not yet mounted (e.g. on the first render before refs are attached) + * + * @param {string} text The string to measure + * @param {HTMLInputElement} inputEl The input element whose font to use + * + * @return {number} Width in pixels + */ +export function measureTextWidth( text, inputEl ) { + if ( ! inputEl ) { + return Math.max( 40, text.length * 8 + 16 ); + } + + const style = window.getComputedStyle( inputEl ); + const canvas = document.createElement( 'canvas' ); + const ctx = canvas.getContext( '2d' ); + ctx.font = style.font; + + const textWidth = Math.ceil( ctx.measureText( text ).width ) + 1; + const borderWidth = parseFloat( style.borderWidth ) || 0; + const paddingLeft = parseFloat( style.paddingLeft ) || 0; + const paddingRight = parseFloat( style.paddingRight ) || 0; + + return ( borderWidth * 2 ) + paddingLeft + textWidth + paddingRight; +} diff --git a/components/admin-ui/index.asset.php b/components/admin-ui/index.asset.php new file mode 100644 index 0000000..7a9e1c4 --- /dev/null +++ b/components/admin-ui/index.asset.php @@ -0,0 +1 @@ + array('react', 'wp-element', 'wp-i18n'), 'version' => 'd4e926fe7497ca77ef2a'); diff --git a/components/admin-ui/index.js b/components/admin-ui/index.js new file mode 100644 index 0000000..9d14a7b --- /dev/null +++ b/components/admin-ui/index.js @@ -0,0 +1,2432 @@ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "./components/admin-ui-src/components/AxisRows.js" +/*!********************************************************!*\ + !*** ./components/admin-ui-src/components/AxisRows.js ***! + \********************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ AxisRows) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); +/* harmony import */ var _utils_measureTextWidth__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utils/measureTextWidth */ "./components/admin-ui-src/utils/measureTextWidth.js"); + + + + + + +// Chart types that show y-min controls (line, spline, area only) +const YMIN_TYPES = new Set(['line', 'spline', 'area']); + +// Chart types that show axis title/unit rows +const AXIS_TYPES = new Set(['line', 'spline', 'area', 'column', 'stacked-column', 'bar', 'stacked-bar', 'scatter', 'bubble']); +function AxisRows() { + var _postMeta$y_min_value; + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__.useChartAdmin)(); + const { + postMeta, + unitTerms + } = state; + const showAxis = AXIS_TYPES.has(postMeta.type); + const showYMin = YMIN_TYPES.has(postMeta.type); + + // Callback ref triggers a re-render when the input mounts, so the canvas measurement runs with the real element instead of the fallback + const [yMinEl, setYMinEl] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null); + const yMinRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useCallback)(node => setYMinEl(node), []); + const yMinValue = String((_postMeta$y_min_value = postMeta.y_min_value) !== null && _postMeta$y_min_value !== void 0 ? _postMeta$y_min_value : 0); + const yMinWidth = yMinEl ? (0,_utils_measureTextWidth__WEBPACK_IMPORTED_MODULE_4__.measureTextWidth)(yMinValue, yMinEl) + 20 + 'px' : '73px'; + function handleChange(field, value) { + dispatch({ + type: 'SET_POST_META', + payload: { + [field]: value + } + }); + } + function handleYMinCheck(checked) { + dispatch({ + type: 'SET_POST_META', + payload: { + y_min: checked + } + }); + } + + // Always render axis rows so field values survive type switches on form save. + // Only hide them visually when the chart type doesn't need them. + const axisStyle = showAxis ? {} : { + display: 'none' + }; + const yMinStyle = showAxis && showYMin ? {} : { + display: 'none' + }; + const unitOptions = (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("option", { + value: "" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('N/A', 'm-chart')), unitTerms.map(({ + group, + units + }) => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.Fragment, { + key: group + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("option", { + value: "", + disabled: true + }, group), units.map(unit => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("option", { + key: unit.name, + value: unit.name + }, unit.name))))); + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "row three vertical-axis", + style: axisStyle + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-y-title" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Vertical axis title', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + className: "input", + type: "text", + name: "m-chart[y_title]", + id: "m-chart-y-title", + value: postMeta.y_title, + style: { + width: '100%' + }, + onChange: e => handleChange('y_title', e.target.value) + })), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "units" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-y-units" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Units', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("select", { + name: "m-chart[y_units]", + id: "m-chart-y-units", + className: "select", + value: postMeta.y_units, + onChange: e => handleChange('y_units', e.target.value) + }, unitOptions))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "row four y-min", + style: yMinStyle + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-y-min" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + type: "checkbox", + name: "m-chart[y_min]", + id: "m-chart-y-min", + value: "1", + checked: !!postMeta.y_min, + onChange: e => handleYMinCheck(e.target.checked) + }), (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)(' Force vertical axis minimum: ', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + type: "number", + name: "m-chart[y_min_value]", + id: "m-chart-y-min-value", + ref: yMinRef, + value: postMeta.y_min_value, + disabled: !postMeta.y_min, + onChange: e => handleChange('y_min_value', e.target.value), + style: { + width: yMinWidth, + minWidth: 0 + } + }))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "row five horizontal-axis", + style: axisStyle + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-x-title" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Horizontal axis title', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + className: "input", + type: "text", + name: "m-chart[x_title]", + id: "m-chart-x-title", + value: postMeta.x_title, + style: { + width: '100%' + }, + onChange: e => handleChange('x_title', e.target.value) + })), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "units" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-x-units" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Units', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("select", { + name: "m-chart[x_units]", + id: "m-chart-x-units", + className: "select", + value: postMeta.x_units, + onChange: e => handleChange('x_units', e.target.value) + }, unitOptions)))); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/ChartMetaBox.js" +/*!************************************************************!*\ + !*** ./components/admin-ui-src/components/ChartMetaBox.js ***! + \************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ChartMetaBox) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _hooks_useChartRefresh__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../hooks/useChartRefresh */ "./components/admin-ui-src/hooks/useChartRefresh.js"); +/* harmony import */ var _hooks_useFormSubmissionGuard__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../hooks/useFormSubmissionGuard */ "./components/admin-ui-src/hooks/useFormSubmissionGuard.js"); +/* harmony import */ var _ChartPreview__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./ChartPreview */ "./components/admin-ui-src/components/ChartPreview.js"); +/* harmony import */ var _ChartSettings__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ChartSettings */ "./components/admin-ui-src/components/ChartSettings.js"); + + + + + + + +/** + * Root component for the chart meta box. + * + * Owns the title state (read from the classic WP #title input) and wires useChartRefresh so chart args are re-fetched whenever settings or data change + * The subtitle input is now a React-controlled SubtitleField component mounted via a separate portal — no DOM bridge needed here. + */ +function ChartMetaBox() { + const [title, setTitle] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(() => { + const el = document.getElementById('title'); + return el ? el.value : ''; + }); + + // Keep the React title state in sync with the native WP title input + // Needed because React doesn't own this input since it's created by core WordPress + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + const el = document.getElementById('title'); + if (!el) { + return; + } + const handler = e => setTitle(e.target.value); + el.addEventListener('input', handler); + return () => el.removeEventListener('input', handler); + }, []); + (0,_hooks_useChartRefresh__WEBPACK_IMPORTED_MODULE_2__.useChartRefresh)(title); + (0,_hooks_useFormSubmissionGuard__WEBPACK_IMPORTED_MODULE_3__.useFormSubmissionGuard)(); + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_ChartPreview__WEBPACK_IMPORTED_MODULE_4__["default"], null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_ChartSettings__WEBPACK_IMPORTED_MODULE_5__["default"], null)); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/ChartPreview.js" +/*!************************************************************!*\ + !*** ./components/admin-ui-src/components/ChartPreview.js ***! + \************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ChartPreview) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); +/* harmony import */ var _hooks_useImageGeneration__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../hooks/useImageGeneration */ "./components/admin-ui-src/hooks/useImageGeneration.js"); + + + + + +/** + * Shallow-copies chart args to avoid mutating React state when Chart.js or MChartHelper modifies the chart config during initialization + * Tooltip callbacks and datalabels formatter are applied by MChartHelper via its beforeUpdate hook (runs each render) + * Bubble preprocessing runs once via beforeInit + */ +function prepareArgs(args) { + if (!args) { + return args; + } + return { + ...args, + data: { + ...args.data + }, + options: { + ...args.options, + plugins: { + ...args.options?.plugins, + tooltip: { + ...args.options?.plugins?.tooltip + }, + datalabels: { + ...args.options?.plugins?.datalabels + } + } + } + }; +} + +/** + * Default Chart.js renderer — create or update the Chart.js instance + * + * Applies chartjs-specific arg preparation before rendering + * Returned instance is stored in chartRef by the caller + * + * @param {HTMLCanvasElement} canvas Target canvas element + * @param {Object} args Raw chart args from state + * @param {Function} onComplete Callback to fire after render completes + * @param {Object|null} existingInstance Existing Chart.js instance, or null on first render + * + * @return {Object} The Chart.js instance + */ +function defaultChartjsRender(canvas, args, onComplete, existingInstance) { + const prepared = prepareArgs(args); + + // Guard against null/undefined datasets or labels (Chart.js requires arrays). + if (!prepared.data?.datasets) { + prepared.data = { + ...prepared.data, + datasets: [] + }; + } + if (null === prepared.data?.labels) { + prepared.data = { + ...prepared.data, + labels: [] + }; + } + const options = { + ...prepared.options, + animation: { + onComplete + } + }; + + // Only create the new chart if there isn't an existing one already + if (!existingInstance) { + return new window.Chart(canvas, { + type: prepared.type, + data: prepared.data, + options + }); + } + existingInstance.data = prepared.data; + existingInstance.config.type = prepared.type; + existingInstance.options = options; + existingInstance.update(); + return existingInstance; +} + +/** + * React-managed chart preview for the admin meta box + * + * The chart instance is managed imperatively via refs and is never recreated on re-render — only updated when chartArgs changes + * + * Rendering is delegated via the 'm_chart.render_chart' wp.hooks filter so library plugins can replace the default Chart.js renderer + * The filter receives ( canvas, args, onComplete, existingInstance ) as extra arguments + * If no filter handles rendering (i.e. returns false), Chart.js is used + * + * The onComplete callback must be called by the renderer once the chart has finished which will fire 'm_chart.render_done' to trigger image generation and/or re-enable the form + */ +function ChartPreview() { + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__.useChartAdmin)(); + const { + postId, + chartArgs, + performance, + imageSupport, + postMeta + } = state; + const canvasRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null); + const chartRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null); + const renderFlagRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(false); + const isFirstRender = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(true); + + // Keep a ref so onComplete closures always see the latest values + const needsImagesRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(false); + needsImagesRef.current = 'default' === performance && 'yes' === imageSupport; + const generateImage = (0,_hooks_useImageGeneration__WEBPACK_IMPORTED_MODULE_3__.useImageGeneration)(chartRef); + + // Cleanup — destroy chart instance on unmount + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + return () => { + if (chartRef.current) { + chartRef.current.destroy(); + chartRef.current = null; + } + }; + }, []); + + // Create or update the chart instance whenever chartArgs changes + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + if (!chartArgs || !canvasRef.current) { + return; + } + function onComplete() { + // Only fire once per update cycle + if (!renderFlagRef.current) { + return; + } + renderFlagRef.current = false; + if (window.wp?.hooks) { + window.wp.hooks.doAction('m_chart.render_done', postId, 1, chartRef.current); + } + if (needsImagesRef.current) { + generateImage(); + } else { + // No image generation — enable form submission immediately + // This also covers the initial page load where useChartRefresh skips its first run + dispatch({ + type: 'SET_FORM_ENABLED', + payload: true + }); + isFirstRender.current = false; + } + } + renderFlagRef.current = true; + + // Allow library plugins to replace the renderer via wp.hooks + // Plugins hook 'm_chart.render_chart' and return their chart instance + // Returning false (the default) falls through to the built-in Chart.js renderer + let instance = false; + if (window.wp?.hooks) { + // See defaultChartjsRender for the filter arguments + instance = window.wp.hooks.applyFilters('m_chart.render_chart', false, canvasRef.current, chartArgs, onComplete, chartRef.current); + } + chartRef.current = false !== instance ? instance : defaultChartjsRender(canvasRef.current, chartArgs, onComplete, chartRef.current); + }, [chartArgs]); // eslint-disable-line react-hooks/exhaustive-deps + + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "m-chart-container", + style: { + height: postMeta.height + 'px' + } + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("canvas", { + ref: canvasRef + })); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/ChartSettings.js" +/*!*************************************************************!*\ + !*** ./components/admin-ui-src/components/ChartSettings.js ***! + \*************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ChartSettings) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _TypeAndThemeRow__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./TypeAndThemeRow */ "./components/admin-ui-src/components/TypeAndThemeRow.js"); +/* harmony import */ var _ParseAndFlagsRow__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./ParseAndFlagsRow */ "./components/admin-ui-src/components/ParseAndFlagsRow.js"); +/* harmony import */ var _AxisRows__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./AxisRows */ "./components/admin-ui-src/components/AxisRows.js"); +/* harmony import */ var _ShortcodeAndImageRow__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ShortcodeAndImageRow */ "./components/admin-ui-src/components/ShortcodeAndImageRow.js"); + + + + + + +function DefaultSettings() { + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_TypeAndThemeRow__WEBPACK_IMPORTED_MODULE_2__["default"], null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_ParseAndFlagsRow__WEBPACK_IMPORTED_MODULE_3__["default"], null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_AxisRows__WEBPACK_IMPORTED_MODULE_4__["default"], null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_ShortcodeAndImageRow__WEBPACK_IMPORTED_MODULE_5__["default"], null)); +} +function ChartSettings() { + // Allow library plugins to replace the settings component via wp.hooks + // useMemo with [] ensures the filter runs once — filters are registered at load time, + // so calling applyFilters on every render would return a new function reference each + // time and cause React to unmount/remount the settings UI + const Settings = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useMemo)(() => { + return window.wp?.hooks ? wp.hooks.applyFilters('m_chart.settings_component', DefaultSettings) : DefaultSettings; + }, []); + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "settings" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(Settings, null)); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/CsvControls.js" +/*!***********************************************************!*\ + !*** ./components/admin-ui-src/components/CsvControls.js ***! + \***********************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ CsvControls) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); +/* harmony import */ var _JspreadsheetWrapper__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./JspreadsheetWrapper */ "./components/admin-ui-src/components/JspreadsheetWrapper.js"); + + + + + + +/** + * CSV import and export controls for the active spreadsheet sheet + * + * Import uses fetch + FormData (replaces the hidden #m-chart-csv-import-form) + * Export uses a dynamically-created temporary form POST to trigger a file download (replaces the hidden #m-chart-csv-export-form) + * + * Props: + * getActiveWorksheet {Function} Returns the active Jspreadsheet worksheet instance + */ +function CsvControls({ + getActiveWorksheet +}) { + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__.useChartAdmin)(); + const { + postId, + nonce, + ajaxUrl, + setNames, + activeSheet, + csvDelimiters, + defaultDelimiter + } = state; + const [selectedFile, setSelectedFile] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null); + const [csvDelimiter, setCsvDelimiter] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(defaultDelimiter); + const [fileError, setFileError] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false); + const [importError, setImportError] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(''); + const [isImporting, setIsImporting] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false); + const fileInputRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null); + function handleSelectFile(e) { + e.preventDefault(); + setFileError(false); + setImportError(''); + fileInputRef.current?.click(); + } + function handleFileChange(e) { + const file = e.target.files[0]; + + // Make sure it's a CSV file + if (!file || !/\.csv$/i.test(file.name)) { + setFileError(true); + setSelectedFile(null); + return; + } + setFileError(false); + setSelectedFile(file); + } + function handleCancel(e) { + e.preventDefault(); + setSelectedFile(null); + + // We're hiding the actual file input so we need to reset it for the user + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + } + async function handleImport(e) { + e.preventDefault(); + if (!selectedFile) { + return; + } + + // Save the file value so we can reset the iput + const file = selectedFile; + + // Set the UI to show we're importing the file + setSelectedFile(null); + setIsImporting(true); + setImportError(''); + + // Reset the actual file input back to empty + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + + // Create a form data object so we can submit it to the endpoint + const formData = new FormData(); + formData.append('import_csv_file', file); + formData.append('post_id', postId); + formData.append('csv_delimiter', csvDelimiter); + formData.append('nonce', nonce); + + // Try submitting the data to the endpoint + try { + const response = await fetch(`${ajaxUrl}?action=m_chart_import_csv`, { + method: 'POST', + body: formData + }); + const json = await response.json(); + if (!json.success) { + setImportError(json.data || (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Import failed', 'm-chart')); + return; + } + + // Get the active worksheet + const worksheet = getActiveWorksheet(); + if (worksheet) { + // Set the active worksheet to the new data + worksheet.setData(json.data); + + // setData() does not trigger onafterchanges so we need to run spreadsheetAutoWidth ourselves + (0,_JspreadsheetWrapper__WEBPACK_IMPORTED_MODULE_4__.spreadsheetAutoWidth)(worksheet); + dispatch({ + type: 'SET_SHEET_DATA', + payload: { + index: activeSheet, + data: worksheet.getData() + } + }); + } + } catch (err) { + setImportError((0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.sprintf)((0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Import error: %s', 'm-chart'), err.message)); + } finally { + // When we're done reset everything in the CSV ui back to default + setIsImporting(false); + } + } + function handleExport(e) { + e.preventDefault(); + + // Get the active worksheet + const worksheet = getActiveWorksheet(); + if (!worksheet) { + return; + } + const data = worksheet.getData(); + const title = document.getElementById('title')?.value || ''; + const setName = setNames[activeSheet] || ''; + + // Build a FormData object so we can submit it to the endpoint + const formData = new FormData(); + formData.append('post_id', postId); + formData.append('data', JSON.stringify(data)); + formData.append('title', title); + formData.append('set_name', setName); + + // Create a temporary form and submit it + // We have to do it this way to trigger a download + const form = document.createElement('form'); + form.action = `${ajaxUrl}?action=m_chart_export_csv`; + form.method = 'post'; + form.style.display = 'none'; + + // Loop through the formData and append it to the temporary form + for (const [name, value] of formData.entries()) { + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = name; + input.value = value; + form.appendChild(input); + } + + // Do the thing + document.body.appendChild(form); + form.submit(); + document.body.removeChild(form); + } + const showConfirmation = selectedFile && !isImporting; + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + id: "m-chart-csv" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "export" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("a", { + href: "#export-csv", + title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Export CSV', 'm-chart'), + className: "button", + onClick: handleExport + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Export', 'm-chart'))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "import" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('CSV Import/Export', 'm-chart'), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "controls" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + ref: fileInputRef, + type: "file", + accept: ".csv", + style: { + display: 'none' + }, + onChange: handleFileChange + }), !showConfirmation && !isImporting && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("a", { + href: "#select-csv", + title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Select CSV File', 'm-chart'), + className: "button select", + onClick: handleSelectFile + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Select File', 'm-chart')), showConfirmation && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "confirmation" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("a", { + href: "#import-csv", + title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Import', 'm-chart'), + className: "button", + onClick: handleImport + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Import', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("select", { + name: "m-chart[csv_delimiter]", + value: csvDelimiter, + onChange: e => setCsvDelimiter(e.target.value) + }, Object.entries(csvDelimiters).map(([val, label]) => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("option", { + key: val, + value: val + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.sprintf)((0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('%s Delimited', 'm-chart'), label))))), fileError && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "file error" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('You can only import CSV files', 'm-chart')), importError && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "import error" + }, importError), isImporting && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "import in-progress" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Importing file', 'm-chart')), showConfirmation && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "file-info" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("a", { + href: "#cancel", + title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Cancel Import', 'm-chart'), + className: "dashicons dashicons-dismiss", + onClick: handleCancel + }), (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.sprintf)((0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('File: %s', 'm-chart'), selectedFile.name), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("span", { + className: "warning" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Importing this file will replace all existing data in this sheet', 'm-chart')))))); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/JspreadsheetWrapper.js" +/*!*******************************************************************!*\ + !*** ./components/admin-ui-src/components/JspreadsheetWrapper.js ***! + \*******************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ JspreadsheetWrapper), +/* harmony export */ spreadsheetAutoWidth: () => (/* binding */ spreadsheetAutoWidth) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); + + + + +// Jspreadsheet CE has a bunch of default menu items this is the list of the ones we actually want +const CONTEXT_MENU_ITEMS = ['Insert a new row before', 'Insert a new row after', 'Delete selected rows', 'Insert a new column before', 'Insert a new column after', 'Delete selected columns']; + +/** + * Resizes columns to fit their content using canvas-based text measurement + * + * @param {object} worksheet Jspreadsheet CE worksheet instance + * @param {Array} [records] Subset of changed records; omit for a full refresh + */ +function spreadsheetAutoWidth(worksheet, records = false) { + // If no records to refresh were passed we'll just do all of them + if (!records) { + records = worksheet.records[0]; + } + + // If there are no records even after the above we stop here + if (!records || !records.length) { + return; + } + const columns = [...new Set(records.map(r => r.x))]; + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + columns.forEach(column => { + let maxWidth = 0; + const padding = 13; + const minWidth = 100 - padding; + for (let i = 0; i < worksheet.records.length; i++) { + const cell = worksheet.records[i]?.[column]?.element; + if (cell) { + context.font = window.getComputedStyle(cell).font; + const metrics = context.measureText(cell.innerText); + if (metrics.width > maxWidth) { + maxWidth = metrics.width; + } + } + } + maxWidth = minWidth > maxWidth ? minWidth : maxWidth; + worksheet.setWidth(column, maxWidth + padding); + }); +} + +/** + * Thin React wrapper around a Jspreadsheet CE worksheet + * + * The Jspreadsheet instance is created once on mount and never recreated on re-render + * Show/hide between active/inactive sheets is done via CSS so that DOM state and undo history are preserved + * + * Props: + * sheetId {number} Stable identity key (used for registration) + * sheetIndex {number} Current position in the sheets array (may change after deletes) + * isActive {boolean} Whether this sheet is currently displayed + * data {Array} Initial 2-D array of cell values + * onMounted {Function} Called with (sheetId, worksheetInstance) after init + * onUnmounted {Function} Called with (sheetId) before unmount + */ +function JspreadsheetWrapper({ + sheetId, + sheetIndex, + isActive, + data, + onMounted, + onUnmounted +}) { + const { + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__.useChartAdmin)(); + const containerRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null); + const worksheetRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null); + + // Keep a ref so the onafterchanges closure always dispatches the current index + const sheetIndexRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(sheetIndex); + sheetIndexRef.current = sheetIndex; + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + if (!containerRef.current || worksheetRef.current) { + return; + } + + // Need to load an empty data array if there's none to start already + const initialData = data && data.length ? data : [['']]; + + // Create the sheet instance + const instance = window.jspreadsheet(containerRef.current, { + worksheets: [{ + data: initialData, + allowComments: false, + minDimensions: [37, 17] + }], + // Filter out all of the contextual menu items we don't want + contextMenu(obj, x, y, e, items) { + return items.filter(item => CONTEXT_MENU_ITEMS.includes(item.title)); + }, + // Run spreadsheetAutoWidth on the intiial load + onload(spreadsheet) { + const ws = spreadsheet.worksheets[spreadsheet.getWorksheetActive()]; + spreadsheetAutoWidth(ws); + }, + // Run spreadsheetAutoWidth on changed recrds and also push any changes to the chart + onafterchanges(worksheet, records) { + spreadsheetAutoWidth(worksheet, records); + dispatch({ + type: 'SET_SHEET_DATA', + payload: { + index: sheetIndexRef.current, + data: worksheet.getData() + } + }); + } + }); + worksheetRef.current = instance[0]; + onMounted(sheetId, worksheetRef.current); + return () => { + onUnmounted(sheetId); + worksheetRef.current = null; + }; + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + ref: containerRef, + className: "spreadsheet", + style: { + display: isActive ? '' : 'none' + } + }); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/ParseAndFlagsRow.js" +/*!****************************************************************!*\ + !*** ./components/admin-ui-src/components/ParseAndFlagsRow.js ***! + \****************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ParseAndFlagsRow) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); + + + +const PARSE_OPTION_NAMES = { + columns: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__.__)('Columns', 'm-chart'), + rows: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__.__)('Rows', 'm-chart') +}; + +// Chart types that support the shared tooltip option +const SHARED_TYPES = new Set(['line', 'spline', 'area', 'radar', 'radar-area']); +function ParseAndFlagsRow() { + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__.useChartAdmin)(); + const { + postMeta + } = state; + const showShared = SHARED_TYPES.has(postMeta.type); + function handleChange(field, value) { + dispatch({ + type: 'SET_POST_META', + payload: { + [field]: value + } + }); + } + function handleCheckbox(field, checked) { + dispatch({ + type: 'SET_POST_META', + payload: { + [field]: checked + } + }); + } + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: `row two${showShared ? ' show-shared' : ''}` + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-parse_in" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__.__)('Parse data in', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("select", { + name: "m-chart[parse_in]", + id: "m-chart-parse_in", + className: "select", + value: postMeta.parse_in, + onChange: e => handleChange('parse_in', e.target.value) + }, Object.entries(PARSE_OPTION_NAMES).map(([value, label]) => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("option", { + key: value, + value: value + }, label)))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "labels" + }, '\u00a0', (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-labels" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + type: "checkbox", + name: "m-chart[labels]", + id: "m-chart-labels", + value: "1", + checked: !!postMeta.labels, + onChange: e => handleCheckbox('labels', e.target.checked) + }), (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__.__)(' Show labels', 'm-chart'))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "legend" + }, '\u00a0', (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-legend" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + type: "checkbox", + name: "m-chart[legend]", + id: "m-chart-legend", + value: "1", + checked: !!postMeta.legend, + onChange: e => handleCheckbox('legend', e.target.checked) + }), (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__.__)(' Show legend', 'm-chart'))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "shared" + }, '\u00a0', (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-shared" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + type: "checkbox", + name: "m-chart[shared]", + id: "m-chart-shared", + value: "1", + checked: !!postMeta.shared, + onChange: e => handleCheckbox('shared', e.target.checked) + }), (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__.__)(' Shared tooltip', 'm-chart')))); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/SheetTab.js" +/*!********************************************************!*\ + !*** ./components/admin-ui-src/components/SheetTab.js ***! + \********************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ SheetTab) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); +/* harmony import */ var _hooks_useLongPress__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../hooks/useLongPress */ "./components/admin-ui-src/hooks/useLongPress.js"); +/* harmony import */ var _utils_measureTextWidth__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utils/measureTextWidth */ "./components/admin-ui-src/utils/measureTextWidth.js"); + + + + + + +/** + * A single sheet tab in the spreadsheet tab bar + * + * Supports: + * - Click to activate + * - Double-click or long-press (500ms) to enter rename mode + * - Enter / blur to commit rename + * - Dismiss icon to delete (guarded by window.confirm) + */ +function SheetTab({ + sheetId, + sheetIndex, + name, + isActive, + isSingle, + isNew +}) { + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__.useChartAdmin)(); + const [isRenaming, setIsRenaming] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(() => !!isNew); + const [inputValue, setInputValue] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(name); + const inputRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null); + const longPress = (0,_hooks_useLongPress__WEBPACK_IMPORTED_MODULE_3__.useLongPress)(() => setIsRenaming(true)); + + // Clear the newSheetId flag once this tab has consumed it + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + if (isNew) { + dispatch({ + type: 'CLEAR_NEW_SHEET_ID' + }); + } + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + // Sync local input value and focus when entering rename mode + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + if (isRenaming) { + setInputValue(name); + if (inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + } + }, [isRenaming]); // eslint-disable-line react-hooks/exhaustive-deps + + function handleClick(e) { + e.preventDefault(); + if (!isActive) { + dispatch({ + type: 'SET_ACTIVE_SHEET', + payload: sheetIndex + }); + } + } + function handleDoubleClick(e) { + e.preventDefault(); + setIsRenaming(true); + } + function handleDelete(e) { + e.preventDefault(); + e.stopPropagation(); + + // If there's only one tab we don't let the user delete it + if (isSingle) { + return; + } + + // If user rejects teh confirmation we stop + if (!window.confirm(state.deleteConfirm)) { + return; + } + + // Activate a neighbouring sheet before deleting so the active index stays valid. + if (isActive) { + const newActive = sheetIndex > 0 ? sheetIndex - 1 : 1; + dispatch({ + type: 'SET_ACTIVE_SHEET', + payload: newActive + }); + } + dispatch({ + type: 'DELETE_SHEET', + payload: { + index: sheetIndex + } + }); + } + function handleNameChange(e) { + setInputValue(e.target.value); + } + function commitRename() { + dispatch({ + type: 'RENAME_SHEET', + payload: { + index: sheetIndex, + name: inputValue + } + }); + setIsRenaming(false); + } + function handleKeyDown(e) { + if (e.key === 'Enter') { + e.preventDefault(); + commitRename(); + } + } + const inputWidth = inputRef.current ? (0,_utils_measureTextWidth__WEBPACK_IMPORTED_MODULE_4__.measureTextWidth)(inputValue, inputRef.current) + 'px' : Math.max(40, inputValue.length * 8 + 16) + 'px'; + const className = ['nav-tab', isActive ? 'nav-tab-active' : '', isSingle ? 'do-not-delete' : ''].filter(Boolean).join(' '); + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("a", { + href: "#", + className: className, + id: `spreadsheet-tab-${sheetId}`, + onClick: handleClick, + onDoubleClick: handleDoubleClick, + ...longPress + }, !isSingle && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("span", { + className: "dashicons dashicons-dismiss", + onClick: handleDelete + }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("span", { + className: "tab-title", + style: { + display: isRenaming ? 'none' : '' + } + }, name), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + ref: inputRef, + type: "text", + name: `m-chart[set_names][${sheetIndex}]`, + className: "spreadsheet-tab-input", + value: inputValue, + style: { + display: isRenaming ? '' : 'none', + width: inputWidth + }, + onChange: handleNameChange, + onBlur: commitRename, + onKeyDown: handleKeyDown + })); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/SheetTabs.js" +/*!*********************************************************!*\ + !*** ./components/admin-ui-src/components/SheetTabs.js ***! + \*********************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ SheetTabs) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); +/* harmony import */ var _SheetTab__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./SheetTab */ "./components/admin-ui-src/components/SheetTab.js"); + + + + + + +/** + * Chart types that support multiple data sets (multiple spreadsheet sheets) + * All other types use a single sheet and the tab bar is hidden + */ +const MULTI_SHEET_TYPES = new Set(['scatter', 'bubble', 'radar', 'radar-area']); + +/** + * The spreadsheet tab bar + * Renders one SheetTab per sheet and an Add Sheet button + * The entire bar is hidden when the current chart type only supports a single data set + */ +function SheetTabs() { + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__.useChartAdmin)(); + const { + postMeta, + sheetIds, + setNames, + activeSheet, + newSheetId + } = state; + + // Allow library plugins to customize which chart types support multiple sheets + // useMemo with [] ensures the filter runs once — filters are registered at load time + // and the resulting Set is stable, so showTabs only re-evaluates when postMeta.type changes + const multiSheetTypes = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useMemo)(() => { + const types = window.wp?.hooks ? wp.hooks.applyFilters('m_chart.multi_sheet_types', [...MULTI_SHEET_TYPES]) : [...MULTI_SHEET_TYPES]; + return new Set(types); + }, []); + const showTabs = multiSheetTypes.has(postMeta.type); + function handleAddSheet(e) { + e.preventDefault(); + dispatch({ + type: 'ADD_SHEET', + payload: {} + }); + } + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("nav", { + id: "spreadsheet-tabs", + className: `nav-tab-wrapper${showTabs ? '' : ' hide'}` + }, sheetIds.map((id, index) => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_SheetTab__WEBPACK_IMPORTED_MODULE_4__["default"], { + key: id, + sheetId: id, + sheetIndex: index, + name: setNames[index] || (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.sprintf)((0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Sheet %d', 'm-chart'), index + 1), + isActive: index === activeSheet, + isSingle: sheetIds.length === 1, + isNew: id === newSheetId + })), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("a", { + href: "#add-sheet", + className: "add-sheet", + title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Add Sheet', 'm-chart'), + onClick: handleAddSheet + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("span", { + className: "dashicons dashicons-plus-alt" + }))); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/ShortcodeAndImageRow.js" +/*!********************************************************************!*\ + !*** ./components/admin-ui-src/components/ShortcodeAndImageRow.js ***! + \********************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ ShortcodeAndImageRow) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); + + + + +function ShortcodeAndImageRow() { + const { + state + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__.useChartAdmin)(); + const { + postId, + postMeta, + imageUrl, + performance, + imageSupport + } = state; + const shortcode = `[chart id="${postId}"]`; + const showImageField = 'default' === performance && 'yes' === imageSupport; + const imageDisabled = !showImageField; + const [copied, setCopied] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false); + function handleCopy() { + navigator.clipboard.writeText(shortcode).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }); + } + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "row seven" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "shortcode" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-shortcode" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Shortcode', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + className: "input", + type: "text", + name: "m-chart[shortcode]", + id: "m-chart-shortcode", + value: shortcode, + style: { + width: '100%' + }, + onClick: e => e.target.select(), + readOnly: true + }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("button", { + type: "button", + className: "button", + onClick: handleCopy + }, copied ? (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Copied!', 'm-chart') : (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Copy', 'm-chart'))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "image" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-image" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Image', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), imageUrl ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + className: "input", + type: "text", + name: "m-chart[image]", + id: "m-chart-image", + value: imageUrl, + style: { + width: '100%' + }, + onClick: e => e.target.select(), + readOnly: true + }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("a", { + href: imageUrl, + className: "button", + target: "_blank", + rel: "noreferrer" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('View', 'm-chart'))) : imageDisabled ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("em", null, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Image generation is disabled', 'm-chart')) : (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("em", null, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Save/Update this post to generate the image version', 'm-chart'))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + type: "hidden", + name: "m-chart[library]", + id: "m-chart-library", + value: postMeta.library + })); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/SpreadsheetMetaBox.js" +/*!******************************************************************!*\ + !*** ./components/admin-ui-src/components/SpreadsheetMetaBox.js ***! + \******************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ SpreadsheetMetaBox) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); +/* harmony import */ var _JspreadsheetWrapper__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./JspreadsheetWrapper */ "./components/admin-ui-src/components/JspreadsheetWrapper.js"); +/* harmony import */ var _SheetTabs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./SheetTabs */ "./components/admin-ui-src/components/SheetTabs.js"); +/* harmony import */ var _CsvControls__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./CsvControls */ "./components/admin-ui-src/components/CsvControls.js"); + + + + + + + +// WordPress submit button IDs that should trigger save behavior +const SUBMIT_BUTTON_IDS = ['publish', 'save-post']; + +/** + * Container for the spreadsheet meta box + * + * Manages Jspreadsheet worksheet instances via a ref map keyed by stable sheet ID + * Handles form submission: writes all sheet data to the hidden textarea[name="m-chart[data]"] before the post form is submitted + */ +function SpreadsheetMetaBox() { + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__.useChartAdmin)(); + const { + sheetIds, + spreadsheetData, + activeSheet, + formEnabled, + pendingSubmit + } = state; + + // Map of stable sheetId → worksheet instance (Jspreadsheet worksheet object) + const worksheetInstances = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)({}); + + // Refs so event handlers always see the latest values without needing to be recreated + const formEnabledRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(formEnabled); + const sheetIdsRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(sheetIds); + formEnabledRef.current = formEnabled; + sheetIdsRef.current = sheetIds; + + // Called by JspreadsheetWrapper after it initialises its jspreadsheet instance + const handleMounted = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useCallback)((sheetId, worksheet) => { + worksheetInstances.current[sheetId] = worksheet; + }, []); + + // Called by JspreadsheetWrapper just before it unmounts + const handleUnmounted = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useCallback)(sheetId => { + delete worksheetInstances.current[sheetId]; + }, []); + + // Returns the worksheet instance for the currently active sheet + const getActiveWorksheet = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useCallback)(() => { + var _worksheetInstances$c; + const activeId = sheetIdsRef.current[state.activeSheet]; + return (_worksheetInstances$c = worksheetInstances.current[activeId]) !== null && _worksheetInstances$c !== void 0 ? _worksheetInstances$c : null; + }, [state.activeSheet]); + + // Writes all sheet data to the hidden textarea so the form POST includes it + const writeDataToForm = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useCallback)(() => { + const form = document.getElementById('post'); + if (!form) { + return; + } + const allData = sheetIdsRef.current.map(id => { + var _worksheetInstances$c2; + return (_worksheetInstances$c2 = worksheetInstances.current[id]?.getData()) !== null && _worksheetInstances$c2 !== void 0 ? _worksheetInstances$c2 : [['']]; + }); + const dataTextarea = form.querySelector('textarea[name="m-chart[data]"]'); + if (dataTextarea) { + dataTextarea.value = JSON.stringify(allData); + } + }, []); + + // When formEnabled becomes true while a submit is pending, submit the form + // Uses form.submit() to bypass event handlers since the data textarea is already written + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + if (formEnabled && pendingSubmit) { + dispatch({ + type: 'SET_PENDING_SUBMIT', + payload: false + }); + + // Write latest data right before submitting + writeDataToForm(); + const form = document.getElementById('post'); + if (form) { + form.submit(); + } + } + }, [formEnabled, pendingSubmit, dispatch, writeDataToForm]); + + // Detect submit intent at the click event on WP submit buttons + // Click fires AFTER blur but BEFORE the form's submit event + // This is the earliest reliable detection point + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + function handleClick(e) { + // If the chart is still refreshing, intercept the click to defer submission + // The form submit event hasn't fired yet so we prevent the default click behavior + if (!formEnabledRef.current) { + e.preventDefault(); + writeDataToForm(); + dispatch({ + type: 'SET_PENDING_SUBMIT', + payload: true + }); + return; + } + + // Form is ready — write data and let the normal submit flow proceed + writeDataToForm(); + } + const buttons = SUBMIT_BUTTON_IDS.map(id => document.getElementById(id)).filter(Boolean); + buttons.forEach(btn => btn.addEventListener('click', handleClick)); + return () => { + buttons.forEach(btn => btn.removeEventListener('click', handleClick)); + }; + }, [dispatch, writeDataToForm]); + + // Intercept the form submit event as a fallback + // Ensures the data textarea is always written before submission regardless of how + // the submit was triggered (keyboard, other plugins, etc) + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => { + const form = document.getElementById('post'); + if (!form) { + return; + } + function handleSubmit(e) { + // If chart is still refreshing, block this submit — the click handler + // already set pendingSubmit so it will auto-submit when ready + if (!formEnabledRef.current) { + e.preventDefault(); + return; + } + + // Write data in case the submit wasn't triggered via our click handler + writeDataToForm(); + } + form.addEventListener('submit', handleSubmit); + return () => form.removeEventListener('submit', handleSubmit); + }, [writeDataToForm]); + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_SheetTabs__WEBPACK_IMPORTED_MODULE_4__["default"], null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + id: "spreadsheets" + }, sheetIds.map((id, index) => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_JspreadsheetWrapper__WEBPACK_IMPORTED_MODULE_3__["default"], { + key: id, + sheetId: id, + sheetIndex: index, + isActive: index === activeSheet, + data: spreadsheetData[index], + onMounted: handleMounted, + onUnmounted: handleUnmounted + }))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_CsvControls__WEBPACK_IMPORTED_MODULE_5__["default"], { + getActiveWorksheet: getActiveWorksheet + })); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/SubtitleField.js" +/*!*************************************************************!*\ + !*** ./components/admin-ui-src/components/SubtitleField.js ***! + \*************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ SubtitleField) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); + + + + +/** + * Reach controlled subtitle input + * + * Renders with the m-chart[subtitle] name attribute so the value is submitted with the native WordPress post form and the existing PHP save logic still works + */ +function SubtitleField() { + var _state$postMeta$subti; + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__.useChartAdmin)(); + const value = (_state$postMeta$subti = state.postMeta?.subtitle) !== null && _state$postMeta$subti !== void 0 ? _state$postMeta$subti : ''; + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + className: "input", + type: "text", + name: "m-chart[subtitle]", + id: "m-chart-subtitle", + value: value, + placeholder: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__.__)('Enter subtitle here', 'm-chart'), + style: { + width: '100%' + }, + onChange: e => dispatch({ + type: 'SET_SUBTITLE', + payload: e.target.value + }) + }); +} + +/***/ }, + +/***/ "./components/admin-ui-src/components/TypeAndThemeRow.js" +/*!***************************************************************!*\ + !*** ./components/admin-ui-src/components/TypeAndThemeRow.js ***! + \***************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ TypeAndThemeRow) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); +/* harmony import */ var _utils_measureTextWidth__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../utils/measureTextWidth */ "./components/admin-ui-src/utils/measureTextWidth.js"); + + + + + +function TypeAndThemeRow() { + var _postMeta$height; + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_3__.useChartAdmin)(); + const { + postMeta, + typeOptions, + typeOptionNames, + themes + } = state; + const [heightEl, setHeightEl] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null); + const heightRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useCallback)(node => setHeightEl(node), []); + const heightValue = String((_postMeta$height = postMeta.height) !== null && _postMeta$height !== void 0 ? _postMeta$height : ''); + const heightWidth = heightEl ? (0,_utils_measureTextWidth__WEBPACK_IMPORTED_MODULE_4__.measureTextWidth)(heightValue, heightEl) + 20 + 'px' : '73px'; + function handleChange(field, value) { + dispatch({ + type: 'SET_POST_META', + payload: { + [field]: value + } + }); + } + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "row one" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-type" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Type', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("select", { + name: "m-chart[type]", + id: "m-chart-type", + className: "select", + value: postMeta.type, + onChange: e => handleChange('type', e.target.value) + }, typeOptions.map(type => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("option", { + key: type, + value: type + }, typeOptionNames[type] || type)))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-theme" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Theme', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("select", { + name: "m-chart[theme]", + id: "m-chart-theme", + value: postMeta.theme, + onChange: e => handleChange('theme', e.target.value) + }, themes.map(theme => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("option", { + key: theme.slug, + value: theme.slug + }, theme.name)))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("label", { + htmlFor: "m-chart-height" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_2__.__)('Height', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("input", { + type: "number", + name: "m-chart[height]", + id: "m-chart-height", + ref: heightRef, + value: postMeta.height, + min: "300", + max: "1500", + onChange: e => handleChange('height', e.target.value), + style: { + width: heightWidth, + minWidth: 0 + } + }))); +} + +/***/ }, + +/***/ "./components/admin-ui-src/context/ChartAdminContext.js" +/*!**************************************************************!*\ + !*** ./components/admin-ui-src/context/ChartAdminContext.js ***! + \**************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ ChartAdminProvider: () => (/* binding */ ChartAdminProvider), +/* harmony export */ useChartAdmin: () => (/* binding */ useChartAdmin), +/* harmony export */ useChartDispatch: () => (/* binding */ useChartDispatch) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); + + +const ChartAdminStateContext = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createContext)(null); +const ChartAdminDispatchContext = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createContext)(null); +const { + m_chart_admin +} = window; + +/** + * Initial state populated from the PHP-localised window.m_chart_admin object + * + * post_meta contains all chart meta fields except 'data' (spreadsheetData holds that) + * Fields mirror the PHP $chart_meta_fields defaults in class-m-chart.php + */ + +// Stable sheet IDs — never change once a sheet is created, survive deletion of siblings +const initialSheetCount = (m_chart_admin.spreadsheet_data || [[]]).length; +const initialSheetIds = Array.from({ + length: initialSheetCount +}, (_, i) => i); +const initialState = { + slug: m_chart_admin.slug, + postId: m_chart_admin.post_id, + nonce: m_chart_admin.nonce, + ajaxUrl: m_chart_admin.ajax_url, + library: m_chart_admin.library, + performance: m_chart_admin.performance, + imageSupport: m_chart_admin.image_support, + instantPreview: m_chart_admin.instant_preview_support, + imageMultiplier: m_chart_admin.image_multiplier, + imageWidth: m_chart_admin.image_width, + deleteConfirm: m_chart_admin.delete_confirm, + csvDelimiters: m_chart_admin.csv_delimiters || { + ',': 'Comma' + }, + defaultDelimiter: m_chart_admin.default_delimiter || ',', + // Chart meta fields (type, theme, height, parse_in, labels, legend, etc.) + postMeta: m_chart_admin.post_meta, + // Array of 2D arrays, one per sheet (matches Jspreadsheet's getData() format) + spreadsheetData: m_chart_admin.spreadsheet_data, + // Array of human-readable names for each sheet tab + setNames: m_chart_admin.set_names || [], + // Stable IDs for each sheet — used as React keys to avoid spurious remounts + sheetIds: initialSheetIds, + // Counter for the next sheet ID to assign + nextSheetId: initialSheetCount, + // ID of the most recently added sheet — SheetTab uses this to auto-enter rename mode + newSheetId: null, + // Index of the currently active sheet tab + activeSheet: 0, + // Chart.js args object — seeded from PHP on load, updated by useChartRefresh + chartArgs: m_chart_admin.chart_args || null, + // True while waiting for ajax_get_chart_args to respond + isRefreshing: false, + // Controls whether the WordPress Save/Publish buttons are enabled + formEnabled: false, + // True when the user clicked Save/Update while the chart was still refreshing + pendingSubmit: false, + // Static config from PHP — library-specific options for the settings form + typeOptions: m_chart_admin.type_options || [], + typeOptionNames: m_chart_admin.type_option_names || {}, + themes: m_chart_admin.themes || [], + unitTerms: m_chart_admin.unit_terms || [], + imageUrl: m_chart_admin.image_url || '' +}; +function reducer(state, action) { + switch (action.type) { + case 'SET_POST_META': + return { + ...state, + postMeta: { + ...state.postMeta, + ...action.payload + } + }; + case 'SET_SHEET_DATA': + { + const spreadsheetData = [...state.spreadsheetData]; + spreadsheetData[action.payload.index] = action.payload.data; + // Disable form immediately so a submit during the refresh window is gated + return { + ...state, + spreadsheetData, + formEnabled: false + }; + } + case 'ADD_SHEET': + { + const setNames = [...state.setNames, action.payload.name || '']; + const spreadsheetData = [...state.spreadsheetData, [['']]]; + const sheetIds = [...state.sheetIds, state.nextSheetId]; + return { + ...state, + setNames, + spreadsheetData, + sheetIds, + nextSheetId: state.nextSheetId + 1, + activeSheet: spreadsheetData.length - 1, + newSheetId: state.nextSheetId + }; + } + case 'CLEAR_NEW_SHEET_ID': + return { + ...state, + newSheetId: null + }; + case 'DELETE_SHEET': + { + if (state.spreadsheetData.length <= 1) { + return state; + } + const idx = action.payload.index; + const spreadsheetData = state.spreadsheetData.filter((_, i) => i !== idx); + const setNames = state.setNames.filter((_, i) => i !== idx); + const sheetIds = state.sheetIds.filter((_, i) => i !== idx); + const activeSheet = Math.min(state.activeSheet, spreadsheetData.length - 1); + return { + ...state, + spreadsheetData, + setNames, + sheetIds, + activeSheet + }; + } + case 'RENAME_SHEET': + { + const setNames = [...state.setNames]; + setNames[action.payload.index] = action.payload.name; + return { + ...state, + setNames + }; + } + case 'SET_ACTIVE_SHEET': + return { + ...state, + activeSheet: action.payload + }; + case 'SET_CHART_ARGS': + return { + ...state, + chartArgs: action.payload + }; + case 'SET_REFRESHING': + return { + ...state, + isRefreshing: action.payload + }; + case 'SET_FORM_ENABLED': + return { + ...state, + formEnabled: action.payload + }; + case 'SET_PENDING_SUBMIT': + return { + ...state, + pendingSubmit: action.payload + }; + case 'SET_SUBTITLE': + return { + ...state, + postMeta: { + ...state.postMeta, + subtitle: action.payload + } + }; + default: + return state; + } +} +function ChartAdminProvider({ + children +}) { + const [state, dispatch] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useReducer)(reducer, initialState); + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(ChartAdminDispatchContext.Provider, { + value: dispatch + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(ChartAdminStateContext.Provider, { + value: state + }, children)); +} + +/** + * Convenience hook — returns { state, dispatch } from the nearest provider + */ +function useChartAdmin() { + const state = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useContext)(ChartAdminStateContext); + const dispatch = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useContext)(ChartAdminDispatchContext); + if (!state) { + throw new Error('useChartAdmin must be used within a ChartAdminProvider'); + } + return { + state, + dispatch + }; +} + +/** + * Dispatch-only hook — subscribes only to the dispatch context + * Components using this hook will not re-render on state changes + */ +function useChartDispatch() { + const dispatch = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useContext)(ChartAdminDispatchContext); + if (!dispatch) { + throw new Error('useChartDispatch must be used within a ChartAdminProvider'); + } + return dispatch; +} + +/***/ }, + +/***/ "./components/admin-ui-src/hooks/useChartRefresh.js" +/*!**********************************************************!*\ + !*** ./components/admin-ui-src/hooks/useChartRefresh.js ***! + \**********************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ useChartRefresh: () => (/* binding */ useChartRefresh) +/* harmony export */ }); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); + + + +/** + * Fires an AJAX request to get updated chart args whenever postMeta, spreadsheetData, setNames, or title changes + * We pass title as a parameter because it's core WP and not present in the React environment + * + * @param {string} title The current post title (read from #title DOM input). + */ +function useChartRefresh(title) { + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_1__.useChartAdmin)(); + const { + postId, + nonce, + ajaxUrl, + library, + postMeta, + spreadsheetData, + setNames, + performance, + imageSupport, + chartArgs + } = state; + const timerRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); + const abortRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); + const isFirstRun = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)(true); + + // Keep a ref to the values that aren't in the effect deps so the async callback + // always reads the latest version without needing them in the deps array + const latestRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); + latestRef.current = { + postId, + nonce, + ajaxUrl, + library, + performance, + imageSupport + }; + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + // On first run we want to skip rendering since the chart will already be rendered + // But only if it's not a brand new chart (chartArgs being null indcates chart is new) + if (isFirstRun.current && null !== chartArgs) { + isFirstRun.current = false; + return; + } + + // Cancel any pending debounce + if (timerRef.current) { + clearTimeout(timerRef.current); + } + timerRef.current = setTimeout(async () => { + // This should cancel any currently running requests so only the most recent request is run + if (abortRef.current) { + abortRef.current.abort(); + } + abortRef.current = new AbortController(); + + // Read from the ref so the async body always has the latest values even if + // the component re-rendered between when the timeout was scheduled and when it fires + const { + postId, + nonce, + ajaxUrl, + library, + performance, + imageSupport + } = latestRef.current; + dispatch({ + type: 'SET_REFRESHING', + payload: true + }); + dispatch({ + type: 'SET_FORM_ENABLED', + payload: false + }); + try { + // Start buidling the values we'll send to the m_chart_get_chart_args endpoint + const body = new URLSearchParams(); + body.append('post_id', postId); + body.append('nonce', nonce); + body.append('library', library); + body.append('title', title || ''); + + // Build post_meta matching the format the m_chart_get_chart_args expects + // Exclude set_names since it is sent separately as indexed PHP array values + const meta = { + ...postMeta + }; + delete meta.set_names; + meta.data = JSON.stringify(spreadsheetData); + Object.entries(meta).forEach(([key, val]) => { + let serialized; + if (typeof val === 'boolean') { + // PHP's (boolean) cast treats any non-empty string as true, including "false" + // Use 1/0 so unchecked checkboxes are correctly read as false + serialized = val ? '1' : '0'; + } else if (typeof val === 'object' && val !== null) { + serialized = JSON.stringify(val); + } else { + serialized = val !== null && val !== void 0 ? val : ''; + } + body.append(`post_meta[${key}]`, serialized); + }); + + // set_names must arrive in PHP as an array, not a JSON string + // Sending post_meta[set_names][0], [1], … lets PHP parse it as an array + (setNames || []).forEach((name, i) => { + body.append(`post_meta[set_names][${i}]`, name); + }); + + // Make the actual request to the endpoint + const response = await fetch(ajaxUrl + '?action=m_chart_get_chart_args', { + method: 'POST', + body, + signal: abortRef.current.signal + }); + const json = await response.json(); + + // If the request succeeded we dispatch the returned data nd then trigger the m_chart.chart_args_success hook and pass it the new data and postId + if (json.success) { + dispatch({ + type: 'SET_CHART_ARGS', + payload: json.data + }); + if (window.wp && window.wp.hooks) { + window.wp.hooks.doAction('m_chart.chart_args_success', json.data, postId); + } + + // If no image generation is needed, enable the form now + // Otherwise ChartPreview's animation.onComplete enables it after capture + if ('default' !== performance || 'yes' !== imageSupport) { + dispatch({ + type: 'SET_FORM_ENABLED', + payload: true + }); + } + } + } catch (err) { + if (err.name !== 'AbortError') { + // eslint-disable-next-line no-console + console.error('m-chart: chart refresh failed', err); + } + } finally { + dispatch({ + type: 'SET_REFRESHING', + payload: false + }); + } + }, 300); + return () => { + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + }, [postMeta, spreadsheetData, setNames, title]); +} + +/***/ }, + +/***/ "./components/admin-ui-src/hooks/useFormSubmissionGuard.js" +/*!*****************************************************************!*\ + !*** ./components/admin-ui-src/hooks/useFormSubmissionGuard.js ***! + \*****************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ useFormSubmissionGuard: () => (/* binding */ useFormSubmissionGuard) +/* harmony export */ }); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); + + +const BUTTON_IDS = ['save-post', 'wp-preview', 'post-preview', 'publish']; + +/** + * Keeps the WordPress Save/Publish buttons and form submission gated on state.formEnabled + * + * When formEnabled is false: + * - Adds the 'disabled' class to the WP submit buttons + * - Blocks form submission via a submit event listener + * + * When formEnabled is true: + * - Removes the 'disabled' class from those buttons + * - Allows form submission through normally + */ +function useFormSubmissionGuard() { + const { + state + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_1__.useChartAdmin)(); + const { + formEnabled + } = state; + + // Toggle disabled class on WP buttons + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + BUTTON_IDS.forEach(id => { + const el = document.getElementById(id); + if (el) { + el.classList.toggle('disabled', !formEnabled); + } + }); + }, [formEnabled]); + + // Block form submission when not ready + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + const form = document.getElementById('post'); + if (!form) { + return; + } + function handleSubmit(e) { + if (!formEnabled) { + e.preventDefault(); + } + } + form.addEventListener('submit', handleSubmit); + return () => form.removeEventListener('submit', handleSubmit); + }, [formEnabled]); +} + +/***/ }, + +/***/ "./components/admin-ui-src/hooks/useImageGeneration.js" +/*!*************************************************************!*\ + !*** ./components/admin-ui-src/hooks/useImageGeneration.js ***! + \*************************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ useImageGeneration: () => (/* binding */ useImageGeneration) +/* harmony export */ }); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); + + + +/** + * Returns a stable `generateImage` callback that captures the current Chart.js instance as a PNG, writes it to the hidden img textarea, then re-enables the form + * + * @param {React.MutableRefObject} chartRef Ref holding the Chart.js instance + */ +function useImageGeneration(chartRef) { + const { + state, + dispatch + } = (0,_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_1__.useChartAdmin)(); + + // Keep a ref so the callback always sees the latest state without being recreated + const stateRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)(state); + stateRef.current = state; + const generateImage = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => { + const chart = chartRef.current; + if (!chart) { + return; + } + + // Non-canvas libraries (e.g. Highcharts) handle image generation + // via the m_chart.render_done action hook instead. + if (!chart.canvas) { + dispatch({ + type: 'SET_FORM_ENABLED', + payload: true + }); + return; + } + const { + imageWidth, + postMeta + } = stateRef.current; + const chartWidth = parseInt(imageWidth, 10); + const chartHeight = parseInt(postMeta.height, 10); + const canvas = chart.canvas; + const container = canvas.parentElement; + + // Resize container to image dimensions so chart fills the right area + container.style.width = chartWidth + 'px'; + container.style.height = chartHeight + 'px'; + chart.resize(); + + // Fill solid white background (canvas is transparent by default) + const ctx = canvas.getContext('2d'); + ctx.save(); + ctx.globalCompositeOperation = 'destination-over'; + ctx.fillStyle = 'white'; + ctx.fillRect(0, 0, chart.width, chart.height); + ctx.restore(); + + // Capture PNG. + const img = chart.toBase64Image('image/png', 1); + + // Restore container to natural dimensions + container.style.width = ''; + container.style.height = ''; + chart.resize(); + + // Write base64 string to the hidden textarea for form POST + const imgEl = document.getElementById('m-chart-img'); + if (imgEl) { + imgEl.value = img; + } + + // Re-enable form submission. + dispatch({ + type: 'SET_FORM_ENABLED', + payload: true + }); + }, [chartRef, dispatch]); // eslint-disable-line react-hooks/exhaustive-deps + + return generateImage; +} + +/***/ }, + +/***/ "./components/admin-ui-src/hooks/useLongPress.js" +/*!*******************************************************!*\ + !*** ./components/admin-ui-src/hooks/useLongPress.js ***! + \*******************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ useLongPress: () => (/* binding */ useLongPress) +/* harmony export */ }); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__); + +const LONG_PRESS_DELAY = 500; + +/** + * Returns pointer-event handlers that fire `callback` after a sustained press + * Spread the returned object onto any element:
+ */ +function useLongPress(callback) { + const timerRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); + const cancel = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => { + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = null; + } + }, []); + const start = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useCallback)(e => { + // Only respond to primary pointer (left-click / first touch) + if (e.button !== undefined && e.button !== 0) { + return; + } + cancel(); + timerRef.current = setTimeout(() => { + timerRef.current = null; + callback(e); + }, LONG_PRESS_DELAY); + }, [callback, cancel]); + return { + onPointerDown: start, + onPointerUp: cancel, + onPointerLeave: cancel, + onPointerCancel: cancel + }; +} + +/***/ }, + +/***/ "./components/admin-ui-src/utils/measureTextWidth.js" +/*!***********************************************************!*\ + !*** ./components/admin-ui-src/utils/measureTextWidth.js ***! + \***********************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ measureTextWidth: () => (/* binding */ measureTextWidth) +/* harmony export */ }); +/** + * Measures the pixel width needed to display a string inside a given input element, using a canvas and the input's computed font style + * + * Falls back to a character-count estimate if the input element is not yet mounted (e.g. on the first render before refs are attached) + * + * @param {string} text The string to measure + * @param {HTMLInputElement} inputEl The input element whose font to use + * + * @return {number} Width in pixels + */ +function measureTextWidth(text, inputEl) { + if (!inputEl) { + return Math.max(40, text.length * 8 + 16); + } + const style = window.getComputedStyle(inputEl); + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + ctx.font = style.font; + const textWidth = Math.ceil(ctx.measureText(text).width) + 1; + const borderWidth = parseFloat(style.borderWidth) || 0; + const paddingLeft = parseFloat(style.paddingLeft) || 0; + const paddingRight = parseFloat(style.paddingRight) || 0; + return borderWidth * 2 + paddingLeft + textWidth + paddingRight; +} + +/***/ }, + +/***/ "react" +/*!************************!*\ + !*** external "React" ***! + \************************/ +(module) { + +module.exports = window["React"]; + +/***/ }, + +/***/ "@wordpress/element" +/*!*********************************!*\ + !*** external ["wp","element"] ***! + \*********************************/ +(module) { + +module.exports = window["wp"]["element"]; + +/***/ }, + +/***/ "@wordpress/i18n" +/*!******************************!*\ + !*** external ["wp","i18n"] ***! + \******************************/ +(module) { + +module.exports = window["wp"]["i18n"]; + +/***/ } + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ if (!(moduleId in __webpack_modules__)) { +/******/ delete __webpack_module_cache__[moduleId]; +/******/ var e = new Error("Cannot find module '" + moduleId + "'"); +/******/ e.code = 'MODULE_NOT_FOUND'; +/******/ throw e; +/******/ } +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk. +(() => { +/*!******************************************!*\ + !*** ./components/admin-ui-src/index.js ***! + \******************************************/ +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./context/ChartAdminContext */ "./components/admin-ui-src/context/ChartAdminContext.js"); +/* harmony import */ var _components_ChartMetaBox__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./components/ChartMetaBox */ "./components/admin-ui-src/components/ChartMetaBox.js"); +/* harmony import */ var _components_SpreadsheetMetaBox__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./components/SpreadsheetMetaBox */ "./components/admin-ui-src/components/SpreadsheetMetaBox.js"); +/* harmony import */ var _components_SubtitleField__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./components/SubtitleField */ "./components/admin-ui-src/components/SubtitleField.js"); +/* harmony import */ var _components_TypeAndThemeRow__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./components/TypeAndThemeRow */ "./components/admin-ui-src/components/TypeAndThemeRow.js"); +/* harmony import */ var _components_ParseAndFlagsRow__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./components/ParseAndFlagsRow */ "./components/admin-ui-src/components/ParseAndFlagsRow.js"); +/* harmony import */ var _components_AxisRows__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./components/AxisRows */ "./components/admin-ui-src/components/AxisRows.js"); +/* harmony import */ var _components_ShortcodeAndImageRow__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./components/ShortcodeAndImageRow */ "./components/admin-ui-src/components/ShortcodeAndImageRow.js"); + + + + + + + + + + + +// Expose shared context hook and settings row components for library plugins +// that implement the m_chart.settings_component filter without a build step +window.m_chart = { + useChartAdmin: _context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__.useChartAdmin, + TypeAndThemeRow: _components_TypeAndThemeRow__WEBPACK_IMPORTED_MODULE_6__["default"], + ParseAndFlagsRow: _components_ParseAndFlagsRow__WEBPACK_IMPORTED_MODULE_7__["default"], + AxisRows: _components_AxisRows__WEBPACK_IMPORTED_MODULE_8__["default"], + ShortcodeAndImageRow: _components_ShortcodeAndImageRow__WEBPACK_IMPORTED_MODULE_9__["default"] +}; + +/** + * The admin UI spans multiple meta boxes and the title area, so we use a single + * React root (in a hidden container) with portals to render into each mount point + * This ensures all components share a single ChartAdminContext instance + */ + +// Register Chart.js plugins before any chart instances are created +if (window.Chart && window.ChartDataLabels) { + window.Chart.register(window.ChartDataLabels); +} +if (window.Chart && window.MChartHelper) { + window.Chart.register(window.MChartHelper); +} +const subtitleRoot = document.getElementById('m-chart-subtitle-root'); +const spreadsheetRoot = document.getElementById('m-chart-spreadsheet-root'); +const chartRoot = document.getElementById('m-chart-chart-root'); +if (subtitleRoot || spreadsheetRoot || chartRoot) { + const App = () => (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_context_ChartAdminContext__WEBPACK_IMPORTED_MODULE_2__.ChartAdminProvider, null, subtitleRoot && (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createPortal)((0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_components_SubtitleField__WEBPACK_IMPORTED_MODULE_5__["default"], null), subtitleRoot), spreadsheetRoot && (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createPortal)((0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_components_SpreadsheetMetaBox__WEBPACK_IMPORTED_MODULE_4__["default"], null), spreadsheetRoot), chartRoot && (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createPortal)((0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_components_ChartMetaBox__WEBPACK_IMPORTED_MODULE_3__["default"], null), chartRoot)); + + // Mount the app into a hidden container appended to the post form + // All visible content is rendered via portals into the actual meta box divs + const postForm = document.getElementById('post'); + if (postForm) { + const container = document.createElement('div'); + container.id = 'm-chart-admin-ui'; + container.hidden = true; + postForm.appendChild(container); + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createRoot)(container).render((0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(App, null)); + } +} +})(); + +/******/ })() +; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/components/admin-ui/index.js.map b/components/admin-ui/index.js.map new file mode 100644 index 0000000..3aabaaf --- /dev/null +++ b/components/admin-ui/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","mappings":"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","sources":["webpack://m-chart/./components/admin-ui-src/components/AxisRows.js","webpack://m-chart/./components/admin-ui-src/components/ChartMetaBox.js","webpack://m-chart/./components/admin-ui-src/components/ChartPreview.js","webpack://m-chart/./components/admin-ui-src/components/ChartSettings.js","webpack://m-chart/./components/admin-ui-src/components/CsvControls.js","webpack://m-chart/./components/admin-ui-src/components/JspreadsheetWrapper.js","webpack://m-chart/./components/admin-ui-src/components/ParseAndFlagsRow.js","webpack://m-chart/./components/admin-ui-src/components/SheetTab.js","webpack://m-chart/./components/admin-ui-src/components/SheetTabs.js","webpack://m-chart/./components/admin-ui-src/components/ShortcodeAndImageRow.js","webpack://m-chart/./components/admin-ui-src/components/SpreadsheetMetaBox.js","webpack://m-chart/./components/admin-ui-src/components/SubtitleField.js","webpack://m-chart/./components/admin-ui-src/components/TypeAndThemeRow.js","webpack://m-chart/./components/admin-ui-src/context/ChartAdminContext.js","webpack://m-chart/./components/admin-ui-src/hooks/useChartRefresh.js","webpack://m-chart/./components/admin-ui-src/hooks/useFormSubmissionGuard.js","webpack://m-chart/./components/admin-ui-src/hooks/useImageGeneration.js","webpack://m-chart/./components/admin-ui-src/hooks/useLongPress.js","webpack://m-chart/./components/admin-ui-src/utils/measureTextWidth.js","webpack://m-chart/external window \"React\"","webpack://m-chart/external window [\"wp\",\"element\"]","webpack://m-chart/external window [\"wp\",\"i18n\"]","webpack://m-chart/webpack/bootstrap","webpack://m-chart/webpack/runtime/compat get default export","webpack://m-chart/webpack/runtime/define property getters","webpack://m-chart/webpack/runtime/hasOwnProperty shorthand","webpack://m-chart/webpack/runtime/make namespace object","webpack://m-chart/./components/admin-ui-src/index.js"],"sourcesContent":["import { Fragment, useState, useCallback } from '@wordpress/element';\nimport { __ } from '@wordpress/i18n';\nimport { useChartAdmin } from '../context/ChartAdminContext';\nimport { measureTextWidth } from '../utils/measureTextWidth';\n\n// Chart types that show y-min controls (line, spline, area only)\nconst YMIN_TYPES = new Set( [\n\t'line',\n\t'spline',\n\t'area',\n] );\n\n// Chart types that show axis title/unit rows\nconst AXIS_TYPES = new Set( [\n\t'line',\n\t'spline',\n\t'area',\n\t'column',\n\t'stacked-column',\n\t'bar',\n\t'stacked-bar',\n\t'scatter',\n\t'bubble',\n] );\n\nexport default function AxisRows() {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst { postMeta, unitTerms } = state;\n\n\tconst showAxis = AXIS_TYPES.has( postMeta.type );\n\tconst showYMin = YMIN_TYPES.has( postMeta.type );\n\n\t// Callback ref triggers a re-render when the input mounts, so the canvas measurement runs with the real element instead of the fallback\n\tconst [ yMinEl, setYMinEl ] = useState( null );\n\tconst yMinRef = useCallback( node => setYMinEl( node ), [] );\n\tconst yMinValue = String( postMeta.y_min_value ?? 0 );\n\tconst yMinWidth = yMinEl ? ( measureTextWidth( yMinValue, yMinEl ) + 20 ) + 'px' : '73px';\n\n\tfunction handleChange( field, value ) {\n\t\tdispatch( { type: 'SET_POST_META', payload: { [ field ]: value } } );\n\t}\n\n\tfunction handleYMinCheck( checked ) {\n\t\tdispatch( { type: 'SET_POST_META', payload: { y_min: checked } } );\n\t}\n\n\t// Always render axis rows so field values survive type switches on form save.\n\t// Only hide them visually when the chart type doesn't need them.\n\tconst axisStyle = showAxis ? {} : { display: 'none' };\n\tconst yMinStyle = showAxis && showYMin ? {} : { display: 'none' };\n\n\tconst unitOptions = (\n\t\t<>\n\t\t\t\n\t\t\t{ unitTerms.map( ( { group, units } ) => (\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{ units.map( ( unit ) => (\n\t\t\t\t\t\t\n\t\t\t\t\t) ) }\n\t\t\t\t\n\t\t\t) ) }\n\t\t\n\t);\n\n\treturn (\n\t\t<>\n\t\t\t
\n\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t handleChange( 'y_title', e.target.value ) }\n\t\t\t\t\t/>\n\t\t\t\t

\n\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t handleChange( 'y_units', e.target.value ) }\n\t\t\t\t\t>\n\t\t\t\t\t\t{ unitOptions }\n\t\t\t\t\t\n\t\t\t\t

\n\t\t\t
\n\t\t\t
\n\t\t\t\t

\n\t\t\t\t\t\n\t\t\t\t\t handleChange( 'y_min_value', e.target.value ) }\n\t\t\t\t\t\tstyle={ { width: yMinWidth, minWidth: 0 } }\n\t\t\t\t\t/>\n\t\t\t\t

\n\t\t\t
\n\t\t\t
\n\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t handleChange( 'x_title', e.target.value ) }\n\t\t\t\t\t/>\n\t\t\t\t

\n\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t handleChange( 'x_units', e.target.value ) }\n\t\t\t\t\t>\n\t\t\t\t\t\t{ unitOptions }\n\t\t\t\t\t\n\t\t\t\t

\n\t\t\t
\n\t\t\n\t);\n}\n","import { useState, useEffect } from '@wordpress/element';\nimport { useChartRefresh } from '../hooks/useChartRefresh';\nimport { useFormSubmissionGuard } from '../hooks/useFormSubmissionGuard';\nimport ChartPreview from './ChartPreview';\nimport ChartSettings from './ChartSettings';\n\n/**\n * Root component for the chart meta box.\n *\n * Owns the title state (read from the classic WP #title input) and wires useChartRefresh so chart args are re-fetched whenever settings or data change\n * The subtitle input is now a React-controlled SubtitleField component mounted via a separate portal — no DOM bridge needed here.\n */\nexport default function ChartMetaBox() {\n\tconst [ title, setTitle ] = useState( () => {\n\t\tconst el = document.getElementById( 'title' );\n\t\treturn el ? el.value : '';\n\t} );\n\n\t// Keep the React title state in sync with the native WP title input\n\t// Needed because React doesn't own this input since it's created by core WordPress\n\tuseEffect( () => {\n\t\tconst el = document.getElementById( 'title' );\n\n\t\tif ( ! el ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst handler = ( e ) => setTitle( e.target.value );\n\n\t\tel.addEventListener( 'input', handler );\n\n\t\treturn () => el.removeEventListener( 'input', handler );\n\t}, [] );\n\n\tuseChartRefresh( title );\n\tuseFormSubmissionGuard();\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n","import { useEffect, useRef } from '@wordpress/element';\nimport { useChartAdmin } from '../context/ChartAdminContext';\nimport { useImageGeneration } from '../hooks/useImageGeneration';\n\n/**\n * Shallow-copies chart args to avoid mutating React state when Chart.js or MChartHelper modifies the chart config during initialization\n * Tooltip callbacks and datalabels formatter are applied by MChartHelper via its beforeUpdate hook (runs each render)\n * Bubble preprocessing runs once via beforeInit\n */\nfunction prepareArgs( args ) {\n\tif ( ! args ) {\n\t\treturn args;\n\t}\n\n\treturn {\n\t\t...args,\n\t\tdata: { ...args.data },\n\t\toptions: {\n\t\t\t...args.options,\n\t\t\tplugins: {\n\t\t\t\t...args.options?.plugins,\n\t\t\t\ttooltip: {\n\t\t\t\t\t...args.options?.plugins?.tooltip,\n\t\t\t\t},\n\t\t\t\tdatalabels: {\n\t\t\t\t\t...args.options?.plugins?.datalabels,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t};\n}\n\n/**\n * Default Chart.js renderer — create or update the Chart.js instance\n *\n * Applies chartjs-specific arg preparation before rendering\n * Returned instance is stored in chartRef by the caller\n *\n * @param {HTMLCanvasElement} canvas Target canvas element\n * @param {Object} args Raw chart args from state\n * @param {Function} onComplete Callback to fire after render completes\n * @param {Object|null} existingInstance Existing Chart.js instance, or null on first render\n *\n * @return {Object} The Chart.js instance\n */\nfunction defaultChartjsRender( canvas, args, onComplete, existingInstance ) {\n\tconst prepared = prepareArgs( args );\n\n\t// Guard against null/undefined datasets or labels (Chart.js requires arrays).\n\tif ( ! prepared.data?.datasets ) {\n\t\tprepared.data = { ...prepared.data, datasets: [] };\n\t}\n\n\tif ( null === prepared.data?.labels ) {\n\t\tprepared.data = { ...prepared.data, labels: [] };\n\t}\n\n\tconst options = {\n\t\t...prepared.options,\n\t\tanimation: { onComplete },\n\t};\n\n\t// Only create the new chart if there isn't an existing one already\n\tif ( ! existingInstance ) {\n\t\treturn new window.Chart( canvas, {\n\t\t\ttype: prepared.type,\n\t\t\tdata: prepared.data,\n\t\t\toptions,\n\t\t} );\n\t}\n\n\texistingInstance.data = prepared.data;\n\texistingInstance.config.type = prepared.type;\n\texistingInstance.options = options;\n\n\texistingInstance.update();\n\n\treturn existingInstance;\n}\n\n/**\n * React-managed chart preview for the admin meta box\n *\n * The chart instance is managed imperatively via refs and is never recreated on re-render — only updated when chartArgs changes\n *\n * Rendering is delegated via the 'm_chart.render_chart' wp.hooks filter so library plugins can replace the default Chart.js renderer\n * The filter receives ( canvas, args, onComplete, existingInstance ) as extra arguments\n * If no filter handles rendering (i.e. returns false), Chart.js is used\n *\n * The onComplete callback must be called by the renderer once the chart has finished which will fire 'm_chart.render_done' to trigger image generation and/or re-enable the form\n */\nexport default function ChartPreview() {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst { postId, chartArgs, performance, imageSupport, postMeta } = state;\n\n\tconst canvasRef = useRef( null );\n\tconst chartRef = useRef( null );\n\tconst renderFlagRef = useRef( false );\n\tconst isFirstRender = useRef( true );\n\n\t// Keep a ref so onComplete closures always see the latest values\n\tconst needsImagesRef = useRef( false );\n\tneedsImagesRef.current = ( 'default' === performance && 'yes' === imageSupport );\n\n\tconst generateImage = useImageGeneration( chartRef );\n\n\t// Cleanup — destroy chart instance on unmount\n\tuseEffect( () => {\n\t\treturn () => {\n\t\t\tif ( chartRef.current ) {\n\t\t\t\tchartRef.current.destroy();\n\t\t\t\tchartRef.current = null;\n\t\t\t}\n\t\t};\n\t}, [] );\n\n\t// Create or update the chart instance whenever chartArgs changes\n\tuseEffect( () => {\n\t\tif ( ! chartArgs || ! canvasRef.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\tfunction onComplete() {\n\t\t\t// Only fire once per update cycle\n\t\t\tif ( ! renderFlagRef.current ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\trenderFlagRef.current = false;\n\n\t\t\tif ( window.wp?.hooks ) {\n\t\t\t\twindow.wp.hooks.doAction( 'm_chart.render_done', postId, 1, chartRef.current );\n\t\t\t}\n\n\t\t\tif ( needsImagesRef.current ) {\n\t\t\t\tgenerateImage();\n\t\t\t} else {\n\t\t\t\t// No image generation — enable form submission immediately\n\t\t\t\t// This also covers the initial page load where useChartRefresh skips its first run\n\t\t\t\tdispatch( { type: 'SET_FORM_ENABLED', payload: true } );\n\t\t\t\tisFirstRender.current = false;\n\t\t\t}\n\t\t}\n\n\t\trenderFlagRef.current = true;\n\n\t\t// Allow library plugins to replace the renderer via wp.hooks\n\t\t// Plugins hook 'm_chart.render_chart' and return their chart instance\n\t\t// Returning false (the default) falls through to the built-in Chart.js renderer\n\t\tlet instance = false;\n\n\t\tif ( window.wp?.hooks ) {\n\t\t\t// See defaultChartjsRender for the filter arguments\n\t\t\tinstance = window.wp.hooks.applyFilters(\n\t\t\t\t'm_chart.render_chart',\n\t\t\t\tfalse,\n\t\t\t\tcanvasRef.current,\n\t\t\t\tchartArgs,\n\t\t\t\tonComplete,\n\t\t\t\tchartRef.current\n\t\t\t);\n\t\t}\n\n\t\tchartRef.current = ( false !== instance )\n\t\t\t? instance\n\t\t\t: defaultChartjsRender( canvasRef.current, chartArgs, onComplete, chartRef.current );\n\n\t}, [ chartArgs ] ); // eslint-disable-line react-hooks/exhaustive-deps\n\n\treturn (\n\t\t
\n\t\t\t\n\t\t
\n\t);\n}\n","import { useMemo } from '@wordpress/element';\nimport TypeAndThemeRow from './TypeAndThemeRow';\nimport ParseAndFlagsRow from './ParseAndFlagsRow';\nimport AxisRows from './AxisRows';\nimport ShortcodeAndImageRow from './ShortcodeAndImageRow';\n\nfunction DefaultSettings() {\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n\nexport default function ChartSettings() {\n\t// Allow library plugins to replace the settings component via wp.hooks\n\t// useMemo with [] ensures the filter runs once — filters are registered at load time,\n\t// so calling applyFilters on every render would return a new function reference each\n\t// time and cause React to unmount/remount the settings UI\n\tconst Settings = useMemo( () => {\n\t\treturn window.wp?.hooks\n\t\t\t? wp.hooks.applyFilters( 'm_chart.settings_component', DefaultSettings )\n\t\t\t: DefaultSettings;\n\t}, [] );\n\n\treturn (\n\t\t
\n\t\t\t\n\t\t
\n\t);\n}\n","import { useState, useRef } from '@wordpress/element';\nimport { __, sprintf } from '@wordpress/i18n';\nimport { useChartAdmin } from '../context/ChartAdminContext';\nimport { spreadsheetAutoWidth } from './JspreadsheetWrapper';\n\n/**\n * CSV import and export controls for the active spreadsheet sheet\n *\n * Import uses fetch + FormData (replaces the hidden #m-chart-csv-import-form)\n * Export uses a dynamically-created temporary form POST to trigger a file download (replaces the hidden #m-chart-csv-export-form)\n *\n * Props:\n * getActiveWorksheet {Function} Returns the active Jspreadsheet worksheet instance\n */\nexport default function CsvControls( { getActiveWorksheet } ) {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst {\n\t\tpostId,\n\t\tnonce,\n\t\tajaxUrl,\n\t\tsetNames,\n\t\tactiveSheet,\n\t\tcsvDelimiters,\n\t\tdefaultDelimiter,\n\t} = state;\n\n\tconst [ selectedFile, setSelectedFile ] = useState( null );\n\tconst [ csvDelimiter, setCsvDelimiter ] = useState( defaultDelimiter );\n\tconst [ fileError, setFileError ] = useState( false );\n\tconst [ importError, setImportError ] = useState( '' );\n\tconst [ isImporting, setIsImporting ] = useState( false );\n\n\tconst fileInputRef = useRef( null );\n\n\tfunction handleSelectFile( e ) {\n\t\te.preventDefault();\n\n\t\tsetFileError( false );\n\t\tsetImportError( '' );\n\n\t\tfileInputRef.current?.click();\n\t}\n\n\tfunction handleFileChange( e ) {\n\t\tconst file = e.target.files[ 0 ];\n\n\t\t// Make sure it's a CSV file\n\t\tif ( ! file || ! /\\.csv$/i.test( file.name ) ) {\n\t\t\tsetFileError( true );\n\t\t\tsetSelectedFile( null );\n\t\t\treturn;\n\t\t}\n\n\t\tsetFileError( false );\n\t\tsetSelectedFile( file );\n\t}\n\n\tfunction handleCancel( e ) {\n\t\te.preventDefault();\n\n\t\tsetSelectedFile( null );\n\n\t\t// We're hiding the actual file input so we need to reset it for the user\n\t\tif ( fileInputRef.current ) {\n\t\t\tfileInputRef.current.value = '';\n\t\t}\n\t}\n\n\tasync function handleImport( e ) {\n\t\te.preventDefault();\n\n\t\tif ( ! selectedFile ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Save the file value so we can reset the iput\n\t\tconst file = selectedFile;\n\n\t\t// Set the UI to show we're importing the file\n\t\tsetSelectedFile( null );\n\t\tsetIsImporting( true );\n\t\tsetImportError( '' );\n\n\t\t// Reset the actual file input back to empty\n\t\tif ( fileInputRef.current ) {\n\t\t\tfileInputRef.current.value = '';\n\t\t}\n\n\t\t// Create a form data object so we can submit it to the endpoint\n\t\tconst formData = new FormData();\n\n\t\tformData.append( 'import_csv_file', file );\n\t\tformData.append( 'post_id', postId );\n\t\tformData.append( 'csv_delimiter', csvDelimiter );\n\t\tformData.append( 'nonce', nonce );\n\n\t\t// Try submitting the data to the endpoint\n\t\ttry {\n\t\t\tconst response = await fetch( `${ ajaxUrl }?action=m_chart_import_csv`, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tbody: formData,\n\t\t\t} );\n\n\t\t\tconst json = await response.json();\n\n\t\t\tif ( ! json.success ) {\n\t\t\t\tsetImportError( json.data || __( 'Import failed', 'm-chart' ) );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Get the active worksheet\n\t\t\tconst worksheet = getActiveWorksheet();\n\n\t\t\tif ( worksheet ) {\n\t\t\t\t// Set the active worksheet to the new data\n\t\t\t\tworksheet.setData( json.data );\n\n\t\t\t\t// setData() does not trigger onafterchanges so we need to run spreadsheetAutoWidth ourselves\n\t\t\t\tspreadsheetAutoWidth( worksheet );\n\n\t\t\t\tdispatch( {\n\t\t\t\t\ttype: 'SET_SHEET_DATA',\n\t\t\t\t\tpayload: { index: activeSheet, data: worksheet.getData() },\n\t\t\t\t} );\n\t\t\t}\n\t\t} catch ( err ) {\n\t\t\tsetImportError( sprintf( __( 'Import error: %s', 'm-chart' ), err.message ) );\n\t\t} finally {\n\t\t\t// When we're done reset everything in the CSV ui back to default\n\t\t\tsetIsImporting( false );\n\t\t}\n\t}\n\n\tfunction handleExport( e ) {\n\t\te.preventDefault();\n\n\t\t// Get the active worksheet\n\t\tconst worksheet = getActiveWorksheet();\n\n\t\tif ( ! worksheet ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst data = worksheet.getData();\n\t\tconst title = document.getElementById( 'title' )?.value || '';\n\t\tconst setName = setNames[ activeSheet ] || '';\n\n\t\t// Build a FormData object so we can submit it to the endpoint\n\t\tconst formData = new FormData();\n\n\t\tformData.append( 'post_id', postId );\n\t\tformData.append( 'data', JSON.stringify( data ) );\n\t\tformData.append( 'title', title );\n\t\tformData.append( 'set_name', setName );\n\n\t\t// Create a temporary form and submit it\n\t\t// We have to do it this way to trigger a download\n\t\tconst form = document.createElement( 'form' );\n\t\tform.action = `${ ajaxUrl }?action=m_chart_export_csv`;\n\t\tform.method = 'post';\n\t\tform.style.display = 'none';\n\n\t\t// Loop through the formData and append it to the temporary form\n\t\tfor ( const [ name, value ] of formData.entries() ) {\n\t\t\tconst input = document.createElement( 'input' );\n\t\t\tinput.type = 'hidden';\n\t\t\tinput.name = name;\n\t\t\tinput.value = value;\n\t\t\tform.appendChild( input );\n\t\t}\n\n\t\t// Do the thing\n\t\tdocument.body.appendChild( form );\n\t\tform.submit();\n\t\tdocument.body.removeChild( form );\n\t}\n\n\tconst showConfirmation = selectedFile && ! isImporting;\n\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{ __( 'Export', 'm-chart' ) }\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t{ __( 'CSV Import/Export', 'm-chart' ) }
\n\t\t\t\t
\n\t\t\t\t\t{ /* Hidden native file input — triggered programmatically */ }\n\t\t\t\t\t\n\t\t\t\t\t{ /* Select File button — shown when no file is selected */ }\n\t\t\t\t\t{ ! showConfirmation && ! isImporting && (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ __( 'Select File', 'm-chart' ) }\n\t\t\t\t\t\t\n\t\t\t\t\t) }\n\t\t\t\t\t{ /* Confirmation row: Import button + delimiter select */ }\n\t\t\t\t\t{ showConfirmation && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{ __( 'Import', 'm-chart' ) }\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t setCsvDelimiter( e.target.value ) }\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{ Object.entries( csvDelimiters ).map( ( [ val, label ] ) => (\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t) ) }\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t) }\n\t\t\t\t\t{ fileError && (\n\t\t\t\t\t\t

{ __( 'You can only import CSV files', 'm-chart' ) }

\n\t\t\t\t\t) }\n\t\t\t\t\t{ importError && (\n\t\t\t\t\t\t

{ importError }

\n\t\t\t\t\t) }\n\t\t\t\t\t{ isImporting && (\n\t\t\t\t\t\t

{ __( 'Importing file', 'm-chart' ) }

\n\t\t\t\t\t) }\n\t\t\t\t\t{ /* File info + cancel — shown while a file is selected */ }\n\t\t\t\t\t{ showConfirmation && (\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ sprintf( __( 'File: %s', 'm-chart' ), selectedFile.name ) }
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{ __( 'Importing this file will replace all existing data in this sheet', 'm-chart' ) }\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t) }\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n}\n","import { useEffect, useRef } from '@wordpress/element';\nimport { useChartAdmin } from '../context/ChartAdminContext';\n\n// Jspreadsheet CE has a bunch of default menu items this is the list of the ones we actually want\nconst CONTEXT_MENU_ITEMS = [\n\t'Insert a new row before',\n\t'Insert a new row after',\n\t'Delete selected rows',\n\t'Insert a new column before',\n\t'Insert a new column after',\n\t'Delete selected columns',\n];\n\n/**\n * Resizes columns to fit their content using canvas-based text measurement\n *\n * @param {object} worksheet Jspreadsheet CE worksheet instance\n * @param {Array} [records] Subset of changed records; omit for a full refresh\n */\nexport function spreadsheetAutoWidth( worksheet, records = false ) {\n\t// If no records to refresh were passed we'll just do all of them\n\tif ( ! records ) {\n\t\trecords = worksheet.records[ 0 ];\n\t}\n\n\t// If there are no records even after the above we stop here\n\tif ( ! records || ! records.length ) {\n\t\treturn;\n\t}\n\n\tconst columns = [ ...new Set( records.map( ( r ) => r.x ) ) ];\n\tconst canvas = document.createElement( 'canvas' );\n\tconst context = canvas.getContext( '2d' );\n\n\tcolumns.forEach( ( column ) => {\n\t\tlet maxWidth = 0;\n\t\tconst padding = 13;\n\t\tconst minWidth = 100 - padding;\n\n\t\tfor ( let i = 0; i < worksheet.records.length; i++ ) {\n\t\t\tconst cell = worksheet.records[ i ]?.[ column ]?.element;\n\n\t\t\tif ( cell ) {\n\t\t\t\tcontext.font = window.getComputedStyle( cell ).font;\n\t\t\t\tconst metrics = context.measureText( cell.innerText );\n\n\t\t\t\tif ( metrics.width > maxWidth ) {\n\t\t\t\t\tmaxWidth = metrics.width;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tmaxWidth = minWidth > maxWidth ? minWidth : maxWidth;\n\n\t\tworksheet.setWidth( column, maxWidth + padding );\n\t} );\n}\n\n/**\n * Thin React wrapper around a Jspreadsheet CE worksheet\n *\n * The Jspreadsheet instance is created once on mount and never recreated on re-render\n * Show/hide between active/inactive sheets is done via CSS so that DOM state and undo history are preserved\n *\n * Props:\n * sheetId {number} Stable identity key (used for registration)\n * sheetIndex {number} Current position in the sheets array (may change after deletes)\n * isActive {boolean} Whether this sheet is currently displayed\n * data {Array} Initial 2-D array of cell values\n * onMounted {Function} Called with (sheetId, worksheetInstance) after init\n * onUnmounted {Function} Called with (sheetId) before unmount\n */\nexport default function JspreadsheetWrapper( {\n\tsheetId,\n\tsheetIndex,\n\tisActive,\n\tdata,\n\tonMounted,\n\tonUnmounted,\n} ) {\n\tconst { dispatch } = useChartAdmin();\n\tconst containerRef = useRef( null );\n\tconst worksheetRef = useRef( null );\n\n\t// Keep a ref so the onafterchanges closure always dispatches the current index\n\tconst sheetIndexRef = useRef( sheetIndex );\n\tsheetIndexRef.current = sheetIndex;\n\n\tuseEffect( () => {\n\t\tif ( ! containerRef.current || worksheetRef.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Need to load an empty data array if there's none to start already\n\t\tconst initialData = data && data.length ? data : [ [ '' ] ];\n\n\t\t// Create the sheet instance\n\t\tconst instance = window.jspreadsheet( containerRef.current, {\n\t\t\tworksheets: [ {\n\t\t\t\tdata: initialData,\n\t\t\t\tallowComments: false,\n\t\t\t\tminDimensions: [ 37, 17 ],\n\t\t\t} ],\n\t\t\t// Filter out all of the contextual menu items we don't want\n\t\t\tcontextMenu( obj, x, y, e, items ) {\n\t\t\t\treturn items.filter( ( item ) =>\n\t\t\t\t\tCONTEXT_MENU_ITEMS.includes( item.title )\n\t\t\t\t);\n\t\t\t},\n\t\t\t// Run spreadsheetAutoWidth on the intiial load\n\t\t\tonload( spreadsheet ) {\n\t\t\t\tconst ws = spreadsheet.worksheets[ spreadsheet.getWorksheetActive() ];\n\t\t\t\tspreadsheetAutoWidth( ws );\n\t\t\t},\n\t\t\t// Run spreadsheetAutoWidth on changed recrds and also push any changes to the chart\n\t\t\tonafterchanges( worksheet, records ) {\n\t\t\t\tspreadsheetAutoWidth( worksheet, records );\n\t\t\t\tdispatch( {\n\t\t\t\t\ttype: 'SET_SHEET_DATA',\n\t\t\t\t\tpayload: { index: sheetIndexRef.current, data: worksheet.getData() },\n\t\t\t\t} );\n\t\t\t},\n\t\t} );\n\n\t\tworksheetRef.current = instance[ 0 ];\n\t\tonMounted( sheetId, worksheetRef.current );\n\n\t\treturn () => {\n\t\t\tonUnmounted( sheetId );\n\t\t\tworksheetRef.current = null;\n\t\t};\n\t}, [] ); // eslint-disable-line react-hooks/exhaustive-deps\n\n\treturn (\n\t\t\n\t);\n}\n","import { __ } from '@wordpress/i18n';\nimport { useChartAdmin } from '../context/ChartAdminContext';\n\nconst PARSE_OPTION_NAMES = {\n\tcolumns: __( 'Columns', 'm-chart' ),\n\trows: __( 'Rows', 'm-chart' ),\n};\n\n// Chart types that support the shared tooltip option\nconst SHARED_TYPES = new Set( [\n\t'line',\n\t'spline',\n\t'area',\n\t'radar',\n\t'radar-area'\n] );\n\nexport default function ParseAndFlagsRow() {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst { postMeta } = state;\n\n\tconst showShared = SHARED_TYPES.has( postMeta.type );\n\n\tfunction handleChange( field, value ) {\n\t\tdispatch( { type: 'SET_POST_META', payload: { [ field ]: value } } );\n\t}\n\n\tfunction handleCheckbox( field, checked ) {\n\t\tdispatch( { type: 'SET_POST_META', payload: { [ field ]: checked } } );\n\t}\n\n\treturn (\n\t\t
\n\t\t\t

\n\t\t\t\t
\n\t\t\t\t handleChange( 'parse_in', e.target.value ) }\n\t\t\t\t>\n\t\t\t\t\t{ Object.entries( PARSE_OPTION_NAMES ).map( ( [ value, label ] ) => (\n\t\t\t\t\t\t\n\t\t\t\t\t) ) }\n\t\t\t\t\n\t\t\t

\n\t\t\t

\n\t\t\t\t{ '\\u00a0' }
\n\t\t\t\t\n\t\t\t

\n\t\t\t

\n\t\t\t\t{ '\\u00a0' }
\n\t\t\t\t\n\t\t\t

\n\t\t\t{ /* Always render shared in DOM so its value survives type switches on save */ }\n\t\t\t

\n\t\t\t\t{ '\\u00a0' }
\n\t\t\t\t\n\t\t\t

\n\t\t
\n\t);\n}\n","import { useState, useEffect, useRef } from '@wordpress/element';\nimport { useChartAdmin } from '../context/ChartAdminContext';\nimport { useLongPress } from '../hooks/useLongPress';\nimport { measureTextWidth } from '../utils/measureTextWidth';\n\n/**\n * A single sheet tab in the spreadsheet tab bar\n *\n * Supports:\n * - Click to activate\n * - Double-click or long-press (500ms) to enter rename mode\n * - Enter / blur to commit rename\n * - Dismiss icon to delete (guarded by window.confirm)\n */\nexport default function SheetTab( {\n\tsheetId,\n\tsheetIndex,\n\tname,\n\tisActive,\n\tisSingle,\n\tisNew,\n} ) {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst [ isRenaming, setIsRenaming ] = useState( () => !! isNew );\n\tconst [ inputValue, setInputValue ] = useState( name );\n\tconst inputRef = useRef( null );\n\n\tconst longPress = useLongPress( () => setIsRenaming( true ) );\n\n\t// Clear the newSheetId flag once this tab has consumed it\n\tuseEffect( () => {\n\t\tif ( isNew ) {\n\t\t\tdispatch( { type: 'CLEAR_NEW_SHEET_ID' } );\n\t\t}\n\t}, [] ); // eslint-disable-line react-hooks/exhaustive-deps\n\n\t// Sync local input value and focus when entering rename mode\n\tuseEffect( () => {\n\t\tif ( isRenaming ) {\n\t\t\tsetInputValue( name );\n\n\t\t\tif ( inputRef.current ) {\n\t\t\t\tinputRef.current.focus();\n\t\t\t\tinputRef.current.select();\n\t\t\t}\n\t\t}\n\t}, [ isRenaming ] ); // eslint-disable-line react-hooks/exhaustive-deps\n\n\tfunction handleClick( e ) {\n\t\te.preventDefault();\n\n\t\tif ( ! isActive ) {\n\t\t\tdispatch( { type: 'SET_ACTIVE_SHEET', payload: sheetIndex } );\n\t\t}\n\t}\n\n\tfunction handleDoubleClick( e ) {\n\t\te.preventDefault();\n\n\t\tsetIsRenaming( true );\n\t}\n\n\tfunction handleDelete( e ) {\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\n\t\t// If there's only one tab we don't let the user delete it\n\t\tif ( isSingle ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If user rejects teh confirmation we stop\n\t\tif ( ! window.confirm( state.deleteConfirm ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Activate a neighbouring sheet before deleting so the active index stays valid.\n\t\tif ( isActive ) {\n\t\t\tconst newActive = sheetIndex > 0 ? sheetIndex - 1 : 1;\n\n\t\t\tdispatch( { type: 'SET_ACTIVE_SHEET', payload: newActive } );\n\t\t}\n\n\t\tdispatch( { type: 'DELETE_SHEET', payload: { index: sheetIndex } } );\n\t}\n\n\tfunction handleNameChange( e ) {\n\t\tsetInputValue( e.target.value );\n\t}\n\n\tfunction commitRename() {\n\t\tdispatch( {\n\t\t\ttype: 'RENAME_SHEET',\n\t\t\tpayload: { index: sheetIndex, name: inputValue },\n\t\t} );\n\n\t\tsetIsRenaming( false );\n\t}\n\n\tfunction handleKeyDown( e ) {\n\t\tif ( e.key === 'Enter' ) {\n\t\t\te.preventDefault();\n\t\t\tcommitRename();\n\t\t}\n\t}\n\n\tconst inputWidth = inputRef.current\n\t\t? measureTextWidth( inputValue, inputRef.current ) + 'px'\n\t\t: Math.max( 40, inputValue.length * 8 + 16 ) + 'px';\n\n\tconst className = [\n\t\t'nav-tab',\n\t\tisActive ? 'nav-tab-active' : '',\n\t\tisSingle ? 'do-not-delete' : '',\n\t].filter( Boolean ).join( ' ' );\n\n\treturn (\n\t\t\n\t\t\t{ ! isSingle && (\n\t\t\t\t\n\t\t\t) }\n\t\t\t\n\t\t\t\t{ name }\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n","import { useMemo } from '@wordpress/element';\nimport { __, sprintf } from '@wordpress/i18n';\nimport { useChartAdmin } from '../context/ChartAdminContext';\nimport SheetTab from './SheetTab';\n\n/**\n * Chart types that support multiple data sets (multiple spreadsheet sheets)\n * All other types use a single sheet and the tab bar is hidden\n */\nconst MULTI_SHEET_TYPES = new Set( [\n\t'scatter',\n\t'bubble',\n\t'radar',\n\t'radar-area',\n] );\n\n/**\n * The spreadsheet tab bar\n * Renders one SheetTab per sheet and an Add Sheet button\n * The entire bar is hidden when the current chart type only supports a single data set\n */\nexport default function SheetTabs() {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst { postMeta, sheetIds, setNames, activeSheet, newSheetId } = state;\n\n\t// Allow library plugins to customize which chart types support multiple sheets\n\t// useMemo with [] ensures the filter runs once — filters are registered at load time\n\t// and the resulting Set is stable, so showTabs only re-evaluates when postMeta.type changes\n\tconst multiSheetTypes = useMemo( () => {\n\t\tconst types = window.wp?.hooks\n\t\t\t? wp.hooks.applyFilters( 'm_chart.multi_sheet_types', [ ...MULTI_SHEET_TYPES ] )\n\t\t\t: [ ...MULTI_SHEET_TYPES ];\n\t\treturn new Set( types );\n\t}, [] );\n\n\tconst showTabs = multiSheetTypes.has( postMeta.type );\n\n\tfunction handleAddSheet( e ) {\n\t\te.preventDefault();\n\n\t\tdispatch( { type: 'ADD_SHEET', payload: {} } );\n\t}\n\n\treturn (\n\t\t\n\t);\n}\n","import { useState } from '@wordpress/element';\nimport { __ } from '@wordpress/i18n';\nimport { useChartAdmin } from '../context/ChartAdminContext';\n\nexport default function ShortcodeAndImageRow() {\n\tconst { state } = useChartAdmin();\n\tconst { postId, postMeta, imageUrl, performance, imageSupport } = state;\n\n\tconst shortcode = `[chart id=\"${ postId }\"]`;\n\n\tconst showImageField = 'default' === performance && 'yes' === imageSupport;\n\tconst imageDisabled = ! showImageField;\n\n\tconst [ copied, setCopied ] = useState( false );\n\n\tfunction handleCopy() {\n\t\tnavigator.clipboard.writeText( shortcode ).then( () => {\n\t\t\tsetCopied( true );\n\t\t\tsetTimeout( () => setCopied( false ), 2000 );\n\t\t} );\n\t}\n\n\treturn (\n\t\t
\n\t\t\t

\n\t\t\t\t
\n\t\t\t\t e.target.select() }\n\t\t\t\t\treadOnly\n\t\t\t\t/>\n\t\t\t\t\n\t\t\t

\n\t\t\t

\n\t\t\t\t
\n\t\t\t\t{ imageUrl ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t e.target.select() }\n\t\t\t\t\t\t\treadOnly\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{ __( 'View', 'm-chart' ) }\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t) : imageDisabled ? (\n\t\t\t\t\t{ __( 'Image generation is disabled', 'm-chart' ) }\n\t\t\t\t) : (\n\t\t\t\t\t{ __( 'Save/Update this post to generate the image version', 'm-chart' ) }\n\t\t\t\t) }\n\t\t\t

\n\t\t\t\n\t\t
\n\t);\n}\n","import { useEffect, useRef, useCallback } from '@wordpress/element';\nimport { useChartAdmin } from '../context/ChartAdminContext';\nimport JspreadsheetWrapper from './JspreadsheetWrapper';\nimport SheetTabs from './SheetTabs';\nimport CsvControls from './CsvControls';\n\n// WordPress submit button IDs that should trigger save behavior\nconst SUBMIT_BUTTON_IDS = [ 'publish', 'save-post' ];\n\n/**\n * Container for the spreadsheet meta box\n *\n * Manages Jspreadsheet worksheet instances via a ref map keyed by stable sheet ID\n * Handles form submission: writes all sheet data to the hidden textarea[name=\"m-chart[data]\"] before the post form is submitted\n */\nexport default function SpreadsheetMetaBox() {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst { sheetIds, spreadsheetData, activeSheet, formEnabled, pendingSubmit } = state;\n\n\t// Map of stable sheetId → worksheet instance (Jspreadsheet worksheet object)\n\tconst worksheetInstances = useRef( {} );\n\n\t// Refs so event handlers always see the latest values without needing to be recreated\n\tconst formEnabledRef = useRef( formEnabled );\n\tconst sheetIdsRef = useRef( sheetIds );\n\n\tformEnabledRef.current = formEnabled;\n\tsheetIdsRef.current = sheetIds;\n\n\t// Called by JspreadsheetWrapper after it initialises its jspreadsheet instance\n\tconst handleMounted = useCallback( ( sheetId, worksheet ) => {\n\t\tworksheetInstances.current[ sheetId ] = worksheet;\n\t}, [] );\n\n\t// Called by JspreadsheetWrapper just before it unmounts\n\tconst handleUnmounted = useCallback( ( sheetId ) => {\n\t\tdelete worksheetInstances.current[ sheetId ];\n\t}, [] );\n\n\t// Returns the worksheet instance for the currently active sheet\n\tconst getActiveWorksheet = useCallback( () => {\n\t\tconst activeId = sheetIdsRef.current[ state.activeSheet ];\n\t\treturn worksheetInstances.current[ activeId ] ?? null;\n\t}, [ state.activeSheet ] );\n\n\t// Writes all sheet data to the hidden textarea so the form POST includes it\n\tconst writeDataToForm = useCallback( () => {\n\t\tconst form = document.getElementById( 'post' );\n\n\t\tif ( ! form ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst allData = sheetIdsRef.current.map(\n\t\t\t( id ) => worksheetInstances.current[ id ]?.getData() ?? [ [ '' ] ]\n\t\t);\n\n\t\tconst dataTextarea = form.querySelector( 'textarea[name=\"m-chart[data]\"]' );\n\n\t\tif ( dataTextarea ) {\n\t\t\tdataTextarea.value = JSON.stringify( allData );\n\t\t}\n\t}, [] );\n\n\t// When formEnabled becomes true while a submit is pending, submit the form\n\t// Uses form.submit() to bypass event handlers since the data textarea is already written\n\tuseEffect( () => {\n\t\tif ( formEnabled && pendingSubmit ) {\n\t\t\tdispatch( { type: 'SET_PENDING_SUBMIT', payload: false } );\n\n\t\t\t// Write latest data right before submitting\n\t\t\twriteDataToForm();\n\n\t\t\tconst form = document.getElementById( 'post' );\n\n\t\t\tif ( form ) {\n\t\t\t\tform.submit();\n\t\t\t}\n\t\t}\n\t}, [ formEnabled, pendingSubmit, dispatch, writeDataToForm ] );\n\n\t// Detect submit intent at the click event on WP submit buttons\n\t// Click fires AFTER blur but BEFORE the form's submit event\n\t// This is the earliest reliable detection point\n\tuseEffect( () => {\n\t\tfunction handleClick( e ) {\n\t\t\t// If the chart is still refreshing, intercept the click to defer submission\n\t\t\t// The form submit event hasn't fired yet so we prevent the default click behavior\n\t\t\tif ( ! formEnabledRef.current ) {\n\t\t\t\te.preventDefault();\n\t\t\t\twriteDataToForm();\n\t\t\t\tdispatch( { type: 'SET_PENDING_SUBMIT', payload: true } );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Form is ready — write data and let the normal submit flow proceed\n\t\t\twriteDataToForm();\n\t\t}\n\n\t\tconst buttons = SUBMIT_BUTTON_IDS\n\t\t\t.map( ( id ) => document.getElementById( id ) )\n\t\t\t.filter( Boolean );\n\n\t\tbuttons.forEach( ( btn ) => btn.addEventListener( 'click', handleClick ) );\n\n\t\treturn () => {\n\t\t\tbuttons.forEach( ( btn ) => btn.removeEventListener( 'click', handleClick ) );\n\t\t};\n\t}, [ dispatch, writeDataToForm ] );\n\n\t// Intercept the form submit event as a fallback\n\t// Ensures the data textarea is always written before submission regardless of how\n\t// the submit was triggered (keyboard, other plugins, etc)\n\tuseEffect( () => {\n\t\tconst form = document.getElementById( 'post' );\n\n\t\tif ( ! form ) {\n\t\t\treturn;\n\t\t}\n\n\t\tfunction handleSubmit( e ) {\n\t\t\t// If chart is still refreshing, block this submit — the click handler\n\t\t\t// already set pendingSubmit so it will auto-submit when ready\n\t\t\tif ( ! formEnabledRef.current ) {\n\t\t\t\te.preventDefault();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Write data in case the submit wasn't triggered via our click handler\n\t\t\twriteDataToForm();\n\t\t}\n\n\t\tform.addEventListener( 'submit', handleSubmit );\n\t\treturn () => form.removeEventListener( 'submit', handleSubmit );\n\t}, [ writeDataToForm ] );\n\n\treturn (\n\t\t<>\n\t\t\t\n\t\t\t
\n\t\t\t\t{ sheetIds.map( ( id, index ) => (\n\t\t\t\t\t\n\t\t\t\t) ) }\n\t\t\t
\n\t\t\t\n\t\t\n\t);\n}\n","import { __ } from '@wordpress/i18n';\nimport { useChartAdmin } from '../context/ChartAdminContext';\n\n/**\n * Reach controlled subtitle input\n *\n * Renders with the m-chart[subtitle] name attribute so the value is submitted with the native WordPress post form and the existing PHP save logic still works\n */\nexport default function SubtitleField() {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst value = state.postMeta?.subtitle ?? '';\n\n\treturn (\n\t\t\n\t\t\t\tdispatch( { type: 'SET_SUBTITLE', payload: e.target.value } )\n\t\t\t}\n\t\t/>\n\t);\n}\n","import { useState, useCallback } from '@wordpress/element';\nimport { __ } from '@wordpress/i18n';\nimport { useChartAdmin } from '../context/ChartAdminContext';\nimport { measureTextWidth } from '../utils/measureTextWidth';\n\nexport default function TypeAndThemeRow() {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst { postMeta, typeOptions, typeOptionNames, themes } = state;\n\n\tconst [ heightEl, setHeightEl ] = useState( null );\n\tconst heightRef = useCallback( node => setHeightEl( node ), [] );\n\tconst heightValue = String( postMeta.height ?? '' );\n\tconst heightWidth = heightEl\n\t\t? ( measureTextWidth( heightValue, heightEl ) + 20 ) + 'px'\n\t\t: '73px';\n\n\tfunction handleChange( field, value ) {\n\t\tdispatch( { type: 'SET_POST_META', payload: { [ field ]: value } } );\n\t}\n\n\treturn (\n\t\t
\n\t\t\t

\n\t\t\t\t
\n\t\t\t\t handleChange( 'type', e.target.value ) }\n\t\t\t\t>\n\t\t\t\t\t{ typeOptions.map( ( type ) => (\n\t\t\t\t\t\t\n\t\t\t\t\t) ) }\n\t\t\t\t\n\t\t\t

\n\t\t\t

\n\t\t\t\t
\n\t\t\t\t handleChange( 'theme', e.target.value ) }\n\t\t\t\t>\n\t\t\t\t\t{ themes.map( ( theme ) => (\n\t\t\t\t\t\t\n\t\t\t\t\t) ) }\n\t\t\t\t\n\t\t\t

\n\t\t\t

\n\t\t\t\t
\n\t\t\t\t handleChange( 'height', e.target.value ) }\n\t\t\t\t\tstyle={ { width: heightWidth, minWidth: 0 } }\n\t\t\t\t/>\n\t\t\t

\n\t\t
\n\t);\n}\n","import { createContext, useContext, useReducer } from '@wordpress/element';\n\nconst ChartAdminStateContext = createContext( null );\nconst ChartAdminDispatchContext = createContext( null );\n\nconst { m_chart_admin } = window;\n\n/**\n * Initial state populated from the PHP-localised window.m_chart_admin object\n *\n * post_meta contains all chart meta fields except 'data' (spreadsheetData holds that)\n * Fields mirror the PHP $chart_meta_fields defaults in class-m-chart.php\n */\n\n// Stable sheet IDs — never change once a sheet is created, survive deletion of siblings\nconst initialSheetCount = ( m_chart_admin.spreadsheet_data || [ [] ] ).length;\nconst initialSheetIds = Array.from( { length: initialSheetCount }, ( _, i ) => i );\n\nconst initialState = {\n\tslug: m_chart_admin.slug,\n\tpostId: m_chart_admin.post_id,\n\tnonce: m_chart_admin.nonce,\n\tajaxUrl: m_chart_admin.ajax_url,\n\tlibrary: m_chart_admin.library,\n\tperformance: m_chart_admin.performance,\n\timageSupport: m_chart_admin.image_support,\n\tinstantPreview: m_chart_admin.instant_preview_support,\n\timageMultiplier: m_chart_admin.image_multiplier,\n\timageWidth: m_chart_admin.image_width,\n\tdeleteConfirm: m_chart_admin.delete_confirm,\n\tcsvDelimiters: m_chart_admin.csv_delimiters || { ',': 'Comma' },\n\tdefaultDelimiter: m_chart_admin.default_delimiter || ',',\n\n\t// Chart meta fields (type, theme, height, parse_in, labels, legend, etc.)\n\tpostMeta: m_chart_admin.post_meta,\n\n\t// Array of 2D arrays, one per sheet (matches Jspreadsheet's getData() format)\n\tspreadsheetData: m_chart_admin.spreadsheet_data,\n\n\t// Array of human-readable names for each sheet tab\n\tsetNames: m_chart_admin.set_names || [],\n\n\t// Stable IDs for each sheet — used as React keys to avoid spurious remounts\n\tsheetIds: initialSheetIds,\n\n\t// Counter for the next sheet ID to assign\n\tnextSheetId: initialSheetCount,\n\n\t// ID of the most recently added sheet — SheetTab uses this to auto-enter rename mode\n\tnewSheetId: null,\n\n\t// Index of the currently active sheet tab\n\tactiveSheet: 0,\n\n\t// Chart.js args object — seeded from PHP on load, updated by useChartRefresh\n\tchartArgs: m_chart_admin.chart_args || null,\n\n\t// True while waiting for ajax_get_chart_args to respond\n\tisRefreshing: false,\n\n\t// Controls whether the WordPress Save/Publish buttons are enabled\n\tformEnabled: false,\n\n\t// True when the user clicked Save/Update while the chart was still refreshing\n\tpendingSubmit: false,\n\n\t// Static config from PHP — library-specific options for the settings form\n\ttypeOptions: m_chart_admin.type_options || [],\n\ttypeOptionNames: m_chart_admin.type_option_names || {},\n\tthemes: m_chart_admin.themes || [],\n\tunitTerms: m_chart_admin.unit_terms || [],\n\timageUrl: m_chart_admin.image_url || '',\n};\n\nfunction reducer( state, action ) {\n\tswitch ( action.type ) {\n\t\tcase 'SET_POST_META':\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\tpostMeta: { ...state.postMeta, ...action.payload },\n\t\t\t};\n\n\t\tcase 'SET_SHEET_DATA': {\n\t\t\tconst spreadsheetData = [ ...state.spreadsheetData ];\n\t\t\tspreadsheetData[ action.payload.index ] = action.payload.data;\n\t\t\t// Disable form immediately so a submit during the refresh window is gated\n\t\t\treturn { ...state, spreadsheetData, formEnabled: false };\n\t\t}\n\n\t\tcase 'ADD_SHEET': {\n\t\t\tconst setNames = [ ...state.setNames, action.payload.name || '' ];\n\t\t\tconst spreadsheetData = [ ...state.spreadsheetData, [ [ '' ] ] ];\n\t\t\tconst sheetIds = [ ...state.sheetIds, state.nextSheetId ];\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\tsetNames,\n\t\t\t\tspreadsheetData,\n\t\t\t\tsheetIds,\n\t\t\t\tnextSheetId: state.nextSheetId + 1,\n\t\t\t\tactiveSheet: spreadsheetData.length - 1,\n\t\t\t\tnewSheetId: state.nextSheetId,\n\t\t\t};\n\t\t}\n\n\t\tcase 'CLEAR_NEW_SHEET_ID':\n\t\t\treturn { ...state, newSheetId: null };\n\n\t\tcase 'DELETE_SHEET': {\n\t\t\tif ( state.spreadsheetData.length <= 1 ) {\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\tconst idx = action.payload.index;\n\t\t\tconst spreadsheetData = state.spreadsheetData.filter( ( _, i ) => i !== idx );\n\t\t\tconst setNames = state.setNames.filter( ( _, i ) => i !== idx );\n\t\t\tconst sheetIds = state.sheetIds.filter( ( _, i ) => i !== idx );\n\t\t\tconst activeSheet = Math.min( state.activeSheet, spreadsheetData.length - 1 );\n\t\t\treturn { ...state, spreadsheetData, setNames, sheetIds, activeSheet };\n\t\t}\n\n\t\tcase 'RENAME_SHEET': {\n\t\t\tconst setNames = [ ...state.setNames ];\n\t\t\tsetNames[ action.payload.index ] = action.payload.name;\n\t\t\treturn { ...state, setNames };\n\t\t}\n\n\t\tcase 'SET_ACTIVE_SHEET':\n\t\t\treturn { ...state, activeSheet: action.payload };\n\n\t\tcase 'SET_CHART_ARGS':\n\t\t\treturn { ...state, chartArgs: action.payload };\n\n\t\tcase 'SET_REFRESHING':\n\t\t\treturn { ...state, isRefreshing: action.payload };\n\n\t\tcase 'SET_FORM_ENABLED':\n\t\t\treturn { ...state, formEnabled: action.payload };\n\n\t\tcase 'SET_PENDING_SUBMIT':\n\t\t\treturn { ...state, pendingSubmit: action.payload };\n\n\t\tcase 'SET_SUBTITLE':\n\t\t\treturn {\n\t\t\t\t...state,\n\t\t\t\tpostMeta: { ...state.postMeta, subtitle: action.payload },\n\t\t\t};\n\n\t\tdefault:\n\t\t\treturn state;\n\t}\n}\n\nexport function ChartAdminProvider( { children } ) {\n\tconst [ state, dispatch ] = useReducer( reducer, initialState );\n\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t{ children }\n\t\t\t\n\t\t\n\t);\n}\n\n/**\n * Convenience hook — returns { state, dispatch } from the nearest provider\n */\nexport function useChartAdmin() {\n\tconst state = useContext( ChartAdminStateContext );\n\tconst dispatch = useContext( ChartAdminDispatchContext );\n\n\tif ( ! state ) {\n\t\tthrow new Error( 'useChartAdmin must be used within a ChartAdminProvider' );\n\t}\n\n\treturn { state, dispatch };\n}\n\n/**\n * Dispatch-only hook — subscribes only to the dispatch context\n * Components using this hook will not re-render on state changes\n */\nexport function useChartDispatch() {\n\tconst dispatch = useContext( ChartAdminDispatchContext );\n\n\tif ( ! dispatch ) {\n\t\tthrow new Error( 'useChartDispatch must be used within a ChartAdminProvider' );\n\t}\n\n\treturn dispatch;\n}\n","import { useEffect, useRef } from '@wordpress/element';\nimport { useChartAdmin } from '../context/ChartAdminContext';\n\n/**\n * Fires an AJAX request to get updated chart args whenever postMeta, spreadsheetData, setNames, or title changes\n * We pass title as a parameter because it's core WP and not present in the React environment\n *\n * @param {string} title The current post title (read from #title DOM input).\n */\nexport function useChartRefresh( title ) {\n\tconst { state, dispatch } = useChartAdmin();\n\tconst {\n\t\tpostId, nonce, ajaxUrl, library,\n\t\tpostMeta, spreadsheetData, setNames,\n\t\tperformance, imageSupport,\n\t\tchartArgs,\n\t} = state;\n\n\tconst timerRef = useRef( null );\n\tconst abortRef = useRef( null );\n\tconst isFirstRun = useRef( true );\n\n\t// Keep a ref to the values that aren't in the effect deps so the async callback\n\t// always reads the latest version without needing them in the deps array\n\tconst latestRef = useRef( null );\n\tlatestRef.current = { postId, nonce, ajaxUrl, library, performance, imageSupport };\n\n\tuseEffect( () => {\n\t\t// On first run we want to skip rendering since the chart will already be rendered\n\t\t// But only if it's not a brand new chart (chartArgs being null indcates chart is new)\n\t\tif ( isFirstRun.current && null !== chartArgs ) {\n\t\t\tisFirstRun.current = false;\n\t\t\t\n\t\t\treturn;\n\t\t}\n\n\t\t// Cancel any pending debounce\n\t\tif ( timerRef.current ) {\n\t\t\tclearTimeout( timerRef.current );\n\t\t}\n\n\t\ttimerRef.current = setTimeout( async () => {\n\t\t\t// This should cancel any currently running requests so only the most recent request is run\n\t\t\tif ( abortRef.current ) {\n\t\t\t\tabortRef.current.abort();\n\t\t\t}\n\n\t\t\tabortRef.current = new AbortController();\n\n\t\t\t// Read from the ref so the async body always has the latest values even if\n\t\t\t// the component re-rendered between when the timeout was scheduled and when it fires\n\t\t\tconst { postId, nonce, ajaxUrl, library, performance, imageSupport } = latestRef.current;\n\n\t\t\tdispatch( { type: 'SET_REFRESHING', payload: true } );\n\t\t\tdispatch( { type: 'SET_FORM_ENABLED', payload: false } );\n\n\t\t\ttry {\n\t\t\t\t// Start buidling the values we'll send to the m_chart_get_chart_args endpoint\n\t\t\t\tconst body = new URLSearchParams();\n\t\t\t\tbody.append( 'post_id', postId );\n\t\t\t\tbody.append( 'nonce', nonce );\n\t\t\t\tbody.append( 'library', library );\n\t\t\t\tbody.append( 'title', title || '' );\n\n\t\t\t\t// Build post_meta matching the format the m_chart_get_chart_args expects\n\t\t\t\t// Exclude set_names since it is sent separately as indexed PHP array values\n\t\t\t\tconst meta = { ...postMeta };\n\t\t\t\tdelete meta.set_names;\n\t\t\t\tmeta.data = JSON.stringify( spreadsheetData );\n\n\t\t\t\tObject.entries( meta ).forEach( ( [ key, val ] ) => {\n\t\t\t\t\tlet serialized;\n\n\t\t\t\t\tif ( typeof val === 'boolean' ) {\n\t\t\t\t\t\t// PHP's (boolean) cast treats any non-empty string as true, including \"false\"\n\t\t\t\t\t\t// Use 1/0 so unchecked checkboxes are correctly read as false\n\t\t\t\t\t\tserialized = val ? '1' : '0';\n\t\t\t\t\t} else if ( typeof val === 'object' && val !== null ) {\n\t\t\t\t\t\tserialized = JSON.stringify( val );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tserialized = val ?? '';\n\t\t\t\t\t}\n\n\t\t\t\t\tbody.append( `post_meta[${ key }]`, serialized );\n\t\t\t\t} );\n\n\t\t\t\t// set_names must arrive in PHP as an array, not a JSON string\n\t\t\t\t// Sending post_meta[set_names][0], [1], … lets PHP parse it as an array\n\t\t\t\t( setNames || [] ).forEach( ( name, i ) => {\n\t\t\t\t\tbody.append( `post_meta[set_names][${ i }]`, name );\n\t\t\t\t} );\n\n\t\t\t\t// Make the actual request to the endpoint\n\t\t\t\tconst response = await fetch( ajaxUrl + '?action=m_chart_get_chart_args', {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tbody,\n\t\t\t\t\tsignal: abortRef.current.signal,\n\t\t\t\t} );\n\n\t\t\t\tconst json = await response.json();\n\n\t\t\t\t// If the request succeeded we dispatch the returned data nd then trigger the m_chart.chart_args_success hook and pass it the new data and postId\n\t\t\t\tif ( json.success ) {\n\t\t\t\t\tdispatch( { type: 'SET_CHART_ARGS', payload: json.data } );\n\n\t\t\t\t\tif ( window.wp && window.wp.hooks ) {\n\t\t\t\t\t\twindow.wp.hooks.doAction( 'm_chart.chart_args_success', json.data, postId );\n\t\t\t\t\t}\n\n\t\t\t\t\t// If no image generation is needed, enable the form now\n\t\t\t\t\t// Otherwise ChartPreview's animation.onComplete enables it after capture\n\t\t\t\t\tif ( 'default' !== performance || 'yes' !== imageSupport ) {\n\t\t\t\t\t\tdispatch( { type: 'SET_FORM_ENABLED', payload: true } );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch ( err ) {\n\t\t\t\tif ( err.name !== 'AbortError' ) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.error( 'm-chart: chart refresh failed', err );\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tdispatch( { type: 'SET_REFRESHING', payload: false } );\n\t\t\t}\n\t\t}, 300 );\n\n\t\treturn () => {\n\t\t\tif ( timerRef.current ) {\n\t\t\t\tclearTimeout( timerRef.current );\n\t\t\t}\n\t\t};\n\t}, [ postMeta, spreadsheetData, setNames, title ] );\n}\n","import { useEffect } from '@wordpress/element';\nimport { useChartAdmin } from '../context/ChartAdminContext';\n\nconst BUTTON_IDS = [ 'save-post', 'wp-preview', 'post-preview', 'publish' ];\n\n/**\n * Keeps the WordPress Save/Publish buttons and form submission gated on state.formEnabled\n *\n * When formEnabled is false:\n * - Adds the 'disabled' class to the WP submit buttons\n * - Blocks form submission via a submit event listener\n *\n * When formEnabled is true:\n * - Removes the 'disabled' class from those buttons\n * - Allows form submission through normally\n */\nexport function useFormSubmissionGuard() {\n\tconst { state } = useChartAdmin();\n\tconst { formEnabled } = state;\n\n\t// Toggle disabled class on WP buttons\n\tuseEffect( () => {\n\t\tBUTTON_IDS.forEach( ( id ) => {\n\t\t\tconst el = document.getElementById( id );\n\n\t\t\tif ( el ) {\n\t\t\t\tel.classList.toggle( 'disabled', ! formEnabled );\n\t\t\t}\n\t\t} );\n\t}, [ formEnabled ] );\n\n\t// Block form submission when not ready\n\tuseEffect( () => {\n\t\tconst form = document.getElementById( 'post' );\n\n\t\tif ( ! form ) {\n\t\t\treturn;\n\t\t}\n\n\t\tfunction handleSubmit( e ) {\n\t\t\tif ( ! formEnabled ) {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t}\n\n\t\tform.addEventListener( 'submit', handleSubmit );\n\n\t\treturn () => form.removeEventListener( 'submit', handleSubmit );\n\t}, [ formEnabled ] );\n}\n","import { useCallback, useRef } from '@wordpress/element';\nimport { useChartAdmin } from '../context/ChartAdminContext';\n\n/**\n * Returns a stable `generateImage` callback that captures the current Chart.js instance as a PNG, writes it to the hidden img textarea, then re-enables the form\n *\n * @param {React.MutableRefObject} chartRef Ref holding the Chart.js instance\n */\nexport function useImageGeneration( chartRef ) {\n\tconst { state, dispatch } = useChartAdmin();\n\n\t// Keep a ref so the callback always sees the latest state without being recreated\n\tconst stateRef = useRef( state );\n\tstateRef.current = state;\n\n\tconst generateImage = useCallback( () => {\n\t\tconst chart = chartRef.current;\n\n\t\tif ( ! chart ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Non-canvas libraries (e.g. Highcharts) handle image generation\n\t\t// via the m_chart.render_done action hook instead.\n\t\tif ( ! chart.canvas ) {\n\t\t\tdispatch( { type: 'SET_FORM_ENABLED', payload: true } );\n\t\t\treturn;\n\t\t}\n\n\t\tconst { imageWidth, postMeta } = stateRef.current;\n\t\tconst chartWidth = parseInt( imageWidth, 10 );\n\t\tconst chartHeight = parseInt( postMeta.height, 10 );\n\n\t\tconst canvas = chart.canvas;\n\t\tconst container = canvas.parentElement;\n\n\t\t// Resize container to image dimensions so chart fills the right area\n\t\tcontainer.style.width = chartWidth + 'px';\n\t\tcontainer.style.height = chartHeight + 'px';\n\t\tchart.resize();\n\n\t\t// Fill solid white background (canvas is transparent by default)\n\t\tconst ctx = canvas.getContext( '2d' );\n\t\tctx.save();\n\t\tctx.globalCompositeOperation = 'destination-over';\n\t\tctx.fillStyle = 'white';\n\t\tctx.fillRect( 0, 0, chart.width, chart.height );\n\t\tctx.restore();\n\n\t\t// Capture PNG.\n\t\tconst img = chart.toBase64Image( 'image/png', 1 );\n\n\t\t// Restore container to natural dimensions\n\t\tcontainer.style.width = '';\n\t\tcontainer.style.height = '';\n\t\tchart.resize();\n\n\t\t// Write base64 string to the hidden textarea for form POST\n\t\tconst imgEl = document.getElementById( 'm-chart-img' );\n\n\t\tif ( imgEl ) {\n\t\t\timgEl.value = img;\n\t\t}\n\n\t\t// Re-enable form submission.\n\t\tdispatch( { type: 'SET_FORM_ENABLED', payload: true } );\n\t}, [ chartRef, dispatch ] ); // eslint-disable-line react-hooks/exhaustive-deps\n\n\treturn generateImage;\n}\n","import { useCallback, useRef } from '@wordpress/element';\n\nconst LONG_PRESS_DELAY = 500;\n\n/**\n * Returns pointer-event handlers that fire `callback` after a sustained press\n * Spread the returned object onto any element:
\n */\nexport function useLongPress( callback ) {\n\tconst timerRef = useRef( null );\n\n\tconst cancel = useCallback( () => {\n\t\tif ( timerRef.current ) {\n\t\t\tclearTimeout( timerRef.current );\n\t\t\ttimerRef.current = null;\n\t\t}\n\t}, [] );\n\n\tconst start = useCallback( ( e ) => {\n\t\t// Only respond to primary pointer (left-click / first touch)\n\t\tif ( e.button !== undefined && e.button !== 0 ) {\n\t\t\treturn;\n\t\t}\n\t\tcancel();\n\t\ttimerRef.current = setTimeout( () => {\n\t\t\ttimerRef.current = null;\n\t\t\tcallback( e );\n\t\t}, LONG_PRESS_DELAY );\n\t}, [ callback, cancel ] );\n\n\treturn {\n\t\tonPointerDown: start,\n\t\tonPointerUp: cancel,\n\t\tonPointerLeave: cancel,\n\t\tonPointerCancel: cancel,\n\t};\n}\n","/**\n * Measures the pixel width needed to display a string inside a given input element, using a canvas and the input's computed font style\n *\n * Falls back to a character-count estimate if the input element is not yet mounted (e.g. on the first render before refs are attached)\n *\n * @param {string} text The string to measure\n * @param {HTMLInputElement} inputEl The input element whose font to use\n * \n * @return {number} Width in pixels\n */\nexport function measureTextWidth( text, inputEl ) {\n\tif ( ! inputEl ) {\n\t\treturn Math.max( 40, text.length * 8 + 16 );\n\t}\n\n\tconst style = window.getComputedStyle( inputEl );\n\tconst canvas = document.createElement( 'canvas' );\n\tconst ctx = canvas.getContext( '2d' );\n\tctx.font = style.font;\n\n\tconst textWidth = Math.ceil( ctx.measureText( text ).width ) + 1;\n\tconst borderWidth = parseFloat( style.borderWidth ) || 0;\n\tconst paddingLeft = parseFloat( style.paddingLeft ) || 0;\n\tconst paddingRight = parseFloat( style.paddingRight ) || 0;\n\n\treturn ( borderWidth * 2 ) + paddingLeft + textWidth + paddingRight;\n}\n","module.exports = window[\"React\"];","module.exports = window[\"wp\"][\"element\"];","module.exports = window[\"wp\"][\"i18n\"];","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\tif (!(moduleId in __webpack_modules__)) {\n\t\tdelete __webpack_module_cache__[moduleId];\n\t\tvar e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { createRoot, createPortal } from '@wordpress/element';\nimport { ChartAdminProvider, useChartAdmin } from './context/ChartAdminContext';\nimport ChartMetaBox from './components/ChartMetaBox';\nimport SpreadsheetMetaBox from './components/SpreadsheetMetaBox';\nimport SubtitleField from './components/SubtitleField';\nimport TypeAndThemeRow from './components/TypeAndThemeRow';\nimport ParseAndFlagsRow from './components/ParseAndFlagsRow';\nimport AxisRows from './components/AxisRows';\nimport ShortcodeAndImageRow from './components/ShortcodeAndImageRow';\n\n// Expose shared context hook and settings row components for library plugins\n// that implement the m_chart.settings_component filter without a build step\nwindow.m_chart = {\n\tuseChartAdmin,\n\tTypeAndThemeRow,\n\tParseAndFlagsRow,\n\tAxisRows,\n\tShortcodeAndImageRow,\n};\n\n/**\n * The admin UI spans multiple meta boxes and the title area, so we use a single\n * React root (in a hidden container) with portals to render into each mount point\n * This ensures all components share a single ChartAdminContext instance\n */\n\n// Register Chart.js plugins before any chart instances are created\nif ( window.Chart && window.ChartDataLabels ) {\n\twindow.Chart.register( window.ChartDataLabels );\n}\n\nif ( window.Chart && window.MChartHelper ) {\n\twindow.Chart.register( window.MChartHelper );\n}\n\nconst subtitleRoot = document.getElementById( 'm-chart-subtitle-root' );\nconst spreadsheetRoot = document.getElementById( 'm-chart-spreadsheet-root' );\nconst chartRoot = document.getElementById( 'm-chart-chart-root' );\n\nif ( subtitleRoot || spreadsheetRoot || chartRoot ) {\n\tconst App = () => (\n\t\t\n\t\t\t{ subtitleRoot && createPortal( , subtitleRoot ) }\n\t\t\t{ spreadsheetRoot && createPortal( , spreadsheetRoot ) }\n\t\t\t{ chartRoot && createPortal( , chartRoot ) }\n\t\t\n\t);\n\n\t// Mount the app into a hidden container appended to the post form\n\t// All visible content is rendered via portals into the actual meta box divs\n\tconst postForm = document.getElementById( 'post' );\n\n\tif ( postForm ) {\n\t\tconst container = document.createElement( 'div' );\n\t\tcontainer.id = 'm-chart-admin-ui';\n\t\tcontainer.hidden = true;\n\t\tpostForm.appendChild( container );\n\t\tcreateRoot( container ).render( );\n\t}\n}\n"],"names":["Fragment","useState","useCallback","__","useChartAdmin","measureTextWidth","YMIN_TYPES","Set","AXIS_TYPES","AxisRows","_postMeta$y_min_value","state","dispatch","postMeta","unitTerms","showAxis","has","type","showYMin","yMinEl","setYMinEl","yMinRef","node","yMinValue","String","y_min_value","yMinWidth","handleChange","field","value","payload","handleYMinCheck","checked","y_min","axisStyle","display","yMinStyle","unitOptions","createElement","map","group","units","key","disabled","unit","name","className","style","htmlFor","id","y_title","width","onChange","e","target","y_units","ref","minWidth","x_title","x_units","useEffect","useChartRefresh","useFormSubmissionGuard","ChartPreview","ChartSettings","ChartMetaBox","title","setTitle","el","document","getElementById","handler","addEventListener","removeEventListener","useRef","useImageGeneration","prepareArgs","args","data","options","plugins","tooltip","datalabels","defaultChartjsRender","canvas","onComplete","existingInstance","prepared","datasets","labels","animation","window","Chart","config","update","postId","chartArgs","performance","imageSupport","canvasRef","chartRef","renderFlagRef","isFirstRender","needsImagesRef","current","generateImage","destroy","wp","hooks","doAction","instance","applyFilters","height","useMemo","TypeAndThemeRow","ParseAndFlagsRow","ShortcodeAndImageRow","DefaultSettings","Settings","sprintf","spreadsheetAutoWidth","CsvControls","getActiveWorksheet","nonce","ajaxUrl","setNames","activeSheet","csvDelimiters","defaultDelimiter","selectedFile","setSelectedFile","csvDelimiter","setCsvDelimiter","fileError","setFileError","importError","setImportError","isImporting","setIsImporting","fileInputRef","handleSelectFile","preventDefault","click","handleFileChange","file","files","test","handleCancel","handleImport","formData","FormData","append","response","fetch","method","body","json","success","worksheet","setData","index","getData","err","message","handleExport","setName","JSON","stringify","form","action","entries","input","appendChild","submit","removeChild","showConfirmation","href","onClick","accept","Object","val","label","CONTEXT_MENU_ITEMS","records","length","columns","r","x","context","getContext","forEach","column","maxWidth","padding","i","cell","element","font","getComputedStyle","metrics","measureText","innerText","setWidth","JspreadsheetWrapper","sheetId","sheetIndex","isActive","onMounted","onUnmounted","containerRef","worksheetRef","sheetIndexRef","initialData","jspreadsheet","worksheets","allowComments","minDimensions","contextMenu","obj","y","items","filter","item","includes","onload","spreadsheet","ws","getWorksheetActive","onafterchanges","PARSE_OPTION_NAMES","rows","SHARED_TYPES","showShared","handleCheckbox","parse_in","legend","shared","useLongPress","SheetTab","isSingle","isNew","isRenaming","setIsRenaming","inputValue","setInputValue","inputRef","longPress","focus","select","handleClick","handleDoubleClick","handleDelete","stopPropagation","confirm","deleteConfirm","newActive","handleNameChange","commitRename","handleKeyDown","inputWidth","Math","max","Boolean","join","onDoubleClick","onBlur","onKeyDown","MULTI_SHEET_TYPES","SheetTabs","sheetIds","newSheetId","multiSheetTypes","types","showTabs","handleAddSheet","imageUrl","shortcode","showImageField","imageDisabled","copied","setCopied","handleCopy","navigator","clipboard","writeText","then","setTimeout","readOnly","rel","library","SUBMIT_BUTTON_IDS","SpreadsheetMetaBox","spreadsheetData","formEnabled","pendingSubmit","worksheetInstances","formEnabledRef","sheetIdsRef","handleMounted","handleUnmounted","_worksheetInstances$c","activeId","writeDataToForm","allData","_worksheetInstances$c2","dataTextarea","querySelector","buttons","btn","handleSubmit","SubtitleField","_state$postMeta$subti","subtitle","placeholder","_postMeta$height","typeOptions","typeOptionNames","themes","heightEl","setHeightEl","heightRef","heightValue","heightWidth","theme","slug","min","createContext","useContext","useReducer","ChartAdminStateContext","ChartAdminDispatchContext","m_chart_admin","initialSheetCount","spreadsheet_data","initialSheetIds","Array","from","_","initialState","post_id","ajax_url","image_support","instantPreview","instant_preview_support","imageMultiplier","image_multiplier","imageWidth","image_width","delete_confirm","csv_delimiters","default_delimiter","post_meta","set_names","nextSheetId","chart_args","isRefreshing","type_options","type_option_names","unit_terms","image_url","reducer","idx","ChartAdminProvider","children","Provider","Error","useChartDispatch","timerRef","abortRef","isFirstRun","latestRef","clearTimeout","abort","AbortController","URLSearchParams","meta","serialized","signal","console","error","BUTTON_IDS","classList","toggle","stateRef","chart","chartWidth","parseInt","chartHeight","container","parentElement","resize","ctx","save","globalCompositeOperation","fillStyle","fillRect","restore","img","toBase64Image","imgEl","LONG_PRESS_DELAY","callback","cancel","start","button","undefined","onPointerDown","onPointerUp","onPointerLeave","onPointerCancel","text","inputEl","textWidth","ceil","borderWidth","parseFloat","paddingLeft","paddingRight","createRoot","createPortal","m_chart","ChartDataLabels","register","MChartHelper","subtitleRoot","spreadsheetRoot","chartRoot","App","postForm","hidden","render"],"sourceRoot":""} \ No newline at end of file diff --git a/components/array-default-unit-terms.php b/components/array-default-unit-terms.php index 4747642..463baf7 100755 --- a/components/array-default-unit-terms.php +++ b/components/array-default-unit-terms.php @@ -4,8 +4,8 @@ * An array of unit terms to generate for the user */ -return array( - esc_html__( 'Time', 'm-chart' ) => array( +return [ + esc_html__( 'Time', 'm-chart' ) => [ esc_html__( 'Seconds', 'm-chart' ), esc_html__( 'Minutes', 'm-chart' ), esc_html__( 'Hours', 'm-chart' ), @@ -16,15 +16,15 @@ esc_html__( 'Years', 'm-chart' ), esc_html__( 'Decades', 'm-chart' ), esc_html__( 'Centuries', 'm-chart' ), - ), - esc_html__( 'Money', 'm-chart' ) => array( + ], + esc_html__( 'Money', 'm-chart' ) => [ esc_html__( 'USD', 'm-chart' ), esc_html__( 'EUR', 'm-chart' ), esc_html__( 'GBP', 'm-chart' ), esc_html__( 'JPY', 'm-chart' ), esc_html__( 'CNY', 'm-chart' ), - ), - esc_html__( 'Length', 'm-chart' ) => array( + ], + esc_html__( 'Length', 'm-chart' ) => [ esc_html__( 'Inch', 'm-chart' ), esc_html__( 'Foot', 'm-chart' ), esc_html__( 'Yard', 'm-chart' ), @@ -33,20 +33,20 @@ esc_html__( 'Centimeter', 'm-chart' ), esc_html__( 'Meter', 'm-chart' ), esc_html__( 'Kilometer', 'm-chart' ), - ), - esc_html__( 'Other', 'm-chart' ) => array( + ], + esc_html__( 'Other', 'm-chart' ) => [ esc_html__( 'Percent', 'm-chart' ), - ), - esc_html__( 'Website/Traffic', 'm-chart' ) => array( + ], + esc_html__( 'Website/Traffic', 'm-chart' ) => [ esc_html__( 'Visitors', 'm-chart' ), esc_html__( 'Unique Visitors', 'm-chart' ), esc_html__( 'Clicks', 'm-chart' ), esc_html__( 'Referrers', 'm-chart' ), esc_html__( 'Active Users', 'm-chart' ), esc_html__( 'Pages', 'm-chart' ), - ), - esc_html__( 'Sales', 'm-chart' ) => array( + ], + esc_html__( 'Sales', 'm-chart' ) => [ esc_html__( 'Units', 'm-chart' ), esc_html__( 'CPM', 'm-chart' ), - ), -); + ], +]; diff --git a/components/array-locale-codes.php b/components/array-locale-codes.php index 46b05a5..1341239 100755 --- a/components/array-locale-codes.php +++ b/components/array-locale-codes.php @@ -5,7 +5,7 @@ * Tanky ou @umpirsky! https://github.com/umpirsky/locale-list */ -return array( +return [ 'af' => esc_html__( 'Afrikaans' ), 'af-NA' => esc_html__( 'Afrikaans (Namibia)' ), 'af-ZA' => esc_html__( 'Afrikaans (South Africa)' ), @@ -569,4 +569,4 @@ 'yo-NG' => esc_html__( 'Yoruba (Nigeria)' ), 'zu' => esc_html__( 'Zulu' ), 'zu-ZA' => esc_html__( 'Zulu (South Africa)' ), -); \ No newline at end of file +]; \ No newline at end of file diff --git a/components/block-src/chart/block.json b/components/block-src/chart/block.json index 6081336..8704c9b 100644 --- a/components/block-src/chart/block.json +++ b/components/block-src/chart/block.json @@ -1,16 +1,19 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "m-chart/chart", - "title": "M Chart", + "title": "Chart", "category": "layout", - "icon": "chart-pie", "description": "Embed a chart made with M Chart into your posts and pages", "textdomain": "m-chart", "attributes": { "chartId": { + "type": "integer", + "default": 0 + }, + "show": { "type": "string", - "default": "" + "default": "chart" } }, "supports": { diff --git a/components/block-src/chart/edit.js b/components/block-src/chart/edit.js index 75ad76c..ef4756d 100644 --- a/components/block-src/chart/edit.js +++ b/components/block-src/chart/edit.js @@ -1,81 +1,158 @@ -import { TextControl, Spinner, ToolbarGroup, ToolbarButton, } from '@wordpress/components'; -import { useState, useEffect } from '@wordpress/element'; -import { useBlockProps, BlockControls } from '@wordpress/block-editor'; +import { SelectControl, Spinner, ToolbarGroup, ToolbarButton, Placeholder, ExternalLink, PanelBody, SearchControl } from '@wordpress/components'; +import { getBlockType } from '@wordpress/blocks'; +import { useState, useEffect, useRef, useMemo, useCallback } from '@wordpress/element'; +import { useBlockProps, BlockControls, InspectorControls } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import apiFetch from '@wordpress/api-fetch'; -import "./editor.scss"; import debounce from 'lodash/debounce'; -import { useCallback } from 'react'; +import "./editor.scss"; export default function edit( { attributes, setAttributes } ) { - - // State. - const [ options, setOptions ] = useState( [] ); + // State + const [ results, setResults ] = useState( [] ); const [ search, setSearch ] = useState( '' ); - const [ maxAvailable, setMaxAvailable ] = useState( 0 ); + const [ postsAvailable, setPostsAvailable ] = useState( false ); const [ available, setAvailable ] = useState( 0 ); const [ loaded, setLoaded ] = useState( false ); - const [ charts, setCharts ] = useState( [] ); - const [ temp, setTemp ] = useState( null ); + const [ selectedChart, setSelectedChart ] = useState( null ); const [ siteUrl, setSiteUrl ] = useState( null ); - const [ imageSupport, setImageSupport ] = useState( true ); + const [ imageSupport, setImageSupport ] = useState( false ); const [ loadProblem, setLoadProblem ] = useState( false ); - // Url constants. - const newUrl = `${ siteUrl }/wp-admin/post-new.php?post_type=m-chart`; - const editUrl = `${ siteUrl }/wp-admin/post.php?post=${ attributes.chartId }&action=edit`; + const [ page, setPage ] = useState( 1 ); + const [ loadingMore, setLoadingMore ] = useState( false ); + const resultsRef = useRef( null ); + + // URLs + const newUrl = `${ siteUrl }/wp-admin/post-new.php?post_type=m-chart`; + const editUrl = `${ siteUrl }/wp-admin/post.php?post=${ attributes.chartId }&action=edit`; const optionsUrl = `/m-chart/v1/options`; - // Blockprops. + // Blockprops const blockProps = useBlockProps( { className: 'm-chart-block-chart-selector' } ); - // Miscellaneous - const random = `?random=${ Math.round( Math.random() * 1000000 ) }`; - // On load we fetch all charts if none available we set a constant to show an error message. - // We then check if a chart is already chosen. If so we show that one else show all. + // Set a cache URL parameter based on the current moment in time to prevent cached images from messing up the UI + const cacheBuster = `?cache=${performance.now()}`; + + // On load we fetch some option settings and run getCharts so we have some intiial reasults loaded into the UI useEffect( () => { fetchOptions(); - fetchGraphs( search ); + getCharts( search ); }, [] ); - // Compose preview list. - const optionsList = options.map( ( x ) => { - if ( imageSupport ) { - return
  • handleClick( x.id ) } title={ x.title }>{ x.src ? <>
    { x.title }
    { :
    { x.title }
    }
  • ; + // Fetch the selected chart individually whenever chartId changes + // Using attributes.chartId as a dependency handles the case where Gutenberg provides the saved attribute value after the initial render + useEffect( () => { + setSelectedChart( null ); + if ( attributes.chartId ) { + getChart( parseInt( attributes.chartId, 10 ) ); + } + }, [ attributes.chartId ] ); + + // Load more charts when scrolling near the bottom of the results list + useEffect( () => { + const el = resultsRef.current; + + if ( ! el ) return; + + const handleScroll = () => { + if ( loadingMore ) return; + if ( results.length >= available ) return; + + // If we're close enough to the bottom of the list load the next page + if ( el.scrollTop + el.clientHeight >= el.scrollHeight - 100 ) { + const nextPage = page + 1; + setPage( nextPage ); + getCharts( search, nextPage ); + } + }; + + el.addEventListener( 'scroll', handleScroll ); + + return () => el.removeEventListener( 'scroll', handleScroll ); + }, [ results, available, loadingMore, page, search ] ); + + // Build list of charts out of the results object + const resultsList = results.map( ( x ) => { + if ( ! imageSupport || ! x.src ) { + return
  • handleClick( x.id ) } title={ x.title }>
    { x.title }
  • ; } else { - return
  • handleClick( x.id ) } title={ x.title }>
    { x.title }
  • ; + return
  • handleClick( x.id ) } title={ x.title }>
    { x.title }
    {
  • ; } } ); - const selected = charts.filter( x => x.id === attributes.chartId )[ 0 ]; - + // Handle clicks to a chart in the results list const handleClick = ( id ) => { setAttributes( { chartId: id } ); - setTemp( id ); + setSelectedChart( null ); }; + // Handle user typing into the search field const handleSearch = ( value ) => { - const regex = /[^a-zA-Z0-9\- , ]/gi; - value = value.replace( regex, '' ); - setSearch( value ); - doDebounce( value ); + console.log( 'search', value ); + doSearch( value ); }; + // Actually actually carry out the debounced search + const doSearch = useCallback( + debounce( ( value ) => { + console.log( 'debounce', value ); + setSearch( value ); + setPage( 1 ); + getCharts( value ); + }, 500), + [] + ); + + // Get option settings const fetchOptions = () => { apiFetch( { path: optionsUrl } ).then( result => { - setImageSupport( result.image_support_active ); + setImageSupport( result.image_support ); setSiteUrl( result.siteurl ); - setMaxAvailable( result.maxAvailable ); + setPostsAvailable( result.posts_avilable ); } ); }; - const fetchGraphs = ( value ) => { + // Get a single chart + const getChart = ( id ) => { + apiFetch( { path: `/m-chart/v1/chart/${ id }` } ).then( result => { + setSelectedChart( { + id: result.id, + title: result.title || '-', + subtitle: result.subtitle, + width: result.width, + height: result.height, + type: result.type || '', + src: result.url || '' + } ); + } ).catch( () => {} ); + }; + + const getCharts = ( value, getPage = 1 ) => { setLoadProblem( false ); - apiFetch( { path: `/m-chart/v1/graphs/${ value }` } ) + + // If we're getting a subsequent page we're adding to the existing results + if ( getPage > 1 ) { + setLoadingMore( true ); + } + + // Build the parameters + const params = new URLSearchParams(); + + if ( value ) { + params.set( 's', value ); + } + + if ( getPage > 1 ) { + params.set( 'page', getPage ); + } + + const query = params.toString(); + + // Run the query and grab the results + apiFetch( { path: `/m-chart/v1/charts${ query ? '?' + query : '' }` } ) .then( result => { - let charts = []; - setAvailable( result[ 0 ] ); - result[ 1 ].map( x => charts.push( { + const newCharts = result.posts.map( x => ( { id: x.id, title: x.title || '-', subtitle: x.subtitle, @@ -84,98 +161,133 @@ export default function edit( { attributes, setAttributes } ) { type: x.type || '', src: x.url || '' } ) ); - setCharts( charts ); - setOptions( charts ); + + // Update the found value to match the current search + setAvailable( result.found_posts ); + + // Either append or replace the existing results + if ( getPage === 1 ) { + setResults( newCharts ); + } else { + setResults( prev => [ ...prev, ...newCharts ] ); + } + setLoaded( true ); + setLoadingMore( false ); } ).catch( ( error ) => { + // If there's an error we'll note it if ( error.code === 'rest_no_route' ) { setLoadProblem( true ); - }; + } + + setLoadingMore( false ); } ); }; - const doDebounce = useCallback( debounce( fetchGraphs, 500 ), [] ); - return (
    + { !! attributes.chartId && + + + setAttributes( { show: value } ) } + /> + + + } - window.location.href = newUrl } icon="external">{ __( 'New Chart', 'm-chart' ) } - { attributes.chartId && + { ! attributes.chartId && + window.open( newUrl, "_blank" ) } icon="external">{ __( 'New chart', 'm-chart' ) } + } + { !! attributes.chartId && <> - window.location.href = editUrl } icon="external" >{ __( 'Edit Chart', 'm-chart' ) } - handleClick( '' ) } >{ __( 'Replace', 'm-chart' ) } + window.open( editUrl, "_blank" ) } icon="external" >{ __( 'Edit chart', 'm-chart' ) } + handleClick( 0 ) } >{ __( 'Replace', 'm-chart' ) } - } + } - -
    -
    - -
    Chart
    -
    - { loadProblem ? -

    { __( 'There was a problem fetching charts', 'm-chart' ) }

    - : - <> - { !loaded ? -

    - -

    - : - attributes.chartId ? -
    - { imageSupport ? -
    - { selected?.src === '' ? -
    - -

    { selected.title }

    -
    - : - - } -
    - : -
    - -

    { selected?.title }

    -

    { selected?.subtitle }

    -
    - } -
    - : - maxAvailable === 0 ? -

    { __( 'No Charts found', 'm-chart' ) } - { __( 'Create a new chart', 'm-chart' ) } -

    - : -
    -
    - handleSearch( value ) } - autoFocus - /> -

    { optionsList.length } { __( ' of ', 'm-chart' ) }{ available }

    -
    - { optionsList.length === 0 && search.length > 1 ? -

    { __( 'No Charts found using this search string', 'm-chart' ) }

    - : -
      - { optionsList } -
    - } -
    - - } - - } -
    -
    -
    -
    + { !! attributes.chartId ? ( +
    + { ! selectedChart ? +

    + : +
    + { ! imageSupport || ! selectedChart.src ? +
    +
    + +
    { selectedChart.title }
    + { selectedChart.subtitle && (
    { selectedChart.subtitle }
    )} +
    +
    + : +
    + +
    + } +
    + } +
    + ) : ( +
    + +
    + { loadProblem ? +

    { __( 'There was a problem loading charts', 'm-chart' ) }

    + : + <> + { !loaded ? +

    + +

    + : + postsAvailable === false ? +
    +

    + { __( 'No charts found', 'm-chart' ) }
    +

    +

    + { __( 'Create a new chart', 'm-chart' ) } +

    +
    + : +
    +
    + handleSearch( value ) } + autoFocus + /> +

    { available } { 1 === available ? __( 'chart found', 'm-chart' ) : __( 'charts found', 'm-chart' ) }

    +
    + { resultsList.length === 0 && search.length > 1 ? +

    { __( 'No charts found', 'm-chart' ) }

    + : +
      + { resultsList } + { loadingMore && +
    • + } +
    + } +
    + } + + } +
    +
    +
    + ) } +
    ); -};; \ No newline at end of file +}; \ No newline at end of file diff --git a/components/block-src/chart/editor.scss b/components/block-src/chart/editor.scss index 223c3dd..91448e5 100644 --- a/components/block-src/chart/editor.scss +++ b/components/block-src/chart/editor.scss @@ -1,3 +1,5 @@ +@use '../../sass/global-mixins-and-variables' as *; + .m-chart-selector { background-color: #fff; @@ -21,7 +23,6 @@ .count { font-family: var(--wp--preset--font-family--system-font); - text-align: center; line-height: 2rem; font-size: .8rem; margin-top: 0; @@ -39,10 +40,7 @@ list-style: none; gap: .5rem; padding: 0; - - @media (min-width: 600px) { - max-height: 15rem; - } + margin-bottom: 0; li { flex-basis: 100%; @@ -73,12 +71,12 @@ } &:hover { + cursor: pointer; + img, .type { - cursor: pointer; border: 2px solid var(--wp-admin-theme-color); border-radius: 2px; - //box-shadow: 0px 0px 3px gray; } } @@ -86,8 +84,17 @@ font-weight: normal; font-size: .8rem; text-transform: none; - margin-top: 1rem; - margin-bottom: 0; + margin: 1rem 5px 0 5px; + } + + &.image { + position: relative; + + h6.title { + @include text-stroke(3); + margin-top: 0; + position: absolute; + } } .type { @@ -161,88 +168,115 @@ } } } + + li.loading-more { + flex-basis: 100%; + padding: 1rem 0; + } } - + // Selected .chart-selected { - .type { - position: relative; - padding-top: 5rem; - padding-bottom: 5rem; - background: #f1f1f1; - - .icon { - &:before { - content: ''; - height: 47px; - display: block; - background-image: url('../../images/types/pie.svg'); - background-repeat: no-repeat; - background-size: contain; - background-position: center; - } - - &.line:before { - background-image: url('../../images/types/line.svg'); - } - - &.spline:before { - background-image: url('../../images/types/spline.svg'); - } + .image { + overflow: hidden; - &.area:before { - background-image: url('../../images/types/area.svg'); - } - - &.column:before { - background-image: url('../../images/types/column.svg'); - } - - &.stacked-column:before { - background-image: url('../../images/types/stacked-column.svg'); - } - - &.bar:before { - background-image: url('../../images/types/bar.svg'); - } - - &.stacked-bar:before { - background-image: url('../../images/types/stacked-bar.svg'); - } - - &.pie:before { - background-image: url('../../images/types/pie.svg'); - } - - &.doughnut:before { - background-image: url('../../images/types/doughnut.svg'); - } - - &.scatter:before { - background-image: url('../../images/types/scatter.svg'); - } + img.preview { + display: block; + max-width: 100%; + height: auto; + } + } + + .image, + .no-image { + background: #f1f1f1; + display: flex; + align-items: center; - &.bubble:before { - background-image: url('../../images/types/bubble.svg'); + .type { + flex: 1; + + .icon { + &:before { + content: ''; + height: 47px; + display: block; + background-image: url('../../images/types/pie.svg'); + background-repeat: no-repeat; + background-size: contain; + background-position: center; + } + + &.line:before { + background-image: url('../../images/types/line.svg'); + } + + &.spline:before { + background-image: url('../../images/types/spline.svg'); + } + + &.area:before { + background-image: url('../../images/types/area.svg'); + } + + &.column:before { + background-image: url('../../images/types/column.svg'); + } + + &.stacked-column:before { + background-image: url('../../images/types/stacked-column.svg'); + } + + &.bar:before { + background-image: url('../../images/types/bar.svg'); + } + + &.stacked-bar:before { + background-image: url('../../images/types/stacked-bar.svg'); + } + + &.pie:before { + background-image: url('../../images/types/pie.svg'); + } + + &.doughnut:before { + background-image: url('../../images/types/doughnut.svg'); + } + + &.scatter:before { + background-image: url('../../images/types/scatter.svg'); + } + + &.bubble:before { + background-image: url('../../images/types/bubble.svg'); + } + + &.radar:before { + background-image: url('../../images/types/radar.svg'); + } + + &.polar:before { + background-image: url('../../images/types/polar.svg'); + } } - - &.radar:before { - background-image: url('../../images/types/radar.svg'); + + h5.title { + font-weight: normal; + font-size: 23px; + text-transform: none; + margin: 0 15px 0 15px; + text-align: center; } - - &.polar:before { - background-image: url('../../images/types/polar.svg'); + + h6.subtitle { + font-weight: normal; + font-size: 17px; + text-transform: none; + margin: 1rem 15px 0 15px; + margin-bottom: 0; + text-align: center; } } - - h4.title { - font-weight: normal; - font-size: 18pt; - text-transform: none; - margin-top: 2rem; - margin-bottom: 0; - text-align: center; - } } } } \ No newline at end of file diff --git a/components/block-src/chart/index.js b/components/block-src/chart/index.js index e012cad..4bfdb70 100644 --- a/components/block-src/chart/index.js +++ b/components/block-src/chart/index.js @@ -1,13 +1,21 @@ import { __ } from '@wordpress/i18n'; import { registerBlockType } from '@wordpress/blocks'; - -import metadata from './block.json'; +import { SVG, Path } from '@wordpress/components'; +import blockJson from './block.json'; import edit from './edit'; +const blockIcon = ( + +); + registerBlockType( - metadata, + blockJson, { edit: edit, save: () => null, + icon: blockIcon, } ); \ No newline at end of file diff --git a/components/block-src/chart/render.php b/components/block-src/chart/render.php index b3c93f2..c173762 100644 --- a/components/block-src/chart/render.php +++ b/components/block-src/chart/render.php @@ -10,5 +10,8 @@ $chart_id = $attributes['chartId'] ?? null; if ( ! empty( $chart_id ) ) : - echo '[chart id="' . $chart_id . '"]'; + $allowed_show = [ 'chart', 'image', 'table' ]; + $show = in_array( $attributes['show'] ?? '', $allowed_show, true ) ? $attributes['show'] : 'chart'; + + echo '[chart id="' . $chart_id . '" show="' . $show . '"]'; endif; diff --git a/components/block/chart/block.json b/components/block/chart/block.json index 83f6306..e84c65d 100644 --- a/components/block/chart/block.json +++ b/components/block/chart/block.json @@ -1,16 +1,19 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", - "apiVersion": 2, + "apiVersion": 3, "name": "m-chart/chart", - "title": "M Chart", + "title": "Chart", "category": "layout", - "icon": "chart-pie", "description": "Embed a chart made with M Chart into your posts and pages", "textdomain": "m-chart", "attributes": { "chartId": { + "type": "integer", + "default": 0 + }, + "show": { "type": "string", - "default": "" + "default": "chart" } }, "supports": { diff --git a/components/block/chart/render.php b/components/block/chart/render.php index b3c93f2..c173762 100644 --- a/components/block/chart/render.php +++ b/components/block/chart/render.php @@ -10,5 +10,8 @@ $chart_id = $attributes['chartId'] ?? null; if ( ! empty( $chart_id ) ) : - echo '[chart id="' . $chart_id . '"]'; + $allowed_show = [ 'chart', 'image', 'table' ]; + $show = in_array( $attributes['show'] ?? '', $allowed_show, true ) ? $attributes['show'] : 'chart'; + + echo '[chart id="' . $chart_id . '" show="' . $show . '"]'; endif; diff --git a/components/block/index.asset.php b/components/block/index.asset.php index a363dfd..28e1c61 100644 --- a/components/block/index.asset.php +++ b/components/block/index.asset.php @@ -1 +1 @@ - array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => '9a81147d426a677341b7'); + array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => '10e52959cca5456b5b99'); diff --git a/components/block/index.css b/components/block/index.css index bc05dd4..6aa30c0 100644 --- a/components/block/index.css +++ b/components/block/index.css @@ -1 +1,241 @@ -.m-chart-selector{background-color:#fff}.m-chart-selector .viewbox{width:100%}.m-chart-selector .components-placeholder__label .dashicons-chart-pie{margin-right:.5rem}.m-chart-selector .search-box .components-base-control__field{margin-bottom:0}.m-chart-selector .search-box .count{font-family:var(--wp--preset--font-family--system-font);font-size:.8rem;line-height:2rem;margin-top:0;text-align:center}.m-chart-selector ul.results{display:flex;flex-wrap:wrap;gap:.5rem;list-style:none;max-height:17rem;overflow-x:hidden;overflow-y:scroll;padding:0;position:relative}@media(min-width:600px){.m-chart-selector ul.results{max-height:15rem}}.m-chart-selector ul.results li{align-items:center;background-color:#fff;display:flex;flex-basis:100%;flex-direction:column;height:auto;justify-content:center;margin:0;text-align:center;z-index:10}@media(min-width:600px){.m-chart-selector ul.results li{flex-basis:calc(50% - 7px);padding-left:2px}}@media(min-width:780px){.m-chart-selector ul.results li{flex-basis:calc(33.3% - 8px);padding-left:2px}}.m-chart-selector ul.results li .type,.m-chart-selector ul.results li img{border:2px solid transparent;width:100%}.m-chart-selector ul.results li:hover .type,.m-chart-selector ul.results li:hover img{border:2px solid var(--wp-admin-theme-color);border-radius:2px;cursor:pointer}.m-chart-selector ul.results li h6.title{font-size:.8rem;font-weight:400;margin-bottom:0;margin-top:1rem;text-transform:none}.m-chart-selector ul.results li .type{background:#f1f1f1;padding-bottom:2rem;padding-top:2rem;position:relative}.m-chart-selector ul.results li .type .icon:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNi4xODUgNTcuMjkyIDQzNi40NTYgNDM2Ljc3NiIgd2lkdGg9IjQzNi40NTYiIGhlaWdodD0iNDM2Ljc3NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ5NDk0OTt9Cjwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGQ9Ik0gNDM3LjgxIDI4OS4yNTggTCAyMjAuOTEgMjg5LjI1OCBMIDIyMC45MSA3Mi4zNDggQyAyMjAuOTEgNjMuNjU4IDIxMy4yMSA1Ni40NDggMjA0LjYxIDU3LjY1OCBDIDk1LjcxIDczLjA1OCAxMi41MSAxNjguMTU4IDE2LjMxIDI4Mi4wNDggQyAyMC4wMSAzOTUuNDQ4IDExNC44MSA0OTAuMTU4IDIyOC4yMSA0OTMuOTQ4IEMgMzQyLjExIDQ5Ny42NTggNDM3LjExIDQxNC40NDggNDUyLjUxIDMwNS42NTggQyA0NTMuNzEgMjk2LjkyOCA0NDYuNTEgMjg5LjI1OCA0MzcuODEgMjg5LjI1OCBaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNIDI0OC40NDggNTcuMjAxIEMgMjQwLjE0OCA1Ni42MDEgMjMzLjA0OCA2My43MDEgMjMzLjA0OCA3Mi4wMDEgTCAyMzMuMDQ4IDI3Ni41MDEgTCA0MzcuNTQ4IDI3Ni41MDEgQyA0NDUuOTQ4IDI3Ni41MDEgNDUyLjk0OCAyNjkuNTAxIDQ1Mi4zNDggMjYxLjEwMSBDIDQ0NC44MDggMTUxLjkwMSAzNTcuNjA4IDY0Ljc1MSAyNDguNDQ4IDU3LjIwMSBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=);background-position:50%;background-repeat:no-repeat;background-size:contain;content:"";display:block;height:27px}.m-chart-selector ul.results li .type .icon.line:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTQzMC45LDE4Ni4xYzUuNywzLjUsMTIuNSw1LjQsMTkuOCw0LjZjMTUuMy0xLjYsMjcuNS0xNC40LDI4LjItMjkuOGMwLjktMTguMS0xMy42LTMzLTMxLjUtMzMKCWMtMTcuNCwwLTMxLjUsMTQuMS0zMS41LDMxLjVjMCwzLjYsMC43LDcsMS45LDEwLjNsLTExMS41LDg5Yy00LjgtMy0xMC41LTQuOC0xNi42LTQuOGMtNi4xLDAtMTEuNywxLjgtMTYuNiw0LjhsLTU4LjgtNDcKCWMxLjEtMy4yLDEuOS02LjYsMS45LTEwLjNjMC0xNy43LTE0LjYtMzItMzIuNS0zMS41Yy0xNS4zLDAuNS0yOC4xLDEyLjItMzAuMiwyNy40Yy0wLjksNi40LDAuNCwxMi40LDIuOSwxNy42bC02My4xLDYzLjEKCWMtNS42LTIuNy0xMi4xLTMuOS0xOS4xLTIuNmMtMTQuMiwyLjctMjUuMSwxNC44LTI1LjksMjkuM2MtMSwxOC44LDE0LjQsMzQuMiwzMy4yLDMzLjJjMTQuNS0wLjgsMjYuNi0xMS42LDI5LjMtMjUuOQoJYzEuMy03LDAuMS0xMy40LTIuNi0xOS4xbDYzLjEtNjMuMWM0LjEsMiw4LjYsMy4yLDEzLjUsMy4yYzYuMSwwLDExLjctMS44LDE2LjYtNC44bDU4LjgsNDdjLTEuMSwzLjItMS45LDYuNy0xLjksMTAuMwoJYzAsMTcuNCwxNC4xLDMxLjUsMzEuNSwzMS41czMxLjUtMTQuMSwzMS41LTMxLjVjMC0zLjYtMC43LTctMS45LTEwLjNMNDMwLjksMTg2LjFMNDMwLjksMTg2LjF6IE00NDUuNywxNDkuMQoJYzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMWMtMC43LDQuMy00LjIsNy45LTguNiw4LjZjLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMUM0MzcuOSwxNTMuMyw0NDEuNCwxNDkuOCw0NDUuNywxNDkuMXogTTgxLjUsMzE2LjgKCWMtNy4yLDEuMi0xMy4zLTUtMTIuMS0xMi4xYzAuNy00LjMsNC4yLTcuOSw4LjYtOC42YzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMUM4OS40LDMxMi42LDg1LjksMzE2LjEsODEuNSwzMTYuOHogTTE4Ni41LDIxMS44CgljLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMWMwLjctNC4zLDQuMi03LjksOC42LTguNmM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFDMTk0LjQsMjA3LjYsMTkwLjksMjExLjEsMTg2LjUsMjExLjh6IE0yOTEuNSwyOTUuOAoJYy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFjMC43LTQuMyw0LjItNy45LDguNi04LjZjNy4yLTEuMiwxMy4zLDUsMTIuMSwxMi4xQzI5OS40LDI5MS42LDI5NS45LDI5NS4xLDI5MS41LDI5NS44eiIvPgo8Y2lyY2xlIGN4PSIxODQuMiIgY3k9IjE5OSIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iNDQ3LjUiIGN5PSIxNTkuNCIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iMjg5LjgiIGN5PSIyODUuNCIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iNzkuOCIgY3k9IjMwNi40IiByPSIxMyIvPgo8L3N2Zz4K)}.m-chart-selector ul.results li .type .icon.spline:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjE7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQo8L3N0eWxlPgo8cGF0aCBkPSJNNTA0LDQxNkgzMlY3MmMwLTQuNC0zLjYtOC04LThIOGMtNC40LDAtOCwzLjYtOCw4djM2MGMwLDguOCw3LjIsMTYsMTYsMTZoNDg4YzQuNCwwLDgtMy42LDgtOHYtMTYKCUM1MTIsNDE5LjYsNTA4LjQsNDE2LDUwNCw0MTZ6Ii8+CjxlbGxpcHNlIHRyYW5zZm9ybT0ibWF0cml4KDMuMzI3Nzk0ZS0wMiAtMC45OTk0IDAuOTk5NCAzLjMyNzc5NGUtMDIgLTIzMS4wODQ4IDM3Ny44OTI5KSIgY3g9Ijc5LjgiIGN5PSIzMDguNCIgcng9IjMyLjciIHJ5PSIzMi43Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03OS44LDMwOC40Qzc5LjgsMjQzLDEzMiwxOTksMTg0LjMsMTk5Yzc5LjMsMCw0NC43LDE0OC4xLDExMi4xLDE0OC4xYzEwOC4yLDAsMTUxLjEtMTA2LjEsMTUxLjEtMTg3LjYiLz4KPGNpcmNsZSBjeD0iNDQ3LjYiIGN5PSIxNTkuNCIgcj0iMzIuNyIvPgo8Y2lyY2xlIGN4PSIyOTYuNCIgY3k9IjM0Ny4xIiByPSIzMi43Ii8+CjxjaXJjbGUgY3g9IjE4NCIgY3k9IjIwMS4yIiByPSIzMi43Ii8+Cjwvc3ZnPgo=)}.m-chart-selector ul.results li .type .icon.area:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe29wYWNpdHk6MC4zMzt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNzkuOCw0MzEgNzkuOCwzMDYuNCAxODQuOCwyMDEuNCAyODkuOCwyODUuNCA0NDcuNSwxNTkuNCA0NDcuNSw0MzEgIi8+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTQzMC45LDE4Ni4xYzUuNywzLjUsMTIuNSw1LjQsMTkuOCw0LjZjMTUuMy0xLjYsMjcuNS0xNC40LDI4LjItMjkuOGMwLjktMTguMS0xMy42LTMzLTMxLjUtMzMKCWMtMTcuNCwwLTMxLjUsMTQuMS0zMS41LDMxLjVjMCwzLjYsMC43LDcsMS45LDEwLjNsLTExMS41LDg5Yy00LjgtMy0xMC41LTQuOC0xNi42LTQuOHMtMTEuNywxLjgtMTYuNiw0LjhsLTU4LjgtNDcKCWMxLjEtMy4yLDEuOS02LjYsMS45LTEwLjNjMC0xNy43LTE0LjYtMzItMzIuNS0zMS41Yy0xNS4zLDAuNS0yOC4xLDEyLjItMzAuMiwyNy40Yy0wLjksNi40LDAuNCwxMi40LDIuOSwxNy42TDkzLjMsMjc4CgljLTUuNi0yLjctMTIuMS0zLjktMTkuMS0yLjZjLTE0LjIsMi43LTI1LjEsMTQuOC0yNS45LDI5LjNjLTEsMTguOCwxNC40LDM0LjIsMzMuMiwzMy4yYzE0LjUtMC44LDI2LjYtMTEuNiwyOS4zLTI1LjkKCWMxLjMtNywwLjEtMTMuNC0yLjYtMTkuMWw2My4xLTYzLjFjNC4xLDIsOC42LDMuMiwxMy41LDMuMmM2LjEsMCwxMS43LTEuOCwxNi42LTQuOGw1OC44LDQ3Yy0xLjEsMy4yLTEuOSw2LjctMS45LDEwLjMKCWMwLDE3LjQsMTQuMSwzMS41LDMxLjUsMzEuNXMzMS41LTE0LjEsMzEuNS0zMS41YzAtMy42LTAuNy03LTEuOS0xMC4zTDQzMC45LDE4Ni4xTDQzMC45LDE4Ni4xeiBNNDQ1LjcsMTQ5LjEKCWM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFjLTAuNyw0LjMtNC4yLDcuOS04LjYsOC42Yy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFDNDM3LjksMTUzLjMsNDQxLjQsMTQ5LjgsNDQ1LjcsMTQ5LjF6IE04MS41LDMxNi44CgljLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMWMwLjctNC4zLDQuMi03LjksOC42LTguNmM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFDODkuNCwzMTIuNiw4NS45LDMxNi4xLDgxLjUsMzE2Ljh6IE0xODYuNSwyMTEuOAoJYy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFjMC43LTQuMyw0LjItNy45LDguNi04LjZjNy4yLTEuMiwxMy4zLDUsMTIuMSwxMi4xQzE5NC40LDIwNy42LDE5MC45LDIxMS4xLDE4Ni41LDIxMS44eiBNMjkxLjUsMjk1LjgKCWMtNy4yLDEuMi0xMy4zLTUtMTIuMS0xMi4xYzAuNy00LjMsNC4yLTcuOSw4LjYtOC42YzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMUMyOTkuNCwyOTEuNiwyOTUuOSwyOTUuMSwyOTEuNSwyOTUuOHoiLz4KPGNpcmNsZSBjeD0iMTg0LjIiIGN5PSIxOTkiIHI9IjEzIi8+CjxjaXJjbGUgY3g9IjQ0Ny41IiBjeT0iMTU5LjQiIHI9IjEzIi8+CjxjaXJjbGUgY3g9IjI4OS44IiBjeT0iMjg1LjQiIHI9IjEzIi8+CjxjaXJjbGUgY3g9Ijc5LjgiIGN5PSIzMDYuNCIgcj0iMTMiLz4KPC9zdmc+Cg==)}.m-chart-selector ul.results li .type .icon.column:before{background-image:url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhbCIgZGF0YS1pY29uPSJjaGFydC1iYXIiIGNsYXNzPSJzdmctaW5saW5lLS1mYSBmYS1jaGFydC1iYXIgZmEtdy0xNiIgcm9sZT0iaW1nIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik00MjQgMzUyaDE2YzQuNCAwIDgtMy42IDgtOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40IDAtOCAzLjYtOCA4djI0MGMwIDQuNCAzLjYgOCA4IDh6bS05NiAwaDE2YzQuNCAwIDgtMy42IDgtOFYyMDBjMC00LjQtMy42LTgtOC04aC0xNmMtNC40IDAtOCAzLjYtOCA4djE0NGMwIDQuNCAzLjYgOCA4IDh6bS0xOTIgMGgxNmM0LjQgMCA4LTMuNiA4LTh2LTgwYzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCAwLTggMy42LTggOHY4MGMwIDQuNCAzLjYgOCA4IDh6bTk2IDBoMTZjNC40IDAgOC0zLjYgOC04VjEzNmMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQgMC04IDMuNi04IDh2MjA4YzAgNC40IDMuNiA4IDggOHptMjcyIDY0SDMyVjcyYzAtNC40Mi0zLjU4LTgtOC04SDhjLTQuNDIgMC04IDMuNTgtOCA4djM2MGMwIDguODQgNy4xNiAxNiAxNiAxNmg0ODhjNC40MiAwIDgtMy41OCA4LTh2LTE2YzAtNC40Mi0zLjU4LTgtOC04eiI+PC9wYXRoPjwvc3ZnPg==)}.m-chart-selector ul.results li .type .icon.stacked-column:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izk5OTk5OTt9Cjwvc3R5bGU+CjxwYXRoIGQ9Ik00MjQsMzUyaDE2YzQuNCwwLDgtMy42LDgtOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djI0MEM0MTYsMzQ4LjQsNDE5LjYsMzUyLDQyNCwzNTJ6IE0zMjgsMzUyaDE2CgljNC40LDAsOC0zLjYsOC04VjIwMGMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQsMC04LDMuNi04LDh2MTQ0QzMyMCwzNDguNCwzMjMuNiwzNTIsMzI4LDM1MnogTTEzNiwzNTJoMTZjNC40LDAsOC0zLjYsOC04di04MAoJYzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOHY4MEMxMjgsMzQ4LjQsMTMxLjYsMzUyLDEzNiwzNTJ6IE0yMzIsMzUyaDE2YzQuNCwwLDgtMy42LDgtOFYxMzZjMC00LjQtMy42LTgtOC04aC0xNgoJYy00LjQsMC04LDMuNi04LDh2MjA4QzIyNCwzNDguNCwyMjcuNiwzNTIsMjMyLDM1MnogTTUwNCw0MTZIMzJWNzJjMC00LjQtMy42LTgtOC04SDhjLTQuNCwwLTgsMy42LTgsOHYzNjBjMCw4LjgsNy4yLDE2LDE2LDE2aDQ4OAoJYzQuNCwwLDgtMy42LDgtOHYtMTZDNTEyLDQxOS42LDUwOC40LDQxNiw1MDQsNDE2eiIvPgo8cGF0aCBkPSJNNDI0LDM1MmgxNmM0LjQsMCw4LTMuNiw4LThWMTA0YzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOHYyNDBDNDE2LDM0OC40LDQxOS42LDM1Miw0MjQsMzUyeiBNMzI4LDM1MmgxNgoJYzQuNCwwLDgtMy42LDgtOFYyMDBjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djE0NEMzMjAsMzQ4LjQsMzIzLjYsMzUyLDMyOCwzNTJ6IE0xMzYsMzUyaDE2YzQuNCwwLDgtMy42LDgtOHYtODAKCWMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQsMC04LDMuNi04LDh2ODBDMTI4LDM0OC40LDEzMS42LDM1MiwxMzYsMzUyeiBNMjMyLDM1MmgxNmM0LjQsMCw4LTMuNiw4LThWMTM2YzAtNC40LTMuNi04LTgtOGgtMTYKCWMtNC40LDAtOCwzLjYtOCw4djIwOEMyMjQsMzQ4LjQsMjI3LjYsMzUyLDIzMiwzNTJ6IE01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODgKCWM0LjQsMCw4LTMuNiw4LTh2LTE2QzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQ0OCwxNjkuOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djY1LjggTTM1MiwyNDMuNVYyMDBjMC00LjQtMy42LTgtOC04aC0xNgoJYy00LjQsMC04LDMuNi04LDh2NDMuNSBNMTYwLDI4N3YtMjNjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djIzIE0yNTYsMjExLjVWMTM2YzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOAoJdjc1LjUiLz4KPC9zdmc+Cg==)}.m-chart-selector ul.results li .type .icon.bar:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTg5LDM0MXYxNmMwLDQuNCwzLjYsOCw4LDhoMjQwYzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMzMzLDg5LDMzNi42LDg5LDM0MXoiLz4KPHBhdGggZD0iTTg5LDI2NnYxNmMwLDQuNCwzLjYsOCw4LDhoMTQ0YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMjU4LDg5LDI2MS42LDg5LDI2NnoiLz4KPHBhdGggZD0iTTg5LDExNnYxNmMwLDQuNCwzLjYsOCw4LDhoODBjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxMDgsODksMTExLjYsODksMTE2eiIvPgo8cGF0aCBkPSJNODksMTkxdjE2YzAsNC40LDMuNiw4LDgsOGgyMDhjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxODMsODksMTg2LjYsODksMTkxeiIvPgo8L3N2Zz4K)}.m-chart-selector ul.results li .type .icon.stacked-bar:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izk5OTk5OTt9Cjwvc3R5bGU+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTg5LDM0MXYxNmMwLDQuNCwzLjYsOCw4LDhoMjQwYzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMzMzLDg5LDMzNi42LDg5LDM0MXoiLz4KPHBhdGggZD0iTTg5LDI2NnYxNmMwLDQuNCwzLjYsOCw4LDhoMTQ0YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMjU4LDg5LDI2MS42LDg5LDI2NnoiLz4KPHBhdGggZD0iTTg5LDExNnYxNmMwLDQuNCwzLjYsOCw4LDhoODBjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxMDgsODksMTExLjYsODksMTE2eiIvPgo8cGF0aCBkPSJNODksMTkxdjE2YzAsNC40LDMuNiw4LDgsOGgyMDhjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxODMsODksMTg2LjYsODksMTkxeiIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjMyLDM2NWgxMDZjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIMjMyIi8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xNjQsMjkwaDc4YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04aC03OCIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTMyLDE0MGg0NmM0LjQsMCw4LTMuNiw4LTh2LTE2YzAtNC40LTMuNi04LTgtOGgtNDYiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTI2MCwyMTVoNDZjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThoLTQ2Ii8+Cjwvc3ZnPgo=)}.m-chart-selector ul.results li .type .icon.pie:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNi4xODUgNTcuMjkyIDQzNi40NTYgNDM2Ljc3NiIgd2lkdGg9IjQzNi40NTYiIGhlaWdodD0iNDM2Ljc3NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ5NDk0OTt9Cjwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGQ9Ik0gNDM3LjgxIDI4OS4yNTggTCAyMjAuOTEgMjg5LjI1OCBMIDIyMC45MSA3Mi4zNDggQyAyMjAuOTEgNjMuNjU4IDIxMy4yMSA1Ni40NDggMjA0LjYxIDU3LjY1OCBDIDk1LjcxIDczLjA1OCAxMi41MSAxNjguMTU4IDE2LjMxIDI4Mi4wNDggQyAyMC4wMSAzOTUuNDQ4IDExNC44MSA0OTAuMTU4IDIyOC4yMSA0OTMuOTQ4IEMgMzQyLjExIDQ5Ny42NTggNDM3LjExIDQxNC40NDggNDUyLjUxIDMwNS42NTggQyA0NTMuNzEgMjk2LjkyOCA0NDYuNTEgMjg5LjI1OCA0MzcuODEgMjg5LjI1OCBaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNIDI0OC40NDggNTcuMjAxIEMgMjQwLjE0OCA1Ni42MDEgMjMzLjA0OCA2My43MDEgMjMzLjA0OCA3Mi4wMDEgTCAyMzMuMDQ4IDI3Ni41MDEgTCA0MzcuNTQ4IDI3Ni41MDEgQyA0NDUuOTQ4IDI3Ni41MDEgNDUyLjk0OCAyNjkuNTAxIDQ1Mi4zNDggMjYxLjEwMSBDIDQ0NC44MDggMTUxLjkwMSAzNTcuNjA4IDY0Ljc1MSAyNDguNDQ4IDU3LjIwMSBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=)}.m-chart-selector ul.results li .type .icon.doughnut:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIzNy42MTUgMzMuMzgzIDQzNi40NTYgNDM2LjU1IiB3aWR0aD0iNDM2LjQ1NiIgaGVpZ2h0PSI0MzYuNTUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnM+CiAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiM0OTQ5NDk7fQo8L3N0eWxlPgogIDwvZGVmcz4KICA8cGF0aCBkPSJNIDQ1OS4yNCAyNjUuMTI0IEwgMzgzLjg5IDI2NS4xMjQgQyAzNzMuNTIxIDM2My42OTIgMjYwLjMzNyA0MTQuMDcxIDE4MC4xNiAzNTUuODA3IEMgOTkuOTgyIDI5Ny41NDMgMTEyLjk0NCAxNzQuMzMzIDIwMy40OTEgMTM0LjAyOSBDIDIxNS44MTUgMTI4LjU0NCAyMjguOTI1IDEyNS4wMjkgMjQyLjM0IDEyMy42MTQgTCAyNDIuMzQgNDguMjE0IEMgMjQyLjM0IDM5LjUxNCAyMzQuNjQgMzIuMzE0IDIyNi4wNCAzMy41MTQgQyAxMTcuMTQgNDguOTE0IDMzLjk0IDE0NC4wMTQgMzcuNzQgMjU3LjkxNCBDIDQxLjQ0IDM3MS4zMTQgMTM2LjI0IDQ2Ni4wMTQgMjQ5LjY0IDQ2OS44MTQgQyAzNjMuNTQgNDczLjUxNCA0NTguNTQgMzkwLjMxNCA0NzMuOTQgMjgxLjUxNCBDIDQ3NS4xNCAyNzIuNzk0IDQ2Ny45NCAyNjUuMTI0IDQ1OS4yNCAyNjUuMTI0IFoiLz4KICA8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0gNDc0LjAzIDIzNy4zMjQgQyA0NjYuNTMgMTI4LjEyNCAzNzkuMzMgNDAuOTI0IDI3MC4xMyAzMy40MjQgQyAyNjEuODMgMzIuODI0IDI1NC43MyAzOS45MjQgMjU0LjczIDQ4LjIyNCBMIDI1NC43MyAxMjMuOTc0IEMgMzI1Ljg1MyAxMjMuOTUyIDM4My41MiAxODEuNjAyIDM4My41MiAyNTIuNzI0IEwgNDU5LjI3IDI1Mi43MjQgQyA0NjcuNjMgMjUyLjcyNCA0NzQuNjMgMjQ1LjcyNCA0NzQuMDMgMjM3LjMyNCBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=)}.m-chart-selector ul.results li .type .icon.scatter:before{background-image:url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhbCIgZGF0YS1pY29uPSJjaGFydC1zY2F0dGVyIiBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtY2hhcnQtc2NhdHRlciBmYS13LTE2IiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTUwNCA0MTZIMzJWNzJhOCA4IDAgMCAwLTgtOEg4YTggOCAwIDAgMC04IDh2MzYwYTE2IDE2IDAgMCAwIDE2IDE2aDQ4OGE4IDggMCAwIDAgOC04di0xNmE4IDggMCAwIDAtOC04ek0xNjAgMzEyYTI0IDI0IDAgMSAwLTI0LTI0IDI0IDI0IDAgMCAwIDI0IDI0em0yNTYtMTYwYTI0IDI0IDAgMSAwLTI0LTI0IDI0IDI0IDAgMCAwIDI0IDI0em0tMjI0IDBhMjQgMjQgMCAxIDAtMjQtMjQgMjQgMjQgMCAwIDAgMjQgMjR6bTE5MiAxNjBhMjQgMjQgMCAxIDAtMjQtMjQgMjQgMjQgMCAwIDAgMjQgMjR6bS05Ni02NGEyNCAyNCAwIDEgMC0yNC0yNCAyNCAyNCAwIDAgMCAyNCAyNHoiPjwvcGF0aD48L3N2Zz4=)}.m-chart-selector ul.results li .type .icon.bubble:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNiBDNTEyLDQxOS42LDUwOC40LDQxNiw1MDQsNDE2eiBNMTYwLDMxMmMxMy4zLDAsMjQtMTAuNywyNC0yNHMtMTAuNy0yNC0yNC0yNHMtMjQsMTAuNy0yNCwyNFMxNDYuNywzMTIsMTYwLDMxMnogTTQxNiwxNTIgYzEzLjMsMCwyNC0xMC43LDI0LTI0cy0xMC43LTI0LTI0LTI0cy0yNCwxMC43LTI0LDI0UzQwMi43LDE1Miw0MTYsMTUyeiBNMTkyLDE1MmMxMy4zLDAsMjQtMTAuNywyNC0yNHMtMTAuNy0yNC0yNC0yNCBzLTI0LDEwLjctMjQsMjRTMTc4LjcsMTUyLDE5MiwxNTJ6IE0zODQsMzEyYzEzLjMsMCwyNC0xMC43LDI0LTI0cy0xMC43LTI0LTI0LTI0cy0yNCwxMC43LTI0LDI0UzM3MC43LDMxMiwzODQsMzEyeiBNMjg4LDI0OCBjMTMuMywwLDI0LTEwLjcsMjQtMjRzLTEwLjctMjQtMjQtMjRzLTI0LDEwLjctMjQsMjRTMjc0LjcsMjQ4LDI4OCwyNDh6Ii8+CiAgPGNpcmNsZSBjeD0iMTkyIiBjeT0iMTI4IiByPSIzMy41Ii8+CiAgPGNpcmNsZSBjeD0iMzg0IiBjeT0iMjg4IiByPSIzMy4zIi8+CiAgPGNpcmNsZSBjeD0iMjY4Ljg5NyIgY3k9IjIzNS44MDUiIHI9IjcyLjQ0NSIgc3R5bGU9IiIvPgogIDxjaXJjbGUgY3g9IjMxOS45IiBjeT0iMTE2LjEiIHI9IjQwLjEiLz4KPC9zdmc+)}.m-chart-selector ul.results li .type .icon.radar:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6I0FCQUJBQjtzdHJva2Utd2lkdGg6MjA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQoJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjIwO3N0cm9rZS1taXRlcmxpbWl0OjEwO30KPC9zdHlsZT4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgxLjA1NTcwNiwgMCwgMCwgMS4wNTU3MDYsIC0xNy4yMTE5MzEsIC0xNC4wMzM4NTkpIiBzdHlsZT0iIj4KICAgIDxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyNTguNSIgeTE9IjIzLjkiIHgyPSIyNTguNSIgeTI9IjQ4OC4xIi8+CiAgICA8bGluZSBjbGFzcz0ic3QwIiB4MT0iNDkwLjYiIHkxPSIyNTYiIHgyPSIyNi40IiB5Mj0iMjU2Ii8+CiAgICA8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIyNTguNSIgY3k9IjI1NiIgcj0iMjMyLjEiLz4KICAgIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjI1OC41IiBjeT0iMjU2IiByPSIxNjIuMiIvPgogICAgPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjU4LjUiIGN5PSIyNTYiIHI9IjgxLjkiLz4KICA8L2c+CiAgPGVsbGlwc2UgY3g9IjE1NS42MDciIGN5PSIyMjEuNzA1IiByeD0iMzQuNTIyIiByeT0iMzQuNTIyIiBzdHlsZT0iIi8+CiAgPGNpcmNsZSBjeD0iMzI1LjE1NCIgY3k9Ijk2LjcwOSIgcj0iMzQuNTIyIiBzdHlsZT0iIi8+CiAgPGNpcmNsZSBjeD0iNDIxLjIyMyIgY3k9IjQzMy4zNzQiIHI9IjM0LjUyMiIgc3R5bGU9IiIvPgogIDxwb2x5Z29uIGNsYXNzPSJzdDEiIHBvaW50cz0iMzI1LjE1NCA5Mi42OTggMTU1LjYwNyAyMjEuNzA1IDQyNS4wMjQgNDMzLjM3NCIgc3R5bGU9IiIvPgo8L3N2Zz4=)}.m-chart-selector ul.results li .type .icon.polar:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6I0FCQUJBQjtzdHJva2Utd2lkdGg6MjA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQoJLnN0MXtmaWxsOiMzOTM5Mzk7fQoJLnN0MntmaWxsOiM1RDVENUQ7fQoJLnN0M3tmaWxsOm5vbmU7fQo8L3N0eWxlPgogIDxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyNTUuNzUzIiB5MT0iMTEuMzgzIiB4Mj0iMjU1Ljc1MyIgeTI9IjUwMC43NDUiLz4KICA8bGluZSBjbGFzcz0ic3QwIiB4MT0iNTAwLjQzNCIgeTE9IjI1Ni4wNjQiIHgyPSIxMS4wNzIiIHkyPSIyNTYuMDY0Ii8+CiAgPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjU1Ljc1MyIgY3k9IjI1Ni4wNjQiIHI9IjI0NC42ODEiLz4KICA8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIyNTUuNzUzIiBjeT0iMjU2LjA2NCIgcj0iMTcwLjk5MiIvPgogIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjI1NS43NTMiIGN5PSIyNTYuMDY0IiByPSI4Ni4zMzkiLz4KICA8cGF0aCBjbGFzcz0ic3QxIiBkPSJNIDI1NC40ODggNTAwLjc0NSBMIDI1NC40ODggMjU1LjExNSBMIDQ5OS4wNjQgMjU1LjExNSBMIDQ5OC45NTggMjU1LjUzNyBDIDQ5OC45NTggMzkwLjY4NiAzODkuNDI2IDUwMC4yMTggMjU0LjI3NyA1MDAuMjE4IEwgMjU0LjQ4OCA1MDAuNzQ1IFoiIHN0eWxlPSJmaWxsOiByZ2IoODIsIDgyLCA4Mik7Ii8+CiAgPHBhdGggY2xhc3M9InN0MiIgZD0iTSA3My45MDMgMjU1Ljg1MyBMIDI1NS4wMTUgMjU1Ljg1MyBMIDI1NS4wMTUgNzQuNzQgTCAyNTQuODA1IDc0Ljk1MSBDIDE1NC42NTUgNzQuOTUxIDczLjQ4MSAxNTYuMTI1IDczLjQ4MSAyNTYuMjc1IiBzdHlsZT0iZmlsbDogcmdiKDEyNywgMTI3LCAxMjcpOyIvPgogIDxsaW5lIGNsYXNzPSJzdDMiIHgxPSIxMTQ2LjAyOSIgeTE9IjE5NS45NzQiIHgyPSIxMzE2LjgxMSIgeTI9IjE5NS45NzQiLz4KICA8cGF0aCBkPSJNIDI1NC41OTQgMTU3LjYwMSBMIDI1NC41OTQgMjU1LjUzNyBMIDM1My42ODkgMjU1LjUzNyBMIDM1My4zNzMgMjU2LjA2NCBDIDM1My4zNzMgMjAxLjM1IDMwOS4wOTYgMTU3LjA3NCAyNTQuMzgzIDE1Ny4wNzQiLz4KPC9zdmc+)}.m-chart-selector .chart-selected .type{background:#f1f1f1;padding-bottom:5rem;padding-top:5rem;position:relative}.m-chart-selector .chart-selected .type .icon:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNi4xODUgNTcuMjkyIDQzNi40NTYgNDM2Ljc3NiIgd2lkdGg9IjQzNi40NTYiIGhlaWdodD0iNDM2Ljc3NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ5NDk0OTt9Cjwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGQ9Ik0gNDM3LjgxIDI4OS4yNTggTCAyMjAuOTEgMjg5LjI1OCBMIDIyMC45MSA3Mi4zNDggQyAyMjAuOTEgNjMuNjU4IDIxMy4yMSA1Ni40NDggMjA0LjYxIDU3LjY1OCBDIDk1LjcxIDczLjA1OCAxMi41MSAxNjguMTU4IDE2LjMxIDI4Mi4wNDggQyAyMC4wMSAzOTUuNDQ4IDExNC44MSA0OTAuMTU4IDIyOC4yMSA0OTMuOTQ4IEMgMzQyLjExIDQ5Ny42NTggNDM3LjExIDQxNC40NDggNDUyLjUxIDMwNS42NTggQyA0NTMuNzEgMjk2LjkyOCA0NDYuNTEgMjg5LjI1OCA0MzcuODEgMjg5LjI1OCBaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNIDI0OC40NDggNTcuMjAxIEMgMjQwLjE0OCA1Ni42MDEgMjMzLjA0OCA2My43MDEgMjMzLjA0OCA3Mi4wMDEgTCAyMzMuMDQ4IDI3Ni41MDEgTCA0MzcuNTQ4IDI3Ni41MDEgQyA0NDUuOTQ4IDI3Ni41MDEgNDUyLjk0OCAyNjkuNTAxIDQ1Mi4zNDggMjYxLjEwMSBDIDQ0NC44MDggMTUxLjkwMSAzNTcuNjA4IDY0Ljc1MSAyNDguNDQ4IDU3LjIwMSBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=);background-position:50%;background-repeat:no-repeat;background-size:contain;content:"";display:block;height:47px}.m-chart-selector .chart-selected .type .icon.line:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTQzMC45LDE4Ni4xYzUuNywzLjUsMTIuNSw1LjQsMTkuOCw0LjZjMTUuMy0xLjYsMjcuNS0xNC40LDI4LjItMjkuOGMwLjktMTguMS0xMy42LTMzLTMxLjUtMzMKCWMtMTcuNCwwLTMxLjUsMTQuMS0zMS41LDMxLjVjMCwzLjYsMC43LDcsMS45LDEwLjNsLTExMS41LDg5Yy00LjgtMy0xMC41LTQuOC0xNi42LTQuOGMtNi4xLDAtMTEuNywxLjgtMTYuNiw0LjhsLTU4LjgtNDcKCWMxLjEtMy4yLDEuOS02LjYsMS45LTEwLjNjMC0xNy43LTE0LjYtMzItMzIuNS0zMS41Yy0xNS4zLDAuNS0yOC4xLDEyLjItMzAuMiwyNy40Yy0wLjksNi40LDAuNCwxMi40LDIuOSwxNy42bC02My4xLDYzLjEKCWMtNS42LTIuNy0xMi4xLTMuOS0xOS4xLTIuNmMtMTQuMiwyLjctMjUuMSwxNC44LTI1LjksMjkuM2MtMSwxOC44LDE0LjQsMzQuMiwzMy4yLDMzLjJjMTQuNS0wLjgsMjYuNi0xMS42LDI5LjMtMjUuOQoJYzEuMy03LDAuMS0xMy40LTIuNi0xOS4xbDYzLjEtNjMuMWM0LjEsMiw4LjYsMy4yLDEzLjUsMy4yYzYuMSwwLDExLjctMS44LDE2LjYtNC44bDU4LjgsNDdjLTEuMSwzLjItMS45LDYuNy0xLjksMTAuMwoJYzAsMTcuNCwxNC4xLDMxLjUsMzEuNSwzMS41czMxLjUtMTQuMSwzMS41LTMxLjVjMC0zLjYtMC43LTctMS45LTEwLjNMNDMwLjksMTg2LjFMNDMwLjksMTg2LjF6IE00NDUuNywxNDkuMQoJYzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMWMtMC43LDQuMy00LjIsNy45LTguNiw4LjZjLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMUM0MzcuOSwxNTMuMyw0NDEuNCwxNDkuOCw0NDUuNywxNDkuMXogTTgxLjUsMzE2LjgKCWMtNy4yLDEuMi0xMy4zLTUtMTIuMS0xMi4xYzAuNy00LjMsNC4yLTcuOSw4LjYtOC42YzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMUM4OS40LDMxMi42LDg1LjksMzE2LjEsODEuNSwzMTYuOHogTTE4Ni41LDIxMS44CgljLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMWMwLjctNC4zLDQuMi03LjksOC42LTguNmM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFDMTk0LjQsMjA3LjYsMTkwLjksMjExLjEsMTg2LjUsMjExLjh6IE0yOTEuNSwyOTUuOAoJYy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFjMC43LTQuMyw0LjItNy45LDguNi04LjZjNy4yLTEuMiwxMy4zLDUsMTIuMSwxMi4xQzI5OS40LDI5MS42LDI5NS45LDI5NS4xLDI5MS41LDI5NS44eiIvPgo8Y2lyY2xlIGN4PSIxODQuMiIgY3k9IjE5OSIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iNDQ3LjUiIGN5PSIxNTkuNCIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iMjg5LjgiIGN5PSIyODUuNCIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iNzkuOCIgY3k9IjMwNi40IiByPSIxMyIvPgo8L3N2Zz4K)}.m-chart-selector .chart-selected .type .icon.spline:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjE7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQo8L3N0eWxlPgo8cGF0aCBkPSJNNTA0LDQxNkgzMlY3MmMwLTQuNC0zLjYtOC04LThIOGMtNC40LDAtOCwzLjYtOCw4djM2MGMwLDguOCw3LjIsMTYsMTYsMTZoNDg4YzQuNCwwLDgtMy42LDgtOHYtMTYKCUM1MTIsNDE5LjYsNTA4LjQsNDE2LDUwNCw0MTZ6Ii8+CjxlbGxpcHNlIHRyYW5zZm9ybT0ibWF0cml4KDMuMzI3Nzk0ZS0wMiAtMC45OTk0IDAuOTk5NCAzLjMyNzc5NGUtMDIgLTIzMS4wODQ4IDM3Ny44OTI5KSIgY3g9Ijc5LjgiIGN5PSIzMDguNCIgcng9IjMyLjciIHJ5PSIzMi43Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03OS44LDMwOC40Qzc5LjgsMjQzLDEzMiwxOTksMTg0LjMsMTk5Yzc5LjMsMCw0NC43LDE0OC4xLDExMi4xLDE0OC4xYzEwOC4yLDAsMTUxLjEtMTA2LjEsMTUxLjEtMTg3LjYiLz4KPGNpcmNsZSBjeD0iNDQ3LjYiIGN5PSIxNTkuNCIgcj0iMzIuNyIvPgo8Y2lyY2xlIGN4PSIyOTYuNCIgY3k9IjM0Ny4xIiByPSIzMi43Ii8+CjxjaXJjbGUgY3g9IjE4NCIgY3k9IjIwMS4yIiByPSIzMi43Ii8+Cjwvc3ZnPgo=)}.m-chart-selector .chart-selected .type .icon.area:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe29wYWNpdHk6MC4zMzt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNzkuOCw0MzEgNzkuOCwzMDYuNCAxODQuOCwyMDEuNCAyODkuOCwyODUuNCA0NDcuNSwxNTkuNCA0NDcuNSw0MzEgIi8+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTQzMC45LDE4Ni4xYzUuNywzLjUsMTIuNSw1LjQsMTkuOCw0LjZjMTUuMy0xLjYsMjcuNS0xNC40LDI4LjItMjkuOGMwLjktMTguMS0xMy42LTMzLTMxLjUtMzMKCWMtMTcuNCwwLTMxLjUsMTQuMS0zMS41LDMxLjVjMCwzLjYsMC43LDcsMS45LDEwLjNsLTExMS41LDg5Yy00LjgtMy0xMC41LTQuOC0xNi42LTQuOHMtMTEuNywxLjgtMTYuNiw0LjhsLTU4LjgtNDcKCWMxLjEtMy4yLDEuOS02LjYsMS45LTEwLjNjMC0xNy43LTE0LjYtMzItMzIuNS0zMS41Yy0xNS4zLDAuNS0yOC4xLDEyLjItMzAuMiwyNy40Yy0wLjksNi40LDAuNCwxMi40LDIuOSwxNy42TDkzLjMsMjc4CgljLTUuNi0yLjctMTIuMS0zLjktMTkuMS0yLjZjLTE0LjIsMi43LTI1LjEsMTQuOC0yNS45LDI5LjNjLTEsMTguOCwxNC40LDM0LjIsMzMuMiwzMy4yYzE0LjUtMC44LDI2LjYtMTEuNiwyOS4zLTI1LjkKCWMxLjMtNywwLjEtMTMuNC0yLjYtMTkuMWw2My4xLTYzLjFjNC4xLDIsOC42LDMuMiwxMy41LDMuMmM2LjEsMCwxMS43LTEuOCwxNi42LTQuOGw1OC44LDQ3Yy0xLjEsMy4yLTEuOSw2LjctMS45LDEwLjMKCWMwLDE3LjQsMTQuMSwzMS41LDMxLjUsMzEuNXMzMS41LTE0LjEsMzEuNS0zMS41YzAtMy42LTAuNy03LTEuOS0xMC4zTDQzMC45LDE4Ni4xTDQzMC45LDE4Ni4xeiBNNDQ1LjcsMTQ5LjEKCWM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFjLTAuNyw0LjMtNC4yLDcuOS04LjYsOC42Yy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFDNDM3LjksMTUzLjMsNDQxLjQsMTQ5LjgsNDQ1LjcsMTQ5LjF6IE04MS41LDMxNi44CgljLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMWMwLjctNC4zLDQuMi03LjksOC42LTguNmM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFDODkuNCwzMTIuNiw4NS45LDMxNi4xLDgxLjUsMzE2Ljh6IE0xODYuNSwyMTEuOAoJYy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFjMC43LTQuMyw0LjItNy45LDguNi04LjZjNy4yLTEuMiwxMy4zLDUsMTIuMSwxMi4xQzE5NC40LDIwNy42LDE5MC45LDIxMS4xLDE4Ni41LDIxMS44eiBNMjkxLjUsMjk1LjgKCWMtNy4yLDEuMi0xMy4zLTUtMTIuMS0xMi4xYzAuNy00LjMsNC4yLTcuOSw4LjYtOC42YzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMUMyOTkuNCwyOTEuNiwyOTUuOSwyOTUuMSwyOTEuNSwyOTUuOHoiLz4KPGNpcmNsZSBjeD0iMTg0LjIiIGN5PSIxOTkiIHI9IjEzIi8+CjxjaXJjbGUgY3g9IjQ0Ny41IiBjeT0iMTU5LjQiIHI9IjEzIi8+CjxjaXJjbGUgY3g9IjI4OS44IiBjeT0iMjg1LjQiIHI9IjEzIi8+CjxjaXJjbGUgY3g9Ijc5LjgiIGN5PSIzMDYuNCIgcj0iMTMiLz4KPC9zdmc+Cg==)}.m-chart-selector .chart-selected .type .icon.column:before{background-image:url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhbCIgZGF0YS1pY29uPSJjaGFydC1iYXIiIGNsYXNzPSJzdmctaW5saW5lLS1mYSBmYS1jaGFydC1iYXIgZmEtdy0xNiIgcm9sZT0iaW1nIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik00MjQgMzUyaDE2YzQuNCAwIDgtMy42IDgtOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40IDAtOCAzLjYtOCA4djI0MGMwIDQuNCAzLjYgOCA4IDh6bS05NiAwaDE2YzQuNCAwIDgtMy42IDgtOFYyMDBjMC00LjQtMy42LTgtOC04aC0xNmMtNC40IDAtOCAzLjYtOCA4djE0NGMwIDQuNCAzLjYgOCA4IDh6bS0xOTIgMGgxNmM0LjQgMCA4LTMuNiA4LTh2LTgwYzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCAwLTggMy42LTggOHY4MGMwIDQuNCAzLjYgOCA4IDh6bTk2IDBoMTZjNC40IDAgOC0zLjYgOC04VjEzNmMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQgMC04IDMuNi04IDh2MjA4YzAgNC40IDMuNiA4IDggOHptMjcyIDY0SDMyVjcyYzAtNC40Mi0zLjU4LTgtOC04SDhjLTQuNDIgMC04IDMuNTgtOCA4djM2MGMwIDguODQgNy4xNiAxNiAxNiAxNmg0ODhjNC40MiAwIDgtMy41OCA4LTh2LTE2YzAtNC40Mi0zLjU4LTgtOC04eiI+PC9wYXRoPjwvc3ZnPg==)}.m-chart-selector .chart-selected .type .icon.stacked-column:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izk5OTk5OTt9Cjwvc3R5bGU+CjxwYXRoIGQ9Ik00MjQsMzUyaDE2YzQuNCwwLDgtMy42LDgtOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djI0MEM0MTYsMzQ4LjQsNDE5LjYsMzUyLDQyNCwzNTJ6IE0zMjgsMzUyaDE2CgljNC40LDAsOC0zLjYsOC04VjIwMGMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQsMC04LDMuNi04LDh2MTQ0QzMyMCwzNDguNCwzMjMuNiwzNTIsMzI4LDM1MnogTTEzNiwzNTJoMTZjNC40LDAsOC0zLjYsOC04di04MAoJYzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOHY4MEMxMjgsMzQ4LjQsMTMxLjYsMzUyLDEzNiwzNTJ6IE0yMzIsMzUyaDE2YzQuNCwwLDgtMy42LDgtOFYxMzZjMC00LjQtMy42LTgtOC04aC0xNgoJYy00LjQsMC04LDMuNi04LDh2MjA4QzIyNCwzNDguNCwyMjcuNiwzNTIsMjMyLDM1MnogTTUwNCw0MTZIMzJWNzJjMC00LjQtMy42LTgtOC04SDhjLTQuNCwwLTgsMy42LTgsOHYzNjBjMCw4LjgsNy4yLDE2LDE2LDE2aDQ4OAoJYzQuNCwwLDgtMy42LDgtOHYtMTZDNTEyLDQxOS42LDUwOC40LDQxNiw1MDQsNDE2eiIvPgo8cGF0aCBkPSJNNDI0LDM1MmgxNmM0LjQsMCw4LTMuNiw4LThWMTA0YzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOHYyNDBDNDE2LDM0OC40LDQxOS42LDM1Miw0MjQsMzUyeiBNMzI4LDM1MmgxNgoJYzQuNCwwLDgtMy42LDgtOFYyMDBjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djE0NEMzMjAsMzQ4LjQsMzIzLjYsMzUyLDMyOCwzNTJ6IE0xMzYsMzUyaDE2YzQuNCwwLDgtMy42LDgtOHYtODAKCWMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQsMC04LDMuNi04LDh2ODBDMTI4LDM0OC40LDEzMS42LDM1MiwxMzYsMzUyeiBNMjMyLDM1MmgxNmM0LjQsMCw4LTMuNiw4LThWMTM2YzAtNC40LTMuNi04LTgtOGgtMTYKCWMtNC40LDAtOCwzLjYtOCw4djIwOEMyMjQsMzQ4LjQsMjI3LjYsMzUyLDIzMiwzNTJ6IE01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODgKCWM0LjQsMCw4LTMuNiw4LTh2LTE2QzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQ0OCwxNjkuOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djY1LjggTTM1MiwyNDMuNVYyMDBjMC00LjQtMy42LTgtOC04aC0xNgoJYy00LjQsMC04LDMuNi04LDh2NDMuNSBNMTYwLDI4N3YtMjNjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djIzIE0yNTYsMjExLjVWMTM2YzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOAoJdjc1LjUiLz4KPC9zdmc+Cg==)}.m-chart-selector .chart-selected .type .icon.bar:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTg5LDM0MXYxNmMwLDQuNCwzLjYsOCw4LDhoMjQwYzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMzMzLDg5LDMzNi42LDg5LDM0MXoiLz4KPHBhdGggZD0iTTg5LDI2NnYxNmMwLDQuNCwzLjYsOCw4LDhoMTQ0YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMjU4LDg5LDI2MS42LDg5LDI2NnoiLz4KPHBhdGggZD0iTTg5LDExNnYxNmMwLDQuNCwzLjYsOCw4LDhoODBjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxMDgsODksMTExLjYsODksMTE2eiIvPgo8cGF0aCBkPSJNODksMTkxdjE2YzAsNC40LDMuNiw4LDgsOGgyMDhjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxODMsODksMTg2LjYsODksMTkxeiIvPgo8L3N2Zz4K)}.m-chart-selector .chart-selected .type .icon.stacked-bar:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izk5OTk5OTt9Cjwvc3R5bGU+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTg5LDM0MXYxNmMwLDQuNCwzLjYsOCw4LDhoMjQwYzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMzMzLDg5LDMzNi42LDg5LDM0MXoiLz4KPHBhdGggZD0iTTg5LDI2NnYxNmMwLDQuNCwzLjYsOCw4LDhoMTQ0YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMjU4LDg5LDI2MS42LDg5LDI2NnoiLz4KPHBhdGggZD0iTTg5LDExNnYxNmMwLDQuNCwzLjYsOCw4LDhoODBjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxMDgsODksMTExLjYsODksMTE2eiIvPgo8cGF0aCBkPSJNODksMTkxdjE2YzAsNC40LDMuNiw4LDgsOGgyMDhjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxODMsODksMTg2LjYsODksMTkxeiIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjMyLDM2NWgxMDZjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIMjMyIi8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xNjQsMjkwaDc4YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04aC03OCIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTMyLDE0MGg0NmM0LjQsMCw4LTMuNiw4LTh2LTE2YzAtNC40LTMuNi04LTgtOGgtNDYiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTI2MCwyMTVoNDZjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThoLTQ2Ii8+Cjwvc3ZnPgo=)}.m-chart-selector .chart-selected .type .icon.pie:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNi4xODUgNTcuMjkyIDQzNi40NTYgNDM2Ljc3NiIgd2lkdGg9IjQzNi40NTYiIGhlaWdodD0iNDM2Ljc3NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ5NDk0OTt9Cjwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGQ9Ik0gNDM3LjgxIDI4OS4yNTggTCAyMjAuOTEgMjg5LjI1OCBMIDIyMC45MSA3Mi4zNDggQyAyMjAuOTEgNjMuNjU4IDIxMy4yMSA1Ni40NDggMjA0LjYxIDU3LjY1OCBDIDk1LjcxIDczLjA1OCAxMi41MSAxNjguMTU4IDE2LjMxIDI4Mi4wNDggQyAyMC4wMSAzOTUuNDQ4IDExNC44MSA0OTAuMTU4IDIyOC4yMSA0OTMuOTQ4IEMgMzQyLjExIDQ5Ny42NTggNDM3LjExIDQxNC40NDggNDUyLjUxIDMwNS42NTggQyA0NTMuNzEgMjk2LjkyOCA0NDYuNTEgMjg5LjI1OCA0MzcuODEgMjg5LjI1OCBaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNIDI0OC40NDggNTcuMjAxIEMgMjQwLjE0OCA1Ni42MDEgMjMzLjA0OCA2My43MDEgMjMzLjA0OCA3Mi4wMDEgTCAyMzMuMDQ4IDI3Ni41MDEgTCA0MzcuNTQ4IDI3Ni41MDEgQyA0NDUuOTQ4IDI3Ni41MDEgNDUyLjk0OCAyNjkuNTAxIDQ1Mi4zNDggMjYxLjEwMSBDIDQ0NC44MDggMTUxLjkwMSAzNTcuNjA4IDY0Ljc1MSAyNDguNDQ4IDU3LjIwMSBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=)}.m-chart-selector .chart-selected .type .icon.doughnut:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIzNy42MTUgMzMuMzgzIDQzNi40NTYgNDM2LjU1IiB3aWR0aD0iNDM2LjQ1NiIgaGVpZ2h0PSI0MzYuNTUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnM+CiAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiM0OTQ5NDk7fQo8L3N0eWxlPgogIDwvZGVmcz4KICA8cGF0aCBkPSJNIDQ1OS4yNCAyNjUuMTI0IEwgMzgzLjg5IDI2NS4xMjQgQyAzNzMuNTIxIDM2My42OTIgMjYwLjMzNyA0MTQuMDcxIDE4MC4xNiAzNTUuODA3IEMgOTkuOTgyIDI5Ny41NDMgMTEyLjk0NCAxNzQuMzMzIDIwMy40OTEgMTM0LjAyOSBDIDIxNS44MTUgMTI4LjU0NCAyMjguOTI1IDEyNS4wMjkgMjQyLjM0IDEyMy42MTQgTCAyNDIuMzQgNDguMjE0IEMgMjQyLjM0IDM5LjUxNCAyMzQuNjQgMzIuMzE0IDIyNi4wNCAzMy41MTQgQyAxMTcuMTQgNDguOTE0IDMzLjk0IDE0NC4wMTQgMzcuNzQgMjU3LjkxNCBDIDQxLjQ0IDM3MS4zMTQgMTM2LjI0IDQ2Ni4wMTQgMjQ5LjY0IDQ2OS44MTQgQyAzNjMuNTQgNDczLjUxNCA0NTguNTQgMzkwLjMxNCA0NzMuOTQgMjgxLjUxNCBDIDQ3NS4xNCAyNzIuNzk0IDQ2Ny45NCAyNjUuMTI0IDQ1OS4yNCAyNjUuMTI0IFoiLz4KICA8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0gNDc0LjAzIDIzNy4zMjQgQyA0NjYuNTMgMTI4LjEyNCAzNzkuMzMgNDAuOTI0IDI3MC4xMyAzMy40MjQgQyAyNjEuODMgMzIuODI0IDI1NC43MyAzOS45MjQgMjU0LjczIDQ4LjIyNCBMIDI1NC43MyAxMjMuOTc0IEMgMzI1Ljg1MyAxMjMuOTUyIDM4My41MiAxODEuNjAyIDM4My41MiAyNTIuNzI0IEwgNDU5LjI3IDI1Mi43MjQgQyA0NjcuNjMgMjUyLjcyNCA0NzQuNjMgMjQ1LjcyNCA0NzQuMDMgMjM3LjMyNCBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=)}.m-chart-selector .chart-selected .type .icon.scatter:before{background-image:url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhbCIgZGF0YS1pY29uPSJjaGFydC1zY2F0dGVyIiBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtY2hhcnQtc2NhdHRlciBmYS13LTE2IiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTUwNCA0MTZIMzJWNzJhOCA4IDAgMCAwLTgtOEg4YTggOCAwIDAgMC04IDh2MzYwYTE2IDE2IDAgMCAwIDE2IDE2aDQ4OGE4IDggMCAwIDAgOC04di0xNmE4IDggMCAwIDAtOC04ek0xNjAgMzEyYTI0IDI0IDAgMSAwLTI0LTI0IDI0IDI0IDAgMCAwIDI0IDI0em0yNTYtMTYwYTI0IDI0IDAgMSAwLTI0LTI0IDI0IDI0IDAgMCAwIDI0IDI0em0tMjI0IDBhMjQgMjQgMCAxIDAtMjQtMjQgMjQgMjQgMCAwIDAgMjQgMjR6bTE5MiAxNjBhMjQgMjQgMCAxIDAtMjQtMjQgMjQgMjQgMCAwIDAgMjQgMjR6bS05Ni02NGEyNCAyNCAwIDEgMC0yNC0yNCAyNCAyNCAwIDAgMCAyNCAyNHoiPjwvcGF0aD48L3N2Zz4=)}.m-chart-selector .chart-selected .type .icon.bubble:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNiBDNTEyLDQxOS42LDUwOC40LDQxNiw1MDQsNDE2eiBNMTYwLDMxMmMxMy4zLDAsMjQtMTAuNywyNC0yNHMtMTAuNy0yNC0yNC0yNHMtMjQsMTAuNy0yNCwyNFMxNDYuNywzMTIsMTYwLDMxMnogTTQxNiwxNTIgYzEzLjMsMCwyNC0xMC43LDI0LTI0cy0xMC43LTI0LTI0LTI0cy0yNCwxMC43LTI0LDI0UzQwMi43LDE1Miw0MTYsMTUyeiBNMTkyLDE1MmMxMy4zLDAsMjQtMTAuNywyNC0yNHMtMTAuNy0yNC0yNC0yNCBzLTI0LDEwLjctMjQsMjRTMTc4LjcsMTUyLDE5MiwxNTJ6IE0zODQsMzEyYzEzLjMsMCwyNC0xMC43LDI0LTI0cy0xMC43LTI0LTI0LTI0cy0yNCwxMC43LTI0LDI0UzM3MC43LDMxMiwzODQsMzEyeiBNMjg4LDI0OCBjMTMuMywwLDI0LTEwLjcsMjQtMjRzLTEwLjctMjQtMjQtMjRzLTI0LDEwLjctMjQsMjRTMjc0LjcsMjQ4LDI4OCwyNDh6Ii8+CiAgPGNpcmNsZSBjeD0iMTkyIiBjeT0iMTI4IiByPSIzMy41Ii8+CiAgPGNpcmNsZSBjeD0iMzg0IiBjeT0iMjg4IiByPSIzMy4zIi8+CiAgPGNpcmNsZSBjeD0iMjY4Ljg5NyIgY3k9IjIzNS44MDUiIHI9IjcyLjQ0NSIgc3R5bGU9IiIvPgogIDxjaXJjbGUgY3g9IjMxOS45IiBjeT0iMTE2LjEiIHI9IjQwLjEiLz4KPC9zdmc+)}.m-chart-selector .chart-selected .type .icon.radar:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6I0FCQUJBQjtzdHJva2Utd2lkdGg6MjA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQoJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjIwO3N0cm9rZS1taXRlcmxpbWl0OjEwO30KPC9zdHlsZT4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgxLjA1NTcwNiwgMCwgMCwgMS4wNTU3MDYsIC0xNy4yMTE5MzEsIC0xNC4wMzM4NTkpIiBzdHlsZT0iIj4KICAgIDxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyNTguNSIgeTE9IjIzLjkiIHgyPSIyNTguNSIgeTI9IjQ4OC4xIi8+CiAgICA8bGluZSBjbGFzcz0ic3QwIiB4MT0iNDkwLjYiIHkxPSIyNTYiIHgyPSIyNi40IiB5Mj0iMjU2Ii8+CiAgICA8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIyNTguNSIgY3k9IjI1NiIgcj0iMjMyLjEiLz4KICAgIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjI1OC41IiBjeT0iMjU2IiByPSIxNjIuMiIvPgogICAgPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjU4LjUiIGN5PSIyNTYiIHI9IjgxLjkiLz4KICA8L2c+CiAgPGVsbGlwc2UgY3g9IjE1NS42MDciIGN5PSIyMjEuNzA1IiByeD0iMzQuNTIyIiByeT0iMzQuNTIyIiBzdHlsZT0iIi8+CiAgPGNpcmNsZSBjeD0iMzI1LjE1NCIgY3k9Ijk2LjcwOSIgcj0iMzQuNTIyIiBzdHlsZT0iIi8+CiAgPGNpcmNsZSBjeD0iNDIxLjIyMyIgY3k9IjQzMy4zNzQiIHI9IjM0LjUyMiIgc3R5bGU9IiIvPgogIDxwb2x5Z29uIGNsYXNzPSJzdDEiIHBvaW50cz0iMzI1LjE1NCA5Mi42OTggMTU1LjYwNyAyMjEuNzA1IDQyNS4wMjQgNDMzLjM3NCIgc3R5bGU9IiIvPgo8L3N2Zz4=)}.m-chart-selector .chart-selected .type .icon.polar:before{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6I0FCQUJBQjtzdHJva2Utd2lkdGg6MjA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQoJLnN0MXtmaWxsOiMzOTM5Mzk7fQoJLnN0MntmaWxsOiM1RDVENUQ7fQoJLnN0M3tmaWxsOm5vbmU7fQo8L3N0eWxlPgogIDxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyNTUuNzUzIiB5MT0iMTEuMzgzIiB4Mj0iMjU1Ljc1MyIgeTI9IjUwMC43NDUiLz4KICA8bGluZSBjbGFzcz0ic3QwIiB4MT0iNTAwLjQzNCIgeTE9IjI1Ni4wNjQiIHgyPSIxMS4wNzIiIHkyPSIyNTYuMDY0Ii8+CiAgPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjU1Ljc1MyIgY3k9IjI1Ni4wNjQiIHI9IjI0NC42ODEiLz4KICA8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIyNTUuNzUzIiBjeT0iMjU2LjA2NCIgcj0iMTcwLjk5MiIvPgogIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjI1NS43NTMiIGN5PSIyNTYuMDY0IiByPSI4Ni4zMzkiLz4KICA8cGF0aCBjbGFzcz0ic3QxIiBkPSJNIDI1NC40ODggNTAwLjc0NSBMIDI1NC40ODggMjU1LjExNSBMIDQ5OS4wNjQgMjU1LjExNSBMIDQ5OC45NTggMjU1LjUzNyBDIDQ5OC45NTggMzkwLjY4NiAzODkuNDI2IDUwMC4yMTggMjU0LjI3NyA1MDAuMjE4IEwgMjU0LjQ4OCA1MDAuNzQ1IFoiIHN0eWxlPSJmaWxsOiByZ2IoODIsIDgyLCA4Mik7Ii8+CiAgPHBhdGggY2xhc3M9InN0MiIgZD0iTSA3My45MDMgMjU1Ljg1MyBMIDI1NS4wMTUgMjU1Ljg1MyBMIDI1NS4wMTUgNzQuNzQgTCAyNTQuODA1IDc0Ljk1MSBDIDE1NC42NTUgNzQuOTUxIDczLjQ4MSAxNTYuMTI1IDczLjQ4MSAyNTYuMjc1IiBzdHlsZT0iZmlsbDogcmdiKDEyNywgMTI3LCAxMjcpOyIvPgogIDxsaW5lIGNsYXNzPSJzdDMiIHgxPSIxMTQ2LjAyOSIgeTE9IjE5NS45NzQiIHgyPSIxMzE2LjgxMSIgeTI9IjE5NS45NzQiLz4KICA8cGF0aCBkPSJNIDI1NC41OTQgMTU3LjYwMSBMIDI1NC41OTQgMjU1LjUzNyBMIDM1My42ODkgMjU1LjUzNyBMIDM1My4zNzMgMjU2LjA2NCBDIDM1My4zNzMgMjAxLjM1IDMwOS4wOTYgMTU3LjA3NCAyNTQuMzgzIDE1Ny4wNzQiLz4KPC9zdmc+)}.m-chart-selector .chart-selected .type h4.title{font-size:18pt;font-weight:400;margin-bottom:0;margin-top:2rem;text-align:center;text-transform:none} +/*!***********************************************************************************************************************************************************************************************************************************************************************************************!*\ + !*** css ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[4].use[2]!./node_modules/@wordpress/scripts/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[4].use[3]!./components/block-src/chart/editor.scss ***! + \***********************************************************************************************************************************************************************************************************************************************************************************************/ +.m-chart-selector { + background-color: #fff; +} +.m-chart-selector .viewbox { + width: 100%; +} +.m-chart-selector .components-placeholder__label .dashicons-chart-pie { + margin-right: 0.5rem; +} +.m-chart-selector .search-box .components-base-control__field { + margin-bottom: 0; +} +.m-chart-selector .search-box .count { + font-family: var(--wp--preset--font-family--system-font); + line-height: 2rem; + font-size: 0.8rem; + margin-top: 0; +} +.m-chart-selector ul.results { + max-height: 17rem; + overflow-y: scroll; + overflow-x: hidden; + position: relative; + display: flex; + flex-wrap: wrap; + list-style: none; + gap: 0.5rem; + padding: 0; + margin-bottom: 0; +} +.m-chart-selector ul.results li { + flex-basis: 100%; + height: auto; + text-align: center; + z-index: 10; + background-color: white; + margin: 0; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} +@media (min-width: 600px) { + .m-chart-selector ul.results li { + flex-basis: calc(50% - 7px); + padding-left: 2px; + } +} +@media (min-width: 780px) { + .m-chart-selector ul.results li { + flex-basis: calc(33.3% - 8px); + padding-left: 2px; + } +} +.m-chart-selector ul.results li img, +.m-chart-selector ul.results li .type { + border: 2px solid transparent; + width: 100%; +} +.m-chart-selector ul.results li:hover { + cursor: pointer; +} +.m-chart-selector ul.results li:hover img, +.m-chart-selector ul.results li:hover .type { + border: 2px solid var(--wp-admin-theme-color); + border-radius: 2px; +} +.m-chart-selector ul.results li h6.title { + font-weight: normal; + font-size: 0.8rem; + text-transform: none; + margin: 1rem 5px 0 5px; +} +.m-chart-selector ul.results li.image { + position: relative; +} +.m-chart-selector ul.results li.image h6.title { + text-shadow: -3px -2px 0 #fff, -3px -1px 0 #fff, -3px 0px 0 #fff, -3px 1px 0 #fff, -3px 2px 0 #fff, -2px -3px 0 #fff, -2px -2px 0 #fff, -2px -1px 0 #fff, -2px 0px 0 #fff, -2px 1px 0 #fff, -2px 2px 0 #fff, -2px 3px 0 #fff, -1px -3px 0 #fff, -1px -2px 0 #fff, -1px -1px 0 #fff, -1px 0px 0 #fff, -1px 1px 0 #fff, -1px 2px 0 #fff, -1px 3px 0 #fff, 0px -3px 0 #fff, 0px -2px 0 #fff, 0px -1px 0 #fff, 0px 0px 0 #fff, 0px 1px 0 #fff, 0px 2px 0 #fff, 0px 3px 0 #fff, 1px -3px 0 #fff, 1px -2px 0 #fff, 1px -1px 0 #fff, 1px 0px 0 #fff, 1px 1px 0 #fff, 1px 2px 0 #fff, 1px 3px 0 #fff, 2px -3px 0 #fff, 2px -2px 0 #fff, 2px -1px 0 #fff, 2px 0px 0 #fff, 2px 1px 0 #fff, 2px 2px 0 #fff, 2px 3px 0 #fff, 3px -2px 0 #fff, 3px -1px 0 #fff, 3px 0px 0 #fff, 3px 1px 0 #fff, 3px 2px 0 #fff; + margin-top: 0; + position: absolute; +} +.m-chart-selector ul.results li .type { + position: relative; + padding-top: 2rem; + padding-bottom: 2rem; + background: #f1f1f1; +} +.m-chart-selector ul.results li .type .icon:before { + content: ""; + height: 27px; + display: block; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNi4xODUgNTcuMjkyIDQzNi40NTYgNDM2Ljc3NiIgd2lkdGg9IjQzNi40NTYiIGhlaWdodD0iNDM2Ljc3NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ5NDk0OTt9Cjwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGQ9Ik0gNDM3LjgxIDI4OS4yNTggTCAyMjAuOTEgMjg5LjI1OCBMIDIyMC45MSA3Mi4zNDggQyAyMjAuOTEgNjMuNjU4IDIxMy4yMSA1Ni40NDggMjA0LjYxIDU3LjY1OCBDIDk1LjcxIDczLjA1OCAxMi41MSAxNjguMTU4IDE2LjMxIDI4Mi4wNDggQyAyMC4wMSAzOTUuNDQ4IDExNC44MSA0OTAuMTU4IDIyOC4yMSA0OTMuOTQ4IEMgMzQyLjExIDQ5Ny42NTggNDM3LjExIDQxNC40NDggNDUyLjUxIDMwNS42NTggQyA0NTMuNzEgMjk2LjkyOCA0NDYuNTEgMjg5LjI1OCA0MzcuODEgMjg5LjI1OCBaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNIDI0OC40NDggNTcuMjAxIEMgMjQwLjE0OCA1Ni42MDEgMjMzLjA0OCA2My43MDEgMjMzLjA0OCA3Mi4wMDEgTCAyMzMuMDQ4IDI3Ni41MDEgTCA0MzcuNTQ4IDI3Ni41MDEgQyA0NDUuOTQ4IDI3Ni41MDEgNDUyLjk0OCAyNjkuNTAxIDQ1Mi4zNDggMjYxLjEwMSBDIDQ0NC44MDggMTUxLjkwMSAzNTcuNjA4IDY0Ljc1MSAyNDguNDQ4IDU3LjIwMSBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + background-position: center; +} +.m-chart-selector ul.results li .type .icon.line:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTQzMC45LDE4Ni4xYzUuNywzLjUsMTIuNSw1LjQsMTkuOCw0LjZjMTUuMy0xLjYsMjcuNS0xNC40LDI4LjItMjkuOGMwLjktMTguMS0xMy42LTMzLTMxLjUtMzMKCWMtMTcuNCwwLTMxLjUsMTQuMS0zMS41LDMxLjVjMCwzLjYsMC43LDcsMS45LDEwLjNsLTExMS41LDg5Yy00LjgtMy0xMC41LTQuOC0xNi42LTQuOGMtNi4xLDAtMTEuNywxLjgtMTYuNiw0LjhsLTU4LjgtNDcKCWMxLjEtMy4yLDEuOS02LjYsMS45LTEwLjNjMC0xNy43LTE0LjYtMzItMzIuNS0zMS41Yy0xNS4zLDAuNS0yOC4xLDEyLjItMzAuMiwyNy40Yy0wLjksNi40LDAuNCwxMi40LDIuOSwxNy42bC02My4xLDYzLjEKCWMtNS42LTIuNy0xMi4xLTMuOS0xOS4xLTIuNmMtMTQuMiwyLjctMjUuMSwxNC44LTI1LjksMjkuM2MtMSwxOC44LDE0LjQsMzQuMiwzMy4yLDMzLjJjMTQuNS0wLjgsMjYuNi0xMS42LDI5LjMtMjUuOQoJYzEuMy03LDAuMS0xMy40LTIuNi0xOS4xbDYzLjEtNjMuMWM0LjEsMiw4LjYsMy4yLDEzLjUsMy4yYzYuMSwwLDExLjctMS44LDE2LjYtNC44bDU4LjgsNDdjLTEuMSwzLjItMS45LDYuNy0xLjksMTAuMwoJYzAsMTcuNCwxNC4xLDMxLjUsMzEuNSwzMS41czMxLjUtMTQuMSwzMS41LTMxLjVjMC0zLjYtMC43LTctMS45LTEwLjNMNDMwLjksMTg2LjFMNDMwLjksMTg2LjF6IE00NDUuNywxNDkuMQoJYzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMWMtMC43LDQuMy00LjIsNy45LTguNiw4LjZjLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMUM0MzcuOSwxNTMuMyw0NDEuNCwxNDkuOCw0NDUuNywxNDkuMXogTTgxLjUsMzE2LjgKCWMtNy4yLDEuMi0xMy4zLTUtMTIuMS0xMi4xYzAuNy00LjMsNC4yLTcuOSw4LjYtOC42YzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMUM4OS40LDMxMi42LDg1LjksMzE2LjEsODEuNSwzMTYuOHogTTE4Ni41LDIxMS44CgljLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMWMwLjctNC4zLDQuMi03LjksOC42LTguNmM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFDMTk0LjQsMjA3LjYsMTkwLjksMjExLjEsMTg2LjUsMjExLjh6IE0yOTEuNSwyOTUuOAoJYy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFjMC43LTQuMyw0LjItNy45LDguNi04LjZjNy4yLTEuMiwxMy4zLDUsMTIuMSwxMi4xQzI5OS40LDI5MS42LDI5NS45LDI5NS4xLDI5MS41LDI5NS44eiIvPgo8Y2lyY2xlIGN4PSIxODQuMiIgY3k9IjE5OSIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iNDQ3LjUiIGN5PSIxNTkuNCIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iMjg5LjgiIGN5PSIyODUuNCIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iNzkuOCIgY3k9IjMwNi40IiByPSIxMyIvPgo8L3N2Zz4K); +} +.m-chart-selector ul.results li .type .icon.spline:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjE7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQo8L3N0eWxlPgo8cGF0aCBkPSJNNTA0LDQxNkgzMlY3MmMwLTQuNC0zLjYtOC04LThIOGMtNC40LDAtOCwzLjYtOCw4djM2MGMwLDguOCw3LjIsMTYsMTYsMTZoNDg4YzQuNCwwLDgtMy42LDgtOHYtMTYKCUM1MTIsNDE5LjYsNTA4LjQsNDE2LDUwNCw0MTZ6Ii8+CjxlbGxpcHNlIHRyYW5zZm9ybT0ibWF0cml4KDMuMzI3Nzk0ZS0wMiAtMC45OTk0IDAuOTk5NCAzLjMyNzc5NGUtMDIgLTIzMS4wODQ4IDM3Ny44OTI5KSIgY3g9Ijc5LjgiIGN5PSIzMDguNCIgcng9IjMyLjciIHJ5PSIzMi43Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03OS44LDMwOC40Qzc5LjgsMjQzLDEzMiwxOTksMTg0LjMsMTk5Yzc5LjMsMCw0NC43LDE0OC4xLDExMi4xLDE0OC4xYzEwOC4yLDAsMTUxLjEtMTA2LjEsMTUxLjEtMTg3LjYiLz4KPGNpcmNsZSBjeD0iNDQ3LjYiIGN5PSIxNTkuNCIgcj0iMzIuNyIvPgo8Y2lyY2xlIGN4PSIyOTYuNCIgY3k9IjM0Ny4xIiByPSIzMi43Ii8+CjxjaXJjbGUgY3g9IjE4NCIgY3k9IjIwMS4yIiByPSIzMi43Ii8+Cjwvc3ZnPgo=); +} +.m-chart-selector ul.results li .type .icon.area:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe29wYWNpdHk6MC4zMzt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNzkuOCw0MzEgNzkuOCwzMDYuNCAxODQuOCwyMDEuNCAyODkuOCwyODUuNCA0NDcuNSwxNTkuNCA0NDcuNSw0MzEgIi8+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTQzMC45LDE4Ni4xYzUuNywzLjUsMTIuNSw1LjQsMTkuOCw0LjZjMTUuMy0xLjYsMjcuNS0xNC40LDI4LjItMjkuOGMwLjktMTguMS0xMy42LTMzLTMxLjUtMzMKCWMtMTcuNCwwLTMxLjUsMTQuMS0zMS41LDMxLjVjMCwzLjYsMC43LDcsMS45LDEwLjNsLTExMS41LDg5Yy00LjgtMy0xMC41LTQuOC0xNi42LTQuOHMtMTEuNywxLjgtMTYuNiw0LjhsLTU4LjgtNDcKCWMxLjEtMy4yLDEuOS02LjYsMS45LTEwLjNjMC0xNy43LTE0LjYtMzItMzIuNS0zMS41Yy0xNS4zLDAuNS0yOC4xLDEyLjItMzAuMiwyNy40Yy0wLjksNi40LDAuNCwxMi40LDIuOSwxNy42TDkzLjMsMjc4CgljLTUuNi0yLjctMTIuMS0zLjktMTkuMS0yLjZjLTE0LjIsMi43LTI1LjEsMTQuOC0yNS45LDI5LjNjLTEsMTguOCwxNC40LDM0LjIsMzMuMiwzMy4yYzE0LjUtMC44LDI2LjYtMTEuNiwyOS4zLTI1LjkKCWMxLjMtNywwLjEtMTMuNC0yLjYtMTkuMWw2My4xLTYzLjFjNC4xLDIsOC42LDMuMiwxMy41LDMuMmM2LjEsMCwxMS43LTEuOCwxNi42LTQuOGw1OC44LDQ3Yy0xLjEsMy4yLTEuOSw2LjctMS45LDEwLjMKCWMwLDE3LjQsMTQuMSwzMS41LDMxLjUsMzEuNXMzMS41LTE0LjEsMzEuNS0zMS41YzAtMy42LTAuNy03LTEuOS0xMC4zTDQzMC45LDE4Ni4xTDQzMC45LDE4Ni4xeiBNNDQ1LjcsMTQ5LjEKCWM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFjLTAuNyw0LjMtNC4yLDcuOS04LjYsOC42Yy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFDNDM3LjksMTUzLjMsNDQxLjQsMTQ5LjgsNDQ1LjcsMTQ5LjF6IE04MS41LDMxNi44CgljLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMWMwLjctNC4zLDQuMi03LjksOC42LTguNmM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFDODkuNCwzMTIuNiw4NS45LDMxNi4xLDgxLjUsMzE2Ljh6IE0xODYuNSwyMTEuOAoJYy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFjMC43LTQuMyw0LjItNy45LDguNi04LjZjNy4yLTEuMiwxMy4zLDUsMTIuMSwxMi4xQzE5NC40LDIwNy42LDE5MC45LDIxMS4xLDE4Ni41LDIxMS44eiBNMjkxLjUsMjk1LjgKCWMtNy4yLDEuMi0xMy4zLTUtMTIuMS0xMi4xYzAuNy00LjMsNC4yLTcuOSw4LjYtOC42YzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMUMyOTkuNCwyOTEuNiwyOTUuOSwyOTUuMSwyOTEuNSwyOTUuOHoiLz4KPGNpcmNsZSBjeD0iMTg0LjIiIGN5PSIxOTkiIHI9IjEzIi8+CjxjaXJjbGUgY3g9IjQ0Ny41IiBjeT0iMTU5LjQiIHI9IjEzIi8+CjxjaXJjbGUgY3g9IjI4OS44IiBjeT0iMjg1LjQiIHI9IjEzIi8+CjxjaXJjbGUgY3g9Ijc5LjgiIGN5PSIzMDYuNCIgcj0iMTMiLz4KPC9zdmc+Cg==); +} +.m-chart-selector ul.results li .type .icon.column:before { + background-image: url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhbCIgZGF0YS1pY29uPSJjaGFydC1iYXIiIGNsYXNzPSJzdmctaW5saW5lLS1mYSBmYS1jaGFydC1iYXIgZmEtdy0xNiIgcm9sZT0iaW1nIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik00MjQgMzUyaDE2YzQuNCAwIDgtMy42IDgtOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40IDAtOCAzLjYtOCA4djI0MGMwIDQuNCAzLjYgOCA4IDh6bS05NiAwaDE2YzQuNCAwIDgtMy42IDgtOFYyMDBjMC00LjQtMy42LTgtOC04aC0xNmMtNC40IDAtOCAzLjYtOCA4djE0NGMwIDQuNCAzLjYgOCA4IDh6bS0xOTIgMGgxNmM0LjQgMCA4LTMuNiA4LTh2LTgwYzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCAwLTggMy42LTggOHY4MGMwIDQuNCAzLjYgOCA4IDh6bTk2IDBoMTZjNC40IDAgOC0zLjYgOC04VjEzNmMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQgMC04IDMuNi04IDh2MjA4YzAgNC40IDMuNiA4IDggOHptMjcyIDY0SDMyVjcyYzAtNC40Mi0zLjU4LTgtOC04SDhjLTQuNDIgMC04IDMuNTgtOCA4djM2MGMwIDguODQgNy4xNiAxNiAxNiAxNmg0ODhjNC40MiAwIDgtMy41OCA4LTh2LTE2YzAtNC40Mi0zLjU4LTgtOC04eiI+PC9wYXRoPjwvc3ZnPg==); +} +.m-chart-selector ul.results li .type .icon.stacked-column:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izk5OTk5OTt9Cjwvc3R5bGU+CjxwYXRoIGQ9Ik00MjQsMzUyaDE2YzQuNCwwLDgtMy42LDgtOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djI0MEM0MTYsMzQ4LjQsNDE5LjYsMzUyLDQyNCwzNTJ6IE0zMjgsMzUyaDE2CgljNC40LDAsOC0zLjYsOC04VjIwMGMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQsMC04LDMuNi04LDh2MTQ0QzMyMCwzNDguNCwzMjMuNiwzNTIsMzI4LDM1MnogTTEzNiwzNTJoMTZjNC40LDAsOC0zLjYsOC04di04MAoJYzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOHY4MEMxMjgsMzQ4LjQsMTMxLjYsMzUyLDEzNiwzNTJ6IE0yMzIsMzUyaDE2YzQuNCwwLDgtMy42LDgtOFYxMzZjMC00LjQtMy42LTgtOC04aC0xNgoJYy00LjQsMC04LDMuNi04LDh2MjA4QzIyNCwzNDguNCwyMjcuNiwzNTIsMjMyLDM1MnogTTUwNCw0MTZIMzJWNzJjMC00LjQtMy42LTgtOC04SDhjLTQuNCwwLTgsMy42LTgsOHYzNjBjMCw4LjgsNy4yLDE2LDE2LDE2aDQ4OAoJYzQuNCwwLDgtMy42LDgtOHYtMTZDNTEyLDQxOS42LDUwOC40LDQxNiw1MDQsNDE2eiIvPgo8cGF0aCBkPSJNNDI0LDM1MmgxNmM0LjQsMCw4LTMuNiw4LThWMTA0YzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOHYyNDBDNDE2LDM0OC40LDQxOS42LDM1Miw0MjQsMzUyeiBNMzI4LDM1MmgxNgoJYzQuNCwwLDgtMy42LDgtOFYyMDBjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djE0NEMzMjAsMzQ4LjQsMzIzLjYsMzUyLDMyOCwzNTJ6IE0xMzYsMzUyaDE2YzQuNCwwLDgtMy42LDgtOHYtODAKCWMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQsMC04LDMuNi04LDh2ODBDMTI4LDM0OC40LDEzMS42LDM1MiwxMzYsMzUyeiBNMjMyLDM1MmgxNmM0LjQsMCw4LTMuNiw4LThWMTM2YzAtNC40LTMuNi04LTgtOGgtMTYKCWMtNC40LDAtOCwzLjYtOCw4djIwOEMyMjQsMzQ4LjQsMjI3LjYsMzUyLDIzMiwzNTJ6IE01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODgKCWM0LjQsMCw4LTMuNiw4LTh2LTE2QzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQ0OCwxNjkuOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djY1LjggTTM1MiwyNDMuNVYyMDBjMC00LjQtMy42LTgtOC04aC0xNgoJYy00LjQsMC04LDMuNi04LDh2NDMuNSBNMTYwLDI4N3YtMjNjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djIzIE0yNTYsMjExLjVWMTM2YzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOAoJdjc1LjUiLz4KPC9zdmc+Cg==); +} +.m-chart-selector ul.results li .type .icon.bar:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTg5LDM0MXYxNmMwLDQuNCwzLjYsOCw4LDhoMjQwYzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMzMzLDg5LDMzNi42LDg5LDM0MXoiLz4KPHBhdGggZD0iTTg5LDI2NnYxNmMwLDQuNCwzLjYsOCw4LDhoMTQ0YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMjU4LDg5LDI2MS42LDg5LDI2NnoiLz4KPHBhdGggZD0iTTg5LDExNnYxNmMwLDQuNCwzLjYsOCw4LDhoODBjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxMDgsODksMTExLjYsODksMTE2eiIvPgo8cGF0aCBkPSJNODksMTkxdjE2YzAsNC40LDMuNiw4LDgsOGgyMDhjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxODMsODksMTg2LjYsODksMTkxeiIvPgo8L3N2Zz4K); +} +.m-chart-selector ul.results li .type .icon.stacked-bar:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izk5OTk5OTt9Cjwvc3R5bGU+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTg5LDM0MXYxNmMwLDQuNCwzLjYsOCw4LDhoMjQwYzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMzMzLDg5LDMzNi42LDg5LDM0MXoiLz4KPHBhdGggZD0iTTg5LDI2NnYxNmMwLDQuNCwzLjYsOCw4LDhoMTQ0YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMjU4LDg5LDI2MS42LDg5LDI2NnoiLz4KPHBhdGggZD0iTTg5LDExNnYxNmMwLDQuNCwzLjYsOCw4LDhoODBjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxMDgsODksMTExLjYsODksMTE2eiIvPgo8cGF0aCBkPSJNODksMTkxdjE2YzAsNC40LDMuNiw4LDgsOGgyMDhjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxODMsODksMTg2LjYsODksMTkxeiIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjMyLDM2NWgxMDZjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIMjMyIi8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xNjQsMjkwaDc4YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04aC03OCIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTMyLDE0MGg0NmM0LjQsMCw4LTMuNiw4LTh2LTE2YzAtNC40LTMuNi04LTgtOGgtNDYiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTI2MCwyMTVoNDZjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThoLTQ2Ii8+Cjwvc3ZnPgo=); +} +.m-chart-selector ul.results li .type .icon.pie:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNi4xODUgNTcuMjkyIDQzNi40NTYgNDM2Ljc3NiIgd2lkdGg9IjQzNi40NTYiIGhlaWdodD0iNDM2Ljc3NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ5NDk0OTt9Cjwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGQ9Ik0gNDM3LjgxIDI4OS4yNTggTCAyMjAuOTEgMjg5LjI1OCBMIDIyMC45MSA3Mi4zNDggQyAyMjAuOTEgNjMuNjU4IDIxMy4yMSA1Ni40NDggMjA0LjYxIDU3LjY1OCBDIDk1LjcxIDczLjA1OCAxMi41MSAxNjguMTU4IDE2LjMxIDI4Mi4wNDggQyAyMC4wMSAzOTUuNDQ4IDExNC44MSA0OTAuMTU4IDIyOC4yMSA0OTMuOTQ4IEMgMzQyLjExIDQ5Ny42NTggNDM3LjExIDQxNC40NDggNDUyLjUxIDMwNS42NTggQyA0NTMuNzEgMjk2LjkyOCA0NDYuNTEgMjg5LjI1OCA0MzcuODEgMjg5LjI1OCBaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNIDI0OC40NDggNTcuMjAxIEMgMjQwLjE0OCA1Ni42MDEgMjMzLjA0OCA2My43MDEgMjMzLjA0OCA3Mi4wMDEgTCAyMzMuMDQ4IDI3Ni41MDEgTCA0MzcuNTQ4IDI3Ni41MDEgQyA0NDUuOTQ4IDI3Ni41MDEgNDUyLjk0OCAyNjkuNTAxIDQ1Mi4zNDggMjYxLjEwMSBDIDQ0NC44MDggMTUxLjkwMSAzNTcuNjA4IDY0Ljc1MSAyNDguNDQ4IDU3LjIwMSBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=); +} +.m-chart-selector ul.results li .type .icon.doughnut:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIzNy42MTUgMzMuMzgzIDQzNi40NTYgNDM2LjU1IiB3aWR0aD0iNDM2LjQ1NiIgaGVpZ2h0PSI0MzYuNTUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnM+CiAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiM0OTQ5NDk7fQo8L3N0eWxlPgogIDwvZGVmcz4KICA8cGF0aCBkPSJNIDQ1OS4yNCAyNjUuMTI0IEwgMzgzLjg5IDI2NS4xMjQgQyAzNzMuNTIxIDM2My42OTIgMjYwLjMzNyA0MTQuMDcxIDE4MC4xNiAzNTUuODA3IEMgOTkuOTgyIDI5Ny41NDMgMTEyLjk0NCAxNzQuMzMzIDIwMy40OTEgMTM0LjAyOSBDIDIxNS44MTUgMTI4LjU0NCAyMjguOTI1IDEyNS4wMjkgMjQyLjM0IDEyMy42MTQgTCAyNDIuMzQgNDguMjE0IEMgMjQyLjM0IDM5LjUxNCAyMzQuNjQgMzIuMzE0IDIyNi4wNCAzMy41MTQgQyAxMTcuMTQgNDguOTE0IDMzLjk0IDE0NC4wMTQgMzcuNzQgMjU3LjkxNCBDIDQxLjQ0IDM3MS4zMTQgMTM2LjI0IDQ2Ni4wMTQgMjQ5LjY0IDQ2OS44MTQgQyAzNjMuNTQgNDczLjUxNCA0NTguNTQgMzkwLjMxNCA0NzMuOTQgMjgxLjUxNCBDIDQ3NS4xNCAyNzIuNzk0IDQ2Ny45NCAyNjUuMTI0IDQ1OS4yNCAyNjUuMTI0IFoiLz4KICA8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0gNDc0LjAzIDIzNy4zMjQgQyA0NjYuNTMgMTI4LjEyNCAzNzkuMzMgNDAuOTI0IDI3MC4xMyAzMy40MjQgQyAyNjEuODMgMzIuODI0IDI1NC43MyAzOS45MjQgMjU0LjczIDQ4LjIyNCBMIDI1NC43MyAxMjMuOTc0IEMgMzI1Ljg1MyAxMjMuOTUyIDM4My41MiAxODEuNjAyIDM4My41MiAyNTIuNzI0IEwgNDU5LjI3IDI1Mi43MjQgQyA0NjcuNjMgMjUyLjcyNCA0NzQuNjMgMjQ1LjcyNCA0NzQuMDMgMjM3LjMyNCBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=); +} +.m-chart-selector ul.results li .type .icon.scatter:before { + background-image: url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhbCIgZGF0YS1pY29uPSJjaGFydC1zY2F0dGVyIiBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtY2hhcnQtc2NhdHRlciBmYS13LTE2IiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTUwNCA0MTZIMzJWNzJhOCA4IDAgMCAwLTgtOEg4YTggOCAwIDAgMC04IDh2MzYwYTE2IDE2IDAgMCAwIDE2IDE2aDQ4OGE4IDggMCAwIDAgOC04di0xNmE4IDggMCAwIDAtOC04ek0xNjAgMzEyYTI0IDI0IDAgMSAwLTI0LTI0IDI0IDI0IDAgMCAwIDI0IDI0em0yNTYtMTYwYTI0IDI0IDAgMSAwLTI0LTI0IDI0IDI0IDAgMCAwIDI0IDI0em0tMjI0IDBhMjQgMjQgMCAxIDAtMjQtMjQgMjQgMjQgMCAwIDAgMjQgMjR6bTE5MiAxNjBhMjQgMjQgMCAxIDAtMjQtMjQgMjQgMjQgMCAwIDAgMjQgMjR6bS05Ni02NGEyNCAyNCAwIDEgMC0yNC0yNCAyNCAyNCAwIDAgMCAyNCAyNHoiPjwvcGF0aD48L3N2Zz4=); +} +.m-chart-selector ul.results li .type .icon.bubble:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNiBDNTEyLDQxOS42LDUwOC40LDQxNiw1MDQsNDE2eiBNMTYwLDMxMmMxMy4zLDAsMjQtMTAuNywyNC0yNHMtMTAuNy0yNC0yNC0yNHMtMjQsMTAuNy0yNCwyNFMxNDYuNywzMTIsMTYwLDMxMnogTTQxNiwxNTIgYzEzLjMsMCwyNC0xMC43LDI0LTI0cy0xMC43LTI0LTI0LTI0cy0yNCwxMC43LTI0LDI0UzQwMi43LDE1Miw0MTYsMTUyeiBNMTkyLDE1MmMxMy4zLDAsMjQtMTAuNywyNC0yNHMtMTAuNy0yNC0yNC0yNCBzLTI0LDEwLjctMjQsMjRTMTc4LjcsMTUyLDE5MiwxNTJ6IE0zODQsMzEyYzEzLjMsMCwyNC0xMC43LDI0LTI0cy0xMC43LTI0LTI0LTI0cy0yNCwxMC43LTI0LDI0UzM3MC43LDMxMiwzODQsMzEyeiBNMjg4LDI0OCBjMTMuMywwLDI0LTEwLjcsMjQtMjRzLTEwLjctMjQtMjQtMjRzLTI0LDEwLjctMjQsMjRTMjc0LjcsMjQ4LDI4OCwyNDh6Ii8+CiAgPGNpcmNsZSBjeD0iMTkyIiBjeT0iMTI4IiByPSIzMy41Ii8+CiAgPGNpcmNsZSBjeD0iMzg0IiBjeT0iMjg4IiByPSIzMy4zIi8+CiAgPGNpcmNsZSBjeD0iMjY4Ljg5NyIgY3k9IjIzNS44MDUiIHI9IjcyLjQ0NSIgc3R5bGU9IiIvPgogIDxjaXJjbGUgY3g9IjMxOS45IiBjeT0iMTE2LjEiIHI9IjQwLjEiLz4KPC9zdmc+); +} +.m-chart-selector ul.results li .type .icon.radar:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6I0FCQUJBQjtzdHJva2Utd2lkdGg6MjA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQoJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjIwO3N0cm9rZS1taXRlcmxpbWl0OjEwO30KPC9zdHlsZT4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgxLjA1NTcwNiwgMCwgMCwgMS4wNTU3MDYsIC0xNy4yMTE5MzEsIC0xNC4wMzM4NTkpIiBzdHlsZT0iIj4KICAgIDxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyNTguNSIgeTE9IjIzLjkiIHgyPSIyNTguNSIgeTI9IjQ4OC4xIi8+CiAgICA8bGluZSBjbGFzcz0ic3QwIiB4MT0iNDkwLjYiIHkxPSIyNTYiIHgyPSIyNi40IiB5Mj0iMjU2Ii8+CiAgICA8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIyNTguNSIgY3k9IjI1NiIgcj0iMjMyLjEiLz4KICAgIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjI1OC41IiBjeT0iMjU2IiByPSIxNjIuMiIvPgogICAgPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjU4LjUiIGN5PSIyNTYiIHI9IjgxLjkiLz4KICA8L2c+CiAgPGVsbGlwc2UgY3g9IjE1NS42MDciIGN5PSIyMjEuNzA1IiByeD0iMzQuNTIyIiByeT0iMzQuNTIyIiBzdHlsZT0iIi8+CiAgPGNpcmNsZSBjeD0iMzI1LjE1NCIgY3k9Ijk2LjcwOSIgcj0iMzQuNTIyIiBzdHlsZT0iIi8+CiAgPGNpcmNsZSBjeD0iNDIxLjIyMyIgY3k9IjQzMy4zNzQiIHI9IjM0LjUyMiIgc3R5bGU9IiIvPgogIDxwb2x5Z29uIGNsYXNzPSJzdDEiIHBvaW50cz0iMzI1LjE1NCA5Mi42OTggMTU1LjYwNyAyMjEuNzA1IDQyNS4wMjQgNDMzLjM3NCIgc3R5bGU9IiIvPgo8L3N2Zz4=); +} +.m-chart-selector ul.results li .type .icon.polar:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6I0FCQUJBQjtzdHJva2Utd2lkdGg6MjA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQoJLnN0MXtmaWxsOiMzOTM5Mzk7fQoJLnN0MntmaWxsOiM1RDVENUQ7fQoJLnN0M3tmaWxsOm5vbmU7fQo8L3N0eWxlPgogIDxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyNTUuNzUzIiB5MT0iMTEuMzgzIiB4Mj0iMjU1Ljc1MyIgeTI9IjUwMC43NDUiLz4KICA8bGluZSBjbGFzcz0ic3QwIiB4MT0iNTAwLjQzNCIgeTE9IjI1Ni4wNjQiIHgyPSIxMS4wNzIiIHkyPSIyNTYuMDY0Ii8+CiAgPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjU1Ljc1MyIgY3k9IjI1Ni4wNjQiIHI9IjI0NC42ODEiLz4KICA8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIyNTUuNzUzIiBjeT0iMjU2LjA2NCIgcj0iMTcwLjk5MiIvPgogIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjI1NS43NTMiIGN5PSIyNTYuMDY0IiByPSI4Ni4zMzkiLz4KICA8cGF0aCBjbGFzcz0ic3QxIiBkPSJNIDI1NC40ODggNTAwLjc0NSBMIDI1NC40ODggMjU1LjExNSBMIDQ5OS4wNjQgMjU1LjExNSBMIDQ5OC45NTggMjU1LjUzNyBDIDQ5OC45NTggMzkwLjY4NiAzODkuNDI2IDUwMC4yMTggMjU0LjI3NyA1MDAuMjE4IEwgMjU0LjQ4OCA1MDAuNzQ1IFoiIHN0eWxlPSJmaWxsOiByZ2IoODIsIDgyLCA4Mik7Ii8+CiAgPHBhdGggY2xhc3M9InN0MiIgZD0iTSA3My45MDMgMjU1Ljg1MyBMIDI1NS4wMTUgMjU1Ljg1MyBMIDI1NS4wMTUgNzQuNzQgTCAyNTQuODA1IDc0Ljk1MSBDIDE1NC42NTUgNzQuOTUxIDczLjQ4MSAxNTYuMTI1IDczLjQ4MSAyNTYuMjc1IiBzdHlsZT0iZmlsbDogcmdiKDEyNywgMTI3LCAxMjcpOyIvPgogIDxsaW5lIGNsYXNzPSJzdDMiIHgxPSIxMTQ2LjAyOSIgeTE9IjE5NS45NzQiIHgyPSIxMzE2LjgxMSIgeTI9IjE5NS45NzQiLz4KICA8cGF0aCBkPSJNIDI1NC41OTQgMTU3LjYwMSBMIDI1NC41OTQgMjU1LjUzNyBMIDM1My42ODkgMjU1LjUzNyBMIDM1My4zNzMgMjU2LjA2NCBDIDM1My4zNzMgMjAxLjM1IDMwOS4wOTYgMTU3LjA3NCAyNTQuMzgzIDE1Ny4wNzQiLz4KPC9zdmc+); +} +.m-chart-selector ul.results li.loading-more { + flex-basis: 100%; + padding: 1rem 0; +} +.m-chart-selector .chart-selected .image { + overflow: hidden; +} +.m-chart-selector .chart-selected .image img.preview { + display: block; + max-width: 100%; + height: auto; +} +.m-chart-selector .chart-selected .image, +.m-chart-selector .chart-selected .no-image { + background: #f1f1f1; + display: flex; + align-items: center; +} +.m-chart-selector .chart-selected .image .type, +.m-chart-selector .chart-selected .no-image .type { + flex: 1; +} +.m-chart-selector .chart-selected .image .type .icon:before, +.m-chart-selector .chart-selected .no-image .type .icon:before { + content: ""; + height: 47px; + display: block; + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNi4xODUgNTcuMjkyIDQzNi40NTYgNDM2Ljc3NiIgd2lkdGg9IjQzNi40NTYiIGhlaWdodD0iNDM2Ljc3NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ5NDk0OTt9Cjwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGQ9Ik0gNDM3LjgxIDI4OS4yNTggTCAyMjAuOTEgMjg5LjI1OCBMIDIyMC45MSA3Mi4zNDggQyAyMjAuOTEgNjMuNjU4IDIxMy4yMSA1Ni40NDggMjA0LjYxIDU3LjY1OCBDIDk1LjcxIDczLjA1OCAxMi41MSAxNjguMTU4IDE2LjMxIDI4Mi4wNDggQyAyMC4wMSAzOTUuNDQ4IDExNC44MSA0OTAuMTU4IDIyOC4yMSA0OTMuOTQ4IEMgMzQyLjExIDQ5Ny42NTggNDM3LjExIDQxNC40NDggNDUyLjUxIDMwNS42NTggQyA0NTMuNzEgMjk2LjkyOCA0NDYuNTEgMjg5LjI1OCA0MzcuODEgMjg5LjI1OCBaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNIDI0OC40NDggNTcuMjAxIEMgMjQwLjE0OCA1Ni42MDEgMjMzLjA0OCA2My43MDEgMjMzLjA0OCA3Mi4wMDEgTCAyMzMuMDQ4IDI3Ni41MDEgTCA0MzcuNTQ4IDI3Ni41MDEgQyA0NDUuOTQ4IDI3Ni41MDEgNDUyLjk0OCAyNjkuNTAxIDQ1Mi4zNDggMjYxLjEwMSBDIDQ0NC44MDggMTUxLjkwMSAzNTcuNjA4IDY0Ljc1MSAyNDguNDQ4IDU3LjIwMSBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=); + background-repeat: no-repeat; + background-size: contain; + background-position: center; +} +.m-chart-selector .chart-selected .image .type .icon.line:before, +.m-chart-selector .chart-selected .no-image .type .icon.line:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTQzMC45LDE4Ni4xYzUuNywzLjUsMTIuNSw1LjQsMTkuOCw0LjZjMTUuMy0xLjYsMjcuNS0xNC40LDI4LjItMjkuOGMwLjktMTguMS0xMy42LTMzLTMxLjUtMzMKCWMtMTcuNCwwLTMxLjUsMTQuMS0zMS41LDMxLjVjMCwzLjYsMC43LDcsMS45LDEwLjNsLTExMS41LDg5Yy00LjgtMy0xMC41LTQuOC0xNi42LTQuOGMtNi4xLDAtMTEuNywxLjgtMTYuNiw0LjhsLTU4LjgtNDcKCWMxLjEtMy4yLDEuOS02LjYsMS45LTEwLjNjMC0xNy43LTE0LjYtMzItMzIuNS0zMS41Yy0xNS4zLDAuNS0yOC4xLDEyLjItMzAuMiwyNy40Yy0wLjksNi40LDAuNCwxMi40LDIuOSwxNy42bC02My4xLDYzLjEKCWMtNS42LTIuNy0xMi4xLTMuOS0xOS4xLTIuNmMtMTQuMiwyLjctMjUuMSwxNC44LTI1LjksMjkuM2MtMSwxOC44LDE0LjQsMzQuMiwzMy4yLDMzLjJjMTQuNS0wLjgsMjYuNi0xMS42LDI5LjMtMjUuOQoJYzEuMy03LDAuMS0xMy40LTIuNi0xOS4xbDYzLjEtNjMuMWM0LjEsMiw4LjYsMy4yLDEzLjUsMy4yYzYuMSwwLDExLjctMS44LDE2LjYtNC44bDU4LjgsNDdjLTEuMSwzLjItMS45LDYuNy0xLjksMTAuMwoJYzAsMTcuNCwxNC4xLDMxLjUsMzEuNSwzMS41czMxLjUtMTQuMSwzMS41LTMxLjVjMC0zLjYtMC43LTctMS45LTEwLjNMNDMwLjksMTg2LjFMNDMwLjksMTg2LjF6IE00NDUuNywxNDkuMQoJYzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMWMtMC43LDQuMy00LjIsNy45LTguNiw4LjZjLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMUM0MzcuOSwxNTMuMyw0NDEuNCwxNDkuOCw0NDUuNywxNDkuMXogTTgxLjUsMzE2LjgKCWMtNy4yLDEuMi0xMy4zLTUtMTIuMS0xMi4xYzAuNy00LjMsNC4yLTcuOSw4LjYtOC42YzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMUM4OS40LDMxMi42LDg1LjksMzE2LjEsODEuNSwzMTYuOHogTTE4Ni41LDIxMS44CgljLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMWMwLjctNC4zLDQuMi03LjksOC42LTguNmM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFDMTk0LjQsMjA3LjYsMTkwLjksMjExLjEsMTg2LjUsMjExLjh6IE0yOTEuNSwyOTUuOAoJYy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFjMC43LTQuMyw0LjItNy45LDguNi04LjZjNy4yLTEuMiwxMy4zLDUsMTIuMSwxMi4xQzI5OS40LDI5MS42LDI5NS45LDI5NS4xLDI5MS41LDI5NS44eiIvPgo8Y2lyY2xlIGN4PSIxODQuMiIgY3k9IjE5OSIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iNDQ3LjUiIGN5PSIxNTkuNCIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iMjg5LjgiIGN5PSIyODUuNCIgcj0iMTMiLz4KPGNpcmNsZSBjeD0iNzkuOCIgY3k9IjMwNi40IiByPSIxMyIvPgo8L3N2Zz4K); +} +.m-chart-selector .chart-selected .image .type .icon.spline:before, +.m-chart-selector .chart-selected .no-image .type .icon.spline:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjE7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQo8L3N0eWxlPgo8cGF0aCBkPSJNNTA0LDQxNkgzMlY3MmMwLTQuNC0zLjYtOC04LThIOGMtNC40LDAtOCwzLjYtOCw4djM2MGMwLDguOCw3LjIsMTYsMTYsMTZoNDg4YzQuNCwwLDgtMy42LDgtOHYtMTYKCUM1MTIsNDE5LjYsNTA4LjQsNDE2LDUwNCw0MTZ6Ii8+CjxlbGxpcHNlIHRyYW5zZm9ybT0ibWF0cml4KDMuMzI3Nzk0ZS0wMiAtMC45OTk0IDAuOTk5NCAzLjMyNzc5NGUtMDIgLTIzMS4wODQ4IDM3Ny44OTI5KSIgY3g9Ijc5LjgiIGN5PSIzMDguNCIgcng9IjMyLjciIHJ5PSIzMi43Ii8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03OS44LDMwOC40Qzc5LjgsMjQzLDEzMiwxOTksMTg0LjMsMTk5Yzc5LjMsMCw0NC43LDE0OC4xLDExMi4xLDE0OC4xYzEwOC4yLDAsMTUxLjEtMTA2LjEsMTUxLjEtMTg3LjYiLz4KPGNpcmNsZSBjeD0iNDQ3LjYiIGN5PSIxNTkuNCIgcj0iMzIuNyIvPgo8Y2lyY2xlIGN4PSIyOTYuNCIgY3k9IjM0Ny4xIiByPSIzMi43Ii8+CjxjaXJjbGUgY3g9IjE4NCIgY3k9IjIwMS4yIiByPSIzMi43Ii8+Cjwvc3ZnPgo=); +} +.m-chart-selector .chart-selected .image .type .icon.area:before, +.m-chart-selector .chart-selected .no-image .type .icon.area:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe29wYWNpdHk6MC4zMzt9Cjwvc3R5bGU+Cjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNzkuOCw0MzEgNzkuOCwzMDYuNCAxODQuOCwyMDEuNCAyODkuOCwyODUuNCA0NDcuNSwxNTkuNCA0NDcuNSw0MzEgIi8+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTQzMC45LDE4Ni4xYzUuNywzLjUsMTIuNSw1LjQsMTkuOCw0LjZjMTUuMy0xLjYsMjcuNS0xNC40LDI4LjItMjkuOGMwLjktMTguMS0xMy42LTMzLTMxLjUtMzMKCWMtMTcuNCwwLTMxLjUsMTQuMS0zMS41LDMxLjVjMCwzLjYsMC43LDcsMS45LDEwLjNsLTExMS41LDg5Yy00LjgtMy0xMC41LTQuOC0xNi42LTQuOHMtMTEuNywxLjgtMTYuNiw0LjhsLTU4LjgtNDcKCWMxLjEtMy4yLDEuOS02LjYsMS45LTEwLjNjMC0xNy43LTE0LjYtMzItMzIuNS0zMS41Yy0xNS4zLDAuNS0yOC4xLDEyLjItMzAuMiwyNy40Yy0wLjksNi40LDAuNCwxMi40LDIuOSwxNy42TDkzLjMsMjc4CgljLTUuNi0yLjctMTIuMS0zLjktMTkuMS0yLjZjLTE0LjIsMi43LTI1LjEsMTQuOC0yNS45LDI5LjNjLTEsMTguOCwxNC40LDM0LjIsMzMuMiwzMy4yYzE0LjUtMC44LDI2LjYtMTEuNiwyOS4zLTI1LjkKCWMxLjMtNywwLjEtMTMuNC0yLjYtMTkuMWw2My4xLTYzLjFjNC4xLDIsOC42LDMuMiwxMy41LDMuMmM2LjEsMCwxMS43LTEuOCwxNi42LTQuOGw1OC44LDQ3Yy0xLjEsMy4yLTEuOSw2LjctMS45LDEwLjMKCWMwLDE3LjQsMTQuMSwzMS41LDMxLjUsMzEuNXMzMS41LTE0LjEsMzEuNS0zMS41YzAtMy42LTAuNy03LTEuOS0xMC4zTDQzMC45LDE4Ni4xTDQzMC45LDE4Ni4xeiBNNDQ1LjcsMTQ5LjEKCWM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFjLTAuNyw0LjMtNC4yLDcuOS04LjYsOC42Yy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFDNDM3LjksMTUzLjMsNDQxLjQsMTQ5LjgsNDQ1LjcsMTQ5LjF6IE04MS41LDMxNi44CgljLTcuMiwxLjItMTMuMy01LTEyLjEtMTIuMWMwLjctNC4zLDQuMi03LjksOC42LTguNmM3LjItMS4yLDEzLjMsNSwxMi4xLDEyLjFDODkuNCwzMTIuNiw4NS45LDMxNi4xLDgxLjUsMzE2Ljh6IE0xODYuNSwyMTEuOAoJYy03LjIsMS4yLTEzLjMtNS0xMi4xLTEyLjFjMC43LTQuMyw0LjItNy45LDguNi04LjZjNy4yLTEuMiwxMy4zLDUsMTIuMSwxMi4xQzE5NC40LDIwNy42LDE5MC45LDIxMS4xLDE4Ni41LDIxMS44eiBNMjkxLjUsMjk1LjgKCWMtNy4yLDEuMi0xMy4zLTUtMTIuMS0xMi4xYzAuNy00LjMsNC4yLTcuOSw4LjYtOC42YzcuMi0xLjIsMTMuMyw1LDEyLjEsMTIuMUMyOTkuNCwyOTEuNiwyOTUuOSwyOTUuMSwyOTEuNSwyOTUuOHoiLz4KPGNpcmNsZSBjeD0iMTg0LjIiIGN5PSIxOTkiIHI9IjEzIi8+CjxjaXJjbGUgY3g9IjQ0Ny41IiBjeT0iMTU5LjQiIHI9IjEzIi8+CjxjaXJjbGUgY3g9IjI4OS44IiBjeT0iMjg1LjQiIHI9IjEzIi8+CjxjaXJjbGUgY3g9Ijc5LjgiIGN5PSIzMDYuNCIgcj0iMTMiLz4KPC9zdmc+Cg==); +} +.m-chart-selector .chart-selected .image .type .icon.column:before, +.m-chart-selector .chart-selected .no-image .type .icon.column:before { + background-image: url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhbCIgZGF0YS1pY29uPSJjaGFydC1iYXIiIGNsYXNzPSJzdmctaW5saW5lLS1mYSBmYS1jaGFydC1iYXIgZmEtdy0xNiIgcm9sZT0iaW1nIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik00MjQgMzUyaDE2YzQuNCAwIDgtMy42IDgtOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40IDAtOCAzLjYtOCA4djI0MGMwIDQuNCAzLjYgOCA4IDh6bS05NiAwaDE2YzQuNCAwIDgtMy42IDgtOFYyMDBjMC00LjQtMy42LTgtOC04aC0xNmMtNC40IDAtOCAzLjYtOCA4djE0NGMwIDQuNCAzLjYgOCA4IDh6bS0xOTIgMGgxNmM0LjQgMCA4LTMuNiA4LTh2LTgwYzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCAwLTggMy42LTggOHY4MGMwIDQuNCAzLjYgOCA4IDh6bTk2IDBoMTZjNC40IDAgOC0zLjYgOC04VjEzNmMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQgMC04IDMuNi04IDh2MjA4YzAgNC40IDMuNiA4IDggOHptMjcyIDY0SDMyVjcyYzAtNC40Mi0zLjU4LTgtOC04SDhjLTQuNDIgMC04IDMuNTgtOCA4djM2MGMwIDguODQgNy4xNiAxNiAxNiAxNmg0ODhjNC40MiAwIDgtMy41OCA4LTh2LTE2YzAtNC40Mi0zLjU4LTgtOC04eiI+PC9wYXRoPjwvc3ZnPg==); +} +.m-chart-selector .chart-selected .image .type .icon.stacked-column:before, +.m-chart-selector .chart-selected .no-image .type .icon.stacked-column:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izk5OTk5OTt9Cjwvc3R5bGU+CjxwYXRoIGQ9Ik00MjQsMzUyaDE2YzQuNCwwLDgtMy42LDgtOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djI0MEM0MTYsMzQ4LjQsNDE5LjYsMzUyLDQyNCwzNTJ6IE0zMjgsMzUyaDE2CgljNC40LDAsOC0zLjYsOC04VjIwMGMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQsMC04LDMuNi04LDh2MTQ0QzMyMCwzNDguNCwzMjMuNiwzNTIsMzI4LDM1MnogTTEzNiwzNTJoMTZjNC40LDAsOC0zLjYsOC04di04MAoJYzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOHY4MEMxMjgsMzQ4LjQsMTMxLjYsMzUyLDEzNiwzNTJ6IE0yMzIsMzUyaDE2YzQuNCwwLDgtMy42LDgtOFYxMzZjMC00LjQtMy42LTgtOC04aC0xNgoJYy00LjQsMC04LDMuNi04LDh2MjA4QzIyNCwzNDguNCwyMjcuNiwzNTIsMjMyLDM1MnogTTUwNCw0MTZIMzJWNzJjMC00LjQtMy42LTgtOC04SDhjLTQuNCwwLTgsMy42LTgsOHYzNjBjMCw4LjgsNy4yLDE2LDE2LDE2aDQ4OAoJYzQuNCwwLDgtMy42LDgtOHYtMTZDNTEyLDQxOS42LDUwOC40LDQxNiw1MDQsNDE2eiIvPgo8cGF0aCBkPSJNNDI0LDM1MmgxNmM0LjQsMCw4LTMuNiw4LThWMTA0YzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOHYyNDBDNDE2LDM0OC40LDQxOS42LDM1Miw0MjQsMzUyeiBNMzI4LDM1MmgxNgoJYzQuNCwwLDgtMy42LDgtOFYyMDBjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djE0NEMzMjAsMzQ4LjQsMzIzLjYsMzUyLDMyOCwzNTJ6IE0xMzYsMzUyaDE2YzQuNCwwLDgtMy42LDgtOHYtODAKCWMwLTQuNC0zLjYtOC04LThoLTE2Yy00LjQsMC04LDMuNi04LDh2ODBDMTI4LDM0OC40LDEzMS42LDM1MiwxMzYsMzUyeiBNMjMyLDM1MmgxNmM0LjQsMCw4LTMuNiw4LThWMTM2YzAtNC40LTMuNi04LTgtOGgtMTYKCWMtNC40LDAtOCwzLjYtOCw4djIwOEMyMjQsMzQ4LjQsMjI3LjYsMzUyLDIzMiwzNTJ6IE01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODgKCWM0LjQsMCw4LTMuNiw4LTh2LTE2QzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQ0OCwxNjkuOFYxMDRjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djY1LjggTTM1MiwyNDMuNVYyMDBjMC00LjQtMy42LTgtOC04aC0xNgoJYy00LjQsMC04LDMuNi04LDh2NDMuNSBNMTYwLDI4N3YtMjNjMC00LjQtMy42LTgtOC04aC0xNmMtNC40LDAtOCwzLjYtOCw4djIzIE0yNTYsMjExLjVWMTM2YzAtNC40LTMuNi04LTgtOGgtMTZjLTQuNCwwLTgsMy42LTgsOAoJdjc1LjUiLz4KPC9zdmc+Cg==); +} +.m-chart-selector .chart-selected .image .type .icon.bar:before, +.m-chart-selector .chart-selected .no-image .type .icon.bar:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTg5LDM0MXYxNmMwLDQuNCwzLjYsOCw4LDhoMjQwYzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMzMzLDg5LDMzNi42LDg5LDM0MXoiLz4KPHBhdGggZD0iTTg5LDI2NnYxNmMwLDQuNCwzLjYsOCw4LDhoMTQ0YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMjU4LDg5LDI2MS42LDg5LDI2NnoiLz4KPHBhdGggZD0iTTg5LDExNnYxNmMwLDQuNCwzLjYsOCw4LDhoODBjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxMDgsODksMTExLjYsODksMTE2eiIvPgo8cGF0aCBkPSJNODksMTkxdjE2YzAsNC40LDMuNiw4LDgsOGgyMDhjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxODMsODksMTg2LjYsODksMTkxeiIvPgo8L3N2Zz4K); +} +.m-chart-selector .chart-selected .image .type .icon.stacked-bar:before, +.m-chart-selector .chart-selected .no-image .type .icon.stacked-bar:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCgkgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6Izk5OTk5OTt9Cjwvc3R5bGU+CjxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNgoJQzUxMiw0MTkuNiw1MDguNCw0MTYsNTA0LDQxNnoiLz4KPHBhdGggZD0iTTg5LDM0MXYxNmMwLDQuNCwzLjYsOCw4LDhoMjQwYzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMzMzLDg5LDMzNi42LDg5LDM0MXoiLz4KPHBhdGggZD0iTTg5LDI2NnYxNmMwLDQuNCwzLjYsOCw4LDhoMTQ0YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04SDk3QzkyLjYsMjU4LDg5LDI2MS42LDg5LDI2NnoiLz4KPHBhdGggZD0iTTg5LDExNnYxNmMwLDQuNCwzLjYsOCw4LDhoODBjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxMDgsODksMTExLjYsODksMTE2eiIvPgo8cGF0aCBkPSJNODksMTkxdjE2YzAsNC40LDMuNiw4LDgsOGgyMDhjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIOTdDOTIuNiwxODMsODksMTg2LjYsODksMTkxeiIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjMyLDM2NWgxMDZjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThIMjMyIi8+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xNjQsMjkwaDc4YzQuNCwwLDgtMy42LDgtOHYtMTZjMC00LjQtMy42LTgtOC04aC03OCIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMTMyLDE0MGg0NmM0LjQsMCw4LTMuNiw4LTh2LTE2YzAtNC40LTMuNi04LTgtOGgtNDYiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTI2MCwyMTVoNDZjNC40LDAsOC0zLjYsOC04di0xNmMwLTQuNC0zLjYtOC04LThoLTQ2Ii8+Cjwvc3ZnPgo=); +} +.m-chart-selector .chart-selected .image .type .icon.pie:before, +.m-chart-selector .chart-selected .no-image .type .icon.pie:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIxNi4xODUgNTcuMjkyIDQzNi40NTYgNDM2Ljc3NiIgd2lkdGg9IjQzNi40NTYiIGhlaWdodD0iNDM2Ljc3NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8ZGVmcz4KICAgIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzQ5NDk0OTt9Cjwvc3R5bGU+CiAgPC9kZWZzPgogIDxwYXRoIGQ9Ik0gNDM3LjgxIDI4OS4yNTggTCAyMjAuOTEgMjg5LjI1OCBMIDIyMC45MSA3Mi4zNDggQyAyMjAuOTEgNjMuNjU4IDIxMy4yMSA1Ni40NDggMjA0LjYxIDU3LjY1OCBDIDk1LjcxIDczLjA1OCAxMi41MSAxNjguMTU4IDE2LjMxIDI4Mi4wNDggQyAyMC4wMSAzOTUuNDQ4IDExNC44MSA0OTAuMTU4IDIyOC4yMSA0OTMuOTQ4IEMgMzQyLjExIDQ5Ny42NTggNDM3LjExIDQxNC40NDggNDUyLjUxIDMwNS42NTggQyA0NTMuNzEgMjk2LjkyOCA0NDYuNTEgMjg5LjI1OCA0MzcuODEgMjg5LjI1OCBaIi8+CiAgPHBhdGggY2xhc3M9ImNscy0xIiBkPSJNIDI0OC40NDggNTcuMjAxIEMgMjQwLjE0OCA1Ni42MDEgMjMzLjA0OCA2My43MDEgMjMzLjA0OCA3Mi4wMDEgTCAyMzMuMDQ4IDI3Ni41MDEgTCA0MzcuNTQ4IDI3Ni41MDEgQyA0NDUuOTQ4IDI3Ni41MDEgNDUyLjk0OCAyNjkuNTAxIDQ1Mi4zNDggMjYxLjEwMSBDIDQ0NC44MDggMTUxLjkwMSAzNTcuNjA4IDY0Ljc1MSAyNDguNDQ4IDU3LjIwMSBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=); +} +.m-chart-selector .chart-selected .image .type .icon.doughnut:before, +.m-chart-selector .chart-selected .no-image .type .icon.doughnut:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2aWV3Qm94PSIzNy42MTUgMzMuMzgzIDQzNi40NTYgNDM2LjU1IiB3aWR0aD0iNDM2LjQ1NiIgaGVpZ2h0PSI0MzYuNTUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnM+CiAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiM0OTQ5NDk7fQo8L3N0eWxlPgogIDwvZGVmcz4KICA8cGF0aCBkPSJNIDQ1OS4yNCAyNjUuMTI0IEwgMzgzLjg5IDI2NS4xMjQgQyAzNzMuNTIxIDM2My42OTIgMjYwLjMzNyA0MTQuMDcxIDE4MC4xNiAzNTUuODA3IEMgOTkuOTgyIDI5Ny41NDMgMTEyLjk0NCAxNzQuMzMzIDIwMy40OTEgMTM0LjAyOSBDIDIxNS44MTUgMTI4LjU0NCAyMjguOTI1IDEyNS4wMjkgMjQyLjM0IDEyMy42MTQgTCAyNDIuMzQgNDguMjE0IEMgMjQyLjM0IDM5LjUxNCAyMzQuNjQgMzIuMzE0IDIyNi4wNCAzMy41MTQgQyAxMTcuMTQgNDguOTE0IDMzLjk0IDE0NC4wMTQgMzcuNzQgMjU3LjkxNCBDIDQxLjQ0IDM3MS4zMTQgMTM2LjI0IDQ2Ni4wMTQgMjQ5LjY0IDQ2OS44MTQgQyAzNjMuNTQgNDczLjUxNCA0NTguNTQgMzkwLjMxNCA0NzMuOTQgMjgxLjUxNCBDIDQ3NS4xNCAyNzIuNzk0IDQ2Ny45NCAyNjUuMTI0IDQ1OS4yNCAyNjUuMTI0IFoiLz4KICA8cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0gNDc0LjAzIDIzNy4zMjQgQyA0NjYuNTMgMTI4LjEyNCAzNzkuMzMgNDAuOTI0IDI3MC4xMyAzMy40MjQgQyAyNjEuODMgMzIuODI0IDI1NC43MyAzOS45MjQgMjU0LjczIDQ4LjIyNCBMIDI1NC43MyAxMjMuOTc0IEMgMzI1Ljg1MyAxMjMuOTUyIDM4My41MiAxODEuNjAyIDM4My41MiAyNTIuNzI0IEwgNDU5LjI3IDI1Mi43MjQgQyA0NjcuNjMgMjUyLjcyNCA0NzQuNjMgMjQ1LjcyNCA0NzQuMDMgMjM3LjMyNCBaIiBzdHlsZT0iZmlsbDogcmdiKDczLCA3MywgNzMpOyIvPgo8L3N2Zz4=); +} +.m-chart-selector .chart-selected .image .type .icon.scatter:before, +.m-chart-selector .chart-selected .no-image .type .icon.scatter:before { + background-image: url(data:image/svg+xml;base64,PHN2ZyBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhbCIgZGF0YS1pY29uPSJjaGFydC1zY2F0dGVyIiBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtY2hhcnQtc2NhdHRlciBmYS13LTE2IiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTUwNCA0MTZIMzJWNzJhOCA4IDAgMCAwLTgtOEg4YTggOCAwIDAgMC04IDh2MzYwYTE2IDE2IDAgMCAwIDE2IDE2aDQ4OGE4IDggMCAwIDAgOC04di0xNmE4IDggMCAwIDAtOC04ek0xNjAgMzEyYTI0IDI0IDAgMSAwLTI0LTI0IDI0IDI0IDAgMCAwIDI0IDI0em0yNTYtMTYwYTI0IDI0IDAgMSAwLTI0LTI0IDI0IDI0IDAgMCAwIDI0IDI0em0tMjI0IDBhMjQgMjQgMCAxIDAtMjQtMjQgMjQgMjQgMCAwIDAgMjQgMjR6bTE5MiAxNjBhMjQgMjQgMCAxIDAtMjQtMjQgMjQgMjQgMCAwIDAgMjQgMjR6bS05Ni02NGEyNCAyNCAwIDEgMC0yNC0yNCAyNCAyNCAwIDAgMCAyNCAyNHoiPjwvcGF0aD48L3N2Zz4=); +} +.m-chart-selector .chart-selected .image .type .icon.bubble:before, +.m-chart-selector .chart-selected .no-image .type .icon.bubble:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGQ9Ik01MDQsNDE2SDMyVjcyYzAtNC40LTMuNi04LTgtOEg4Yy00LjQsMC04LDMuNi04LDh2MzYwYzAsOC44LDcuMiwxNiwxNiwxNmg0ODhjNC40LDAsOC0zLjYsOC04di0xNiBDNTEyLDQxOS42LDUwOC40LDQxNiw1MDQsNDE2eiBNMTYwLDMxMmMxMy4zLDAsMjQtMTAuNywyNC0yNHMtMTAuNy0yNC0yNC0yNHMtMjQsMTAuNy0yNCwyNFMxNDYuNywzMTIsMTYwLDMxMnogTTQxNiwxNTIgYzEzLjMsMCwyNC0xMC43LDI0LTI0cy0xMC43LTI0LTI0LTI0cy0yNCwxMC43LTI0LDI0UzQwMi43LDE1Miw0MTYsMTUyeiBNMTkyLDE1MmMxMy4zLDAsMjQtMTAuNywyNC0yNHMtMTAuNy0yNC0yNC0yNCBzLTI0LDEwLjctMjQsMjRTMTc4LjcsMTUyLDE5MiwxNTJ6IE0zODQsMzEyYzEzLjMsMCwyNC0xMC43LDI0LTI0cy0xMC43LTI0LTI0LTI0cy0yNCwxMC43LTI0LDI0UzM3MC43LDMxMiwzODQsMzEyeiBNMjg4LDI0OCBjMTMuMywwLDI0LTEwLjcsMjQtMjRzLTEwLjctMjQtMjQtMjRzLTI0LDEwLjctMjQsMjRTMjc0LjcsMjQ4LDI4OCwyNDh6Ii8+CiAgPGNpcmNsZSBjeD0iMTkyIiBjeT0iMTI4IiByPSIzMy41Ii8+CiAgPGNpcmNsZSBjeD0iMzg0IiBjeT0iMjg4IiByPSIzMy4zIi8+CiAgPGNpcmNsZSBjeD0iMjY4Ljg5NyIgY3k9IjIzNS44MDUiIHI9IjcyLjQ0NSIgc3R5bGU9IiIvPgogIDxjaXJjbGUgY3g9IjMxOS45IiBjeT0iMTE2LjEiIHI9IjQwLjEiLz4KPC9zdmc+); +} +.m-chart-selector .chart-selected .image .type .icon.radar:before, +.m-chart-selector .chart-selected .no-image .type .icon.radar:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6I0FCQUJBQjtzdHJva2Utd2lkdGg6MjA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQoJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjIwO3N0cm9rZS1taXRlcmxpbWl0OjEwO30KPC9zdHlsZT4KICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgxLjA1NTcwNiwgMCwgMCwgMS4wNTU3MDYsIC0xNy4yMTE5MzEsIC0xNC4wMzM4NTkpIiBzdHlsZT0iIj4KICAgIDxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyNTguNSIgeTE9IjIzLjkiIHgyPSIyNTguNSIgeTI9IjQ4OC4xIi8+CiAgICA8bGluZSBjbGFzcz0ic3QwIiB4MT0iNDkwLjYiIHkxPSIyNTYiIHgyPSIyNi40IiB5Mj0iMjU2Ii8+CiAgICA8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIyNTguNSIgY3k9IjI1NiIgcj0iMjMyLjEiLz4KICAgIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjI1OC41IiBjeT0iMjU2IiByPSIxNjIuMiIvPgogICAgPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjU4LjUiIGN5PSIyNTYiIHI9IjgxLjkiLz4KICA8L2c+CiAgPGVsbGlwc2UgY3g9IjE1NS42MDciIGN5PSIyMjEuNzA1IiByeD0iMzQuNTIyIiByeT0iMzQuNTIyIiBzdHlsZT0iIi8+CiAgPGNpcmNsZSBjeD0iMzI1LjE1NCIgY3k9Ijk2LjcwOSIgcj0iMzQuNTIyIiBzdHlsZT0iIi8+CiAgPGNpcmNsZSBjeD0iNDIxLjIyMyIgY3k9IjQzMy4zNzQiIHI9IjM0LjUyMiIgc3R5bGU9IiIvPgogIDxwb2x5Z29uIGNsYXNzPSJzdDEiIHBvaW50cz0iMzI1LjE1NCA5Mi42OTggMTU1LjYwNyAyMjEuNzA1IDQyNS4wMjQgNDMzLjM3NCIgc3R5bGU9IiIvPgo8L3N2Zz4=); +} +.m-chart-selector .chart-selected .image .type .icon.polar:before, +.m-chart-selector .chart-selected .no-image .type .icon.polar:before { + background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiBmb2N1c2FibGU9ImZhbHNlIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6bm9uZTtzdHJva2U6I0FCQUJBQjtzdHJva2Utd2lkdGg6MjA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7fQoJLnN0MXtmaWxsOiMzOTM5Mzk7fQoJLnN0MntmaWxsOiM1RDVENUQ7fQoJLnN0M3tmaWxsOm5vbmU7fQo8L3N0eWxlPgogIDxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyNTUuNzUzIiB5MT0iMTEuMzgzIiB4Mj0iMjU1Ljc1MyIgeTI9IjUwMC43NDUiLz4KICA8bGluZSBjbGFzcz0ic3QwIiB4MT0iNTAwLjQzNCIgeTE9IjI1Ni4wNjQiIHgyPSIxMS4wNzIiIHkyPSIyNTYuMDY0Ii8+CiAgPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjU1Ljc1MyIgY3k9IjI1Ni4wNjQiIHI9IjI0NC42ODEiLz4KICA8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIyNTUuNzUzIiBjeT0iMjU2LjA2NCIgcj0iMTcwLjk5MiIvPgogIDxjaXJjbGUgY2xhc3M9InN0MCIgY3g9IjI1NS43NTMiIGN5PSIyNTYuMDY0IiByPSI4Ni4zMzkiLz4KICA8cGF0aCBjbGFzcz0ic3QxIiBkPSJNIDI1NC40ODggNTAwLjc0NSBMIDI1NC40ODggMjU1LjExNSBMIDQ5OS4wNjQgMjU1LjExNSBMIDQ5OC45NTggMjU1LjUzNyBDIDQ5OC45NTggMzkwLjY4NiAzODkuNDI2IDUwMC4yMTggMjU0LjI3NyA1MDAuMjE4IEwgMjU0LjQ4OCA1MDAuNzQ1IFoiIHN0eWxlPSJmaWxsOiByZ2IoODIsIDgyLCA4Mik7Ii8+CiAgPHBhdGggY2xhc3M9InN0MiIgZD0iTSA3My45MDMgMjU1Ljg1MyBMIDI1NS4wMTUgMjU1Ljg1MyBMIDI1NS4wMTUgNzQuNzQgTCAyNTQuODA1IDc0Ljk1MSBDIDE1NC42NTUgNzQuOTUxIDczLjQ4MSAxNTYuMTI1IDczLjQ4MSAyNTYuMjc1IiBzdHlsZT0iZmlsbDogcmdiKDEyNywgMTI3LCAxMjcpOyIvPgogIDxsaW5lIGNsYXNzPSJzdDMiIHgxPSIxMTQ2LjAyOSIgeTE9IjE5NS45NzQiIHgyPSIxMzE2LjgxMSIgeTI9IjE5NS45NzQiLz4KICA8cGF0aCBkPSJNIDI1NC41OTQgMTU3LjYwMSBMIDI1NC41OTQgMjU1LjUzNyBMIDM1My42ODkgMjU1LjUzNyBMIDM1My4zNzMgMjU2LjA2NCBDIDM1My4zNzMgMjAxLjM1IDMwOS4wOTYgMTU3LjA3NCAyNTQuMzgzIDE1Ny4wNzQiLz4KPC9zdmc+); +} +.m-chart-selector .chart-selected .image .type h5.title, +.m-chart-selector .chart-selected .no-image .type h5.title { + font-weight: normal; + font-size: 23px; + text-transform: none; + margin: 0 15px 0 15px; + text-align: center; +} +.m-chart-selector .chart-selected .image .type h6.subtitle, +.m-chart-selector .chart-selected .no-image .type h6.subtitle { + font-weight: normal; + font-size: 17px; + text-transform: none; + margin: 1rem 15px 0 15px; + margin-bottom: 0; + text-align: center; +} + +/*# sourceMappingURL=index.css.map*/ \ No newline at end of file diff --git a/components/block/index.css.map b/components/block/index.css.map new file mode 100644 index 0000000..38cdef5 --- /dev/null +++ b/components/block/index.css.map @@ -0,0 +1 @@ +{"version":3,"file":"index.css","mappings":";;;AAEA;EACC;AADD;AAIC;EACC;AAFF;AAOE;EACC;AALH;AAWE;EACC;AATH;AAYE;EACC;EACA;EACA;EACA;AAVH;AAeC;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAbF;AAeE;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAbH;AAeG;EAZD;IAaE;IACA;EAZF;AACF;AAcG;EAjBD;IAkBE;IACA;EAXF;AACF;AAaG;;EAEC;EACA;AAXJ;AAcG;EACC;AAZJ;AAcI;;EAEC;EACA;AAZL;AAgBG;EACC;EACA;EACA;EACA;AAdJ;AAiBG;EACC;AAfJ;AAiBI;ECnBH;EDqBI;EACA;AAfL;AAmBG;EACC;EACA;EACA;EACA;AAjBJ;AAoBK;EACC;EACA;EACA;EACA;EACA;EACA;EACA;AAlBN;AAqBK;EACC;AAnBN;AAsBK;EACC;AApBN;AAuBK;EACC;AArBN;AAwBK;EACC;AAtBN;AAyBK;EACC;AAvBN;AA0BK;EACC;AAxBN;AA2BK;EACC;AAzBN;AA4BK;EACC;AA1BN;AA6BK;EACC;AA3BN;AA8BK;EACC;AA5BN;AA+BK;EACC;AA7BN;AAgCK;EACC;AA9BN;AAiCK;EACC;AA/BN;AAqCE;EACC;EACA;AAnCH;AAyCE;EACC;AAvCH;AAyCG;EACC;EACA;EACA;AAvCJ;AA2CE;;EAEC;EACA;EACA;AAzCH;AA2CG;;EACC;AAxCJ;AA2CK;;EACC;EACA;EACA;EACA;EACA;EACA;EACA;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA2CK;;EACC;AAxCN;AA4CI;;EACC;EACA;EACA;EACA;EACA;AAzCL;AA4CI;;EACC;EACA;EACA;EACA;EACA;EACA;AAzCL,C","sources":["webpack://m-chart/./components/block-src/chart/editor.scss","webpack://m-chart/./components/sass/_global-mixins-and-variables.scss"],"sourcesContent":["@use '../../sass/global-mixins-and-variables' as *;\n\n.m-chart-selector {\n\tbackground-color: #fff;\n\t\n\t// In some views this doesn't automatically stretch\n\t.viewbox {\n\t\twidth: 100%;\n\t}\n\t\n\t// Block header styles\n\t.components-placeholder__label {\n\t\t.dashicons-chart-pie {\n\t\t\tmargin-right: 0.5rem;\n\t\t}\n\t}\n\t\n\t// Search controls\n\t.search-box {\n\t\t.components-base-control__field {\n\t\t\tmargin-bottom: 0;\n\t\t}\n\t\t\n\t\t.count {\n\t\t\tfont-family: var(--wp--preset--font-family--system-font);\n\t\t\tline-height: 2rem;\n\t\t\tfont-size: .8rem;\n\t\t\tmargin-top: 0;\n\t\t}\n\t}\n\t\n\t// Results \n\tul.results {\n\t\tmax-height: 17rem;\n\t\toverflow-y: scroll;\n\t\toverflow-x: hidden;\n\t\tposition: relative;\n\t\tdisplay: flex;\n\t\tflex-wrap: wrap;\n\t\tlist-style: none;\n\t\tgap: .5rem;\n\t\tpadding: 0;\n\t\tmargin-bottom: 0;\n\t\t\n\t\tli {\n\t\t\tflex-basis: 100%;\n\t\t\theight: auto;\n\t\t\ttext-align: center;\n\t\t\tz-index: 10;\n\t\t\tbackground-color: white;\n\t\t\tmargin: 0;\n\t\t\tdisplay: flex;\n\t\t\tjustify-content: center;\n\t\t\talign-items: center;\n\t\t\tflex-direction: column;\n\t\t\t\n\t\t\t@media (min-width: 600px) {\n\t\t\t\tflex-basis: calc(50% - 7px);\n\t\t\t\tpadding-left: 2px;\n\t\t\t}\n\t\t\t\n\t\t\t@media (min-width: 780px) {\n\t\t\t\tflex-basis: calc(33.3% - 8px);\n\t\t\t\tpadding-left: 2px;\n\t\t\t}\n\t\t\t\t\t\t\n\t\t\timg,\n\t\t\t.type {\n\t\t\t\tborder: 2px solid transparent;\n\t\t\t\twidth: 100%;\n\t\t\t}\n\n\t\t\t&:hover {\n\t\t\t\tcursor: pointer;\n\n\t\t\t\timg,\n\t\t\t\t.type {\n\t\t\t\t\tborder: 2px solid var(--wp-admin-theme-color);\n\t\t\t\t\tborder-radius: 2px;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\th6.title {\n\t\t\t\tfont-weight: normal;\n\t\t\t\tfont-size: .8rem;\n\t\t\t\ttext-transform: none;\n\t\t\t\tmargin: 1rem 5px 0 5px;\n\t\t\t}\n\n\t\t\t&.image {\n\t\t\t\tposition: relative;\n\n\t\t\t\th6.title {\n\t\t\t\t\t@include text-stroke(3);\n\t\t\t\t\tmargin-top: 0;\n\t\t\t\t\tposition: absolute;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t.type {\n\t\t\t\tposition: relative;\n\t\t\t\tpadding-top: 2rem;\n\t\t\t\tpadding-bottom: 2rem;\n\t\t\t\tbackground: #f1f1f1;\n\t\t\t\t\n\t\t\t\t.icon {\n\t\t\t\t\t&:before {\n\t\t\t\t\t\tcontent: '';\n\t\t\t\t\t\theight: 27px;\n\t\t\t\t\t\tdisplay: block;\n\t\t\t\t\t\tbackground-image: url('../../images/types/pie.svg');\n\t\t\t\t\t\tbackground-repeat: no-repeat;\n\t\t\t\t\t\tbackground-size: contain;\n\t\t\t\t\t\tbackground-position: center;\n\t\t\t\t\t}\n\n\t\t\t\t\t&.line:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/line.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.spline:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/spline.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.area:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/area.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.column:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/column.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.stacked-column:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/stacked-column.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.bar:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/bar.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.stacked-bar:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/stacked-bar.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.pie:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/pie.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.doughnut:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/doughnut.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.scatter:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/scatter.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.bubble:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/bubble.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.radar:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/radar.svg');\n\t\t\t\t\t}\n\n\t\t\t\t\t&.polar:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/polar.svg');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tli.loading-more {\n\t\t\tflex-basis: 100%;\n\t\t\tpadding: 1rem 0;\n\t\t}\n\t}\n\n\t// Selected\n\t.chart-selected {\n\t\t.image {\n\t\t\toverflow: hidden;\n\n\t\t\timg.preview {\n\t\t\t\tdisplay: block;\n\t\t\t\tmax-width: 100%;\n\t\t\t\theight: auto;\n\t\t\t}\n\t\t}\n\t\t\n\t\t.image,\n\t\t.no-image {\n\t\t\tbackground: #f1f1f1;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\n\t\t\t.type {\n\t\t\t\tflex: 1;\n\t\t\t\t\n\t\t\t\t.icon {\n\t\t\t\t\t&:before {\n\t\t\t\t\t\tcontent: '';\n\t\t\t\t\t\theight: 47px;\n\t\t\t\t\t\tdisplay: block;\n\t\t\t\t\t\tbackground-image: url('../../images/types/pie.svg');\n\t\t\t\t\t\tbackground-repeat: no-repeat;\n\t\t\t\t\t\tbackground-size: contain;\n\t\t\t\t\t\tbackground-position: center;\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.line:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/line.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.spline:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/spline.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.area:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/area.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.column:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/column.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.stacked-column:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/stacked-column.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.bar:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/bar.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.stacked-bar:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/stacked-bar.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.pie:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/pie.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.doughnut:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/doughnut.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.scatter:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/scatter.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.bubble:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/bubble.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.radar:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/radar.svg');\n\t\t\t\t\t}\n\t\n\t\t\t\t\t&.polar:before {\n\t\t\t\t\t\tbackground-image: url('../../images/types/polar.svg');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\th5.title {\n\t\t\t\t\tfont-weight: normal;\n\t\t\t\t\tfont-size: 23px;\n\t\t\t\t\ttext-transform: none;\n\t\t\t\t\tmargin: 0 15px 0 15px;\n\t\t\t\t\ttext-align: center;\n\t\t\t\t}\n\t\n\t\t\t\th6.subtitle {\n\t\t\t\t\tfont-weight: normal;\n\t\t\t\t\tfont-size: 17px;\n\t\t\t\t\ttext-transform: none;\n\t\t\t\t\tmargin: 1rem 15px 0 15px;\n\t\t\t\t\tmargin-bottom: 0;\n\t\t\t\t\ttext-align: center;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}","// Global\n\n@use 'sass:list';\n@use 'sass:math';\n\n@mixin clearfix {\n\t&::after {\n\t\tclear: both;\n\t\tcontent: '';\n\t\tdisplay: table;\n\t}\n}\n\n$rem-base: 16;\n\n@function rem-calc( $values, $base-value: $rem-base ) {\n\t$rem-values: ();\n\n\t@each $value in $values {\n\t\t@if $value == 0 {\n\t\t\t$rem-values: list.append( $rem-values, 0 );\n\t\t} @else {\n\t\t\t$rem-values: list.append( $rem-values, math.div( $value, $base-value ) * 1rem );\n\t\t}\n\t}\n\n\t@if list.length( $rem-values ) == 1 {\n\t\t@return list.nth( $rem-values, 1 );\n\t}\n\n\t@return $rem-values;\n}\n\n// Yes, text-stroke exists but it's not well supported yet and at least in my experience renders strangely depending on the browser\n// This deals with that issue by rendering a text-shadow that does what we actually want\n@function text-stroke( $size: 2, $color: #fff, $correction: 0 ) {\n $size-rounded: math.round( $size );\n $size-rest: $size-rounded - $size;\n \n\t$radius: $size - $correction;\n\t$stroke: ();\n\n\t@for $i from -$size-rounded through $size-rounded {\n\t $i: $i + $size-rest;\n\t \n\t\t@for $k from -$size-rounded through $size-rounded {\n\t\t $k: $k + $size-rest;\n\n\t\t\t$x: $k;\n\t\t\t$y: $i;\n\n\t\t\t@if $k > 0 {\n\t\t\t\t$x: $k - 0.5;\n\t\t\t} @else if $k < 0 {\n\t\t\t\t$x: $k + 0.5;\n\t\t\t}\n\n\t\t\t@if $i > 0 {\n\t\t\t\t$y: $i - 0.5;\n\t\t\t} @else if $i < 0 {\n\t\t\t\t$y: $i + 0.5;\n\t\t\t}\n\n\t\t\t@if ( $x*$x + $y*$y <= $radius*$radius ) {\n\t\t\t\t$stroke: list.append( $stroke, $i*1px $k*1px 0 $color, comma );\n\t\t\t}\n\t\t}\n\t}\n\n\t@return $stroke;\n}\n\n@mixin text-stroke( $size: 2, $color: #fff, $correction: 0 ) {\n\ttext-shadow: text-stroke( $size, $color, $correction );\n}\n\n$wp-admin-black: #000000;\n$wp-admin-red: #c80011;\n$wp-admin-border-color: #dddddd;\n$wp-admin-meta-border-color: #cdd0d4;\n$wp-admin-dark-background-color: #f5f5f5;\n$wp-admin-icon-color: #b8b8b8;\n$wp-admin-error-color: #b00000;\n$wp-admin-description-color: #666666;\n$wp-admin-inactive-text-color: #555555;\n$wp-admin-active-tab-color: #f1f1f1;\n$wp-admin-inactive-icon-color: #82878c;\n\n$wp-admin-dark-mode-border-color: #24282d;\n$wp-admin-dark-mode-meta-background-color: #33373c;\n$wp-admin-dark-mode-meta-border-color: #1a1f24;\n$wp-admin-dark-mode-field-background-color: #53616e;\n$wp-admin-dark-mode-tab-text-color: #bdc8d3;\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/components/block/index.js b/components/block/index.js index 8623289..7e8babb 100644 --- a/components/block/index.js +++ b/components/block/index.js @@ -1 +1,1224 @@ -!function(){var e={705:function(e,t,r){var n=r(639).Symbol;e.exports=n},239:function(e,t,r){var n=r(705),a=r(607),c=r(333),o=n?n.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":o&&o in Object(e)?a(e):c(e)}},561:function(e,t,r){var n=r(990),a=/^\s+/;e.exports=function(e){return e?e.slice(0,n(e)+1).replace(a,""):e}},957:function(e,t,r){var n="object"==typeof r.g&&r.g&&r.g.Object===Object&&r.g;e.exports=n},607:function(e,t,r){var n=r(705),a=Object.prototype,c=a.hasOwnProperty,o=a.toString,i=n?n.toStringTag:void 0;e.exports=function(e){var t=c.call(e,i),r=e[i];try{e[i]=void 0;var n=!0}catch(e){}var a=o.call(e);return n&&(t?e[i]=r:delete e[i]),a}},333:function(e){var t=Object.prototype.toString;e.exports=function(e){return t.call(e)}},639:function(e,t,r){var n=r(957),a="object"==typeof self&&self&&self.Object===Object&&self,c=n||a||Function("return this")();e.exports=c},990:function(e){var t=/\s/;e.exports=function(e){for(var r=e.length;r--&&t.test(e.charAt(r)););return r}},279:function(e,t,r){var n=r(218),a=r(771),c=r(841),o=Math.max,i=Math.min;e.exports=function(e,t,r){var l,s,u,m,p,h,d=0,f=!1,v=!1,b=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function E(t){var r=l,n=s;return l=s=void 0,d=t,m=e.apply(n,r)}function g(e){var r=e-h;return void 0===h||r>=t||r<0||v&&e-d>=u}function w(){var e=a();if(g(e))return y(e);p=setTimeout(w,function(e){var r=t-(e-h);return v?i(r,u-(e-d)):r}(e))}function y(e){return p=void 0,b&&l?E(e):(l=s=void 0,m)}function N(){var e=a(),r=g(e);if(l=arguments,s=this,h=e,r){if(void 0===p)return function(e){return d=e,p=setTimeout(w,t),f?E(e):m}(h);if(v)return clearTimeout(p),p=setTimeout(w,t),E(h)}return void 0===p&&(p=setTimeout(w,t)),m}return t=c(t)||0,n(r)&&(f=!!r.leading,u=(v="maxWait"in r)?o(c(r.maxWait)||0,t):u,b="trailing"in r?!!r.trailing:b),N.cancel=function(){void 0!==p&&clearTimeout(p),d=0,l=h=s=p=void 0},N.flush=function(){return void 0===p?m:y(a())},N}},218:function(e){e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},5:function(e){e.exports=function(e){return null!=e&&"object"==typeof e}},448:function(e,t,r){var n=r(239),a=r(5);e.exports=function(e){return"symbol"==typeof e||a(e)&&"[object Symbol]"==n(e)}},771:function(e,t,r){var n=r(639);e.exports=function(){return n.Date.now()}},841:function(e,t,r){var n=r(561),a=r(218),c=r(448),o=/^[-+]0x[0-9a-f]+$/i,i=/^0b[01]+$/i,l=/^0o[0-7]+$/i,s=parseInt;e.exports=function(e){if("number"==typeof e)return e;if(c(e))return NaN;if(a(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=a(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=n(e);var r=i.test(e);return r||l.test(e)?s(e.slice(2),r?2:8):o.test(e)?NaN:+e}}},t={};function r(n){var a=t[n];if(void 0!==a)return a.exports;var c=t[n]={exports:{}};return e[n](c,c.exports,r),c.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){"use strict";var e=window.wp.i18n,t=window.wp.blocks,n=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"m-chart/chart","title":"M Chart","category":"layout","icon":"chart-pie","description":"Embed a chart made with M Chart into your posts and pages","textdomain":"m-chart","attributes":{"chartId":{"type":"string","default":""}},"supports":{"html":false},"editorScript":"m-chart-editor","editorStyle":"m-chart-editor-style","render":"file:./render.php"}'),a=window.wp.element,c=window.wp.components,o=window.wp.blockEditor,i=window.wp.apiFetch,l=r.n(i),s=r(279),u=r.n(s),m=window.React;(0,t.registerBlockType)(n,{edit:function({attributes:t,setAttributes:r}){const[n,i]=(0,a.useState)([]),[s,p]=(0,a.useState)(""),[h,d]=(0,a.useState)(0),[f,v]=(0,a.useState)(0),[b,E]=(0,a.useState)(!1),[g,w]=(0,a.useState)([]),[y,N]=(0,a.useState)(null),[x,_]=(0,a.useState)(null),[S,j]=(0,a.useState)(!0),[k,T]=(0,a.useState)(!1),C=`${x}/wp-admin/post-new.php?post_type=m-chart`,O=`${x}/wp-admin/post.php?post=${t.chartId}&action=edit`,$=(0,o.useBlockProps)({className:"m-chart-block-chart-selector"}),F=`?random=${Math.round(1e6*Math.random())}`;(0,a.useEffect)((()=>{A(),P(s)}),[]);const I=n.map((e=>S?(0,a.createElement)("li",{className:e.src?"item img":"item no-image",key:e.id,onClick:()=>B(e.id),title:e.title},e.src?(0,a.createElement)(a.Fragment,null,(0,a.createElement)("h6",{className:"title"},e.title),(0,a.createElement)("img",{src:e.src+F,alt:e.title})):(0,a.createElement)("div",{className:"type"},(0,a.createElement)("span",{className:"icon "+e.type},(0,a.createElement)("h6",{className:"title"},e.title)))):(0,a.createElement)("li",{className:"no-image",key:e.id,onClick:()=>B(e.id),title:e.title},(0,a.createElement)("div",{className:"type"},(0,a.createElement)("span",{className:"icon "+e.type}),(0,a.createElement)("h6",{className:"title"},e.title))))),M=g.filter((e=>e.id===t.chartId))[0],B=e=>{r({chartId:e}),N(e)},A=()=>{l()({path:"/m-chart/v1/options"}).then((e=>{j(e.image_support_active),_(e.siteurl),d(e.maxAvailable)}))},P=e=>{T(!1),l()({path:`/m-chart/v1/graphs/${e}`}).then((e=>{let t=[];v(e[0]),e[1].map((e=>t.push({id:e.id,title:e.title||"-",subtitle:e.subtitle,width:e.width,height:e.height,type:e.type||"",src:e.url||""}))),w(t),i(t),E(!0)})).catch((e=>{"rest_no_route"===e.code&&T(!0)}))},R=(0,m.useCallback)(u()(P,500),[]);return(0,a.createElement)("div",{...$},(0,a.createElement)(o.BlockControls,null,(0,a.createElement)(c.ToolbarGroup,{className:"m-chart-block"},(0,a.createElement)(c.ToolbarButton,{onClick:()=>window.location.href=C,icon:"external"},(0,e.__)("New Chart","m-chart")),t.chartId&&(0,a.createElement)(a.Fragment,null,(0,a.createElement)(c.ToolbarButton,{onClick:()=>window.location.href=O,icon:"external"},(0,e.__)("Edit Chart","m-chart")),(0,a.createElement)(c.ToolbarButton,{onClick:()=>B("")},(0,e.__)("Replace","m-chart"))))),(0,a.createElement)("div",{className:"wp-block m-chart-selector"},(0,a.createElement)("div",{className:"components-placeholder block-editor-m-chart-placeholder is-large"},(0,a.createElement)("div",{className:"components-placeholder__label"},(0,a.createElement)("span",{className:"dashicons dashicons-chart-pie"}),"Chart"),(0,a.createElement)("div",{className:"viewbox"},k?(0,a.createElement)("p",null,(0,e.__)("There was a problem fetching charts","m-chart")):(0,a.createElement)(a.Fragment,null,b?t.chartId?(0,a.createElement)("div",{className:"chart-selected"},S?(0,a.createElement)("div",{className:"image-support"},""===M?.src?(0,a.createElement)("div",{className:"type"},(0,a.createElement)("span",{className:"icon "+M.type}),(0,a.createElement)("h4",{className:"title"},M.title)):(0,a.createElement)("img",{className:"preview",src:M?.src+F})):(0,a.createElement)("div",{className:"no-image-support",style:{aspectRatio:M.width/M.height}},(0,a.createElement)("span",{className:"type "+M.type}),(0,a.createElement)("h4",{className:"title"},M?.title),(0,a.createElement)("p",null,M?.subtitle))):0===h?(0,a.createElement)("p",{className:"center"},(0,e.__)("No Charts found","m-chart"),(0,a.createElement)("a",{href:C},(0,e.__)("Create a new chart","m-chart"))):(0,a.createElement)("div",{className:"no-chart-selected"},(0,a.createElement)("div",{className:"search-box"},(0,a.createElement)(c.TextControl,{value:s,placeholder:(0,e.__)("Search by title","m-chart"),onChange:e=>(e=>{e=e.replace(/[^a-zA-Z0-9\- , ]/gi,""),p(e),R(e)})(e),autoFocus:!0}),(0,a.createElement)("p",{className:"count"},I.length," ",(0,e.__)(" of ","m-chart"),f)),0===I.length&&s.length>1?(0,a.createElement)("p",null,(0,e.__)("No Charts found using this search string","m-chart")):(0,a.createElement)("ul",{className:S?"results image-support":"results no-image-support"},I)):(0,a.createElement)("p",{className:"center"},(0,a.createElement)(c.Spinner,null)))))))},save:()=>null})}()}(); \ No newline at end of file +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "./components/block-src/chart/edit.js" +/*!********************************************!*\ + !*** ./components/block-src/chart/edit.js ***! + \********************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "default": () => (/* binding */ edit) +/* harmony export */ }); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components"); +/* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _wordpress_blocks__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/blocks */ "@wordpress/blocks"); +/* harmony import */ var _wordpress_blocks__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_blocks__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); +/* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _wordpress_block_editor__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @wordpress/block-editor */ "@wordpress/block-editor"); +/* harmony import */ var _wordpress_block_editor__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_4__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__); +/* harmony import */ var _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @wordpress/api-fetch */ "@wordpress/api-fetch"); +/* harmony import */ var _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6__); +/* harmony import */ var lodash_debounce__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! lodash/debounce */ "./node_modules/lodash/debounce.js"); +/* harmony import */ var lodash_debounce__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(lodash_debounce__WEBPACK_IMPORTED_MODULE_7__); +/* harmony import */ var _editor_scss__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./editor.scss */ "./components/block-src/chart/editor.scss"); + + + + + + + + + +function edit({ + attributes, + setAttributes +}) { + // State + const [results, setResults] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)([]); + const [search, setSearch] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(''); + const [postsAvailable, setPostsAvailable] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(false); + const [available, setAvailable] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(0); + const [loaded, setLoaded] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(false); + const [selectedChart, setSelectedChart] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(null); + const [siteUrl, setSiteUrl] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(null); + const [imageSupport, setImageSupport] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(false); + const [loadProblem, setLoadProblem] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(false); + const [page, setPage] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(1); + const [loadingMore, setLoadingMore] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(false); + const resultsRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useRef)(null); + + // URLs + const newUrl = `${siteUrl}/wp-admin/post-new.php?post_type=m-chart`; + const editUrl = `${siteUrl}/wp-admin/post.php?post=${attributes.chartId}&action=edit`; + const optionsUrl = `/m-chart/v1/options`; + + // Blockprops + const blockProps = (0,_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_4__.useBlockProps)({ + className: 'm-chart-block-chart-selector' + }); + + // Set a cache URL parameter based on the current moment in time to prevent cached images from messing up the UI + const cacheBuster = `?cache=${performance.now()}`; + + // On load we fetch some option settings and run getCharts so we have some intiial reasults loaded into the UI + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useEffect)(() => { + fetchOptions(); + getCharts(search); + }, []); + + // Fetch the selected chart individually whenever chartId changes + // Using attributes.chartId as a dependency handles the case where Gutenberg provides the saved attribute value after the initial render + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useEffect)(() => { + setSelectedChart(null); + if (attributes.chartId) { + getChart(parseInt(attributes.chartId, 10)); + } + }, [attributes.chartId]); + + // Load more charts when scrolling near the bottom of the results list + (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useEffect)(() => { + const el = resultsRef.current; + if (!el) return; + const handleScroll = () => { + if (loadingMore) return; + if (results.length >= available) return; + + // If we're close enough to the bottom of the list load the next page + if (el.scrollTop + el.clientHeight >= el.scrollHeight - 100) { + const nextPage = page + 1; + setPage(nextPage); + getCharts(search, nextPage); + } + }; + el.addEventListener('scroll', handleScroll); + return () => el.removeEventListener('scroll', handleScroll); + }, [results, available, loadingMore, page, search]); + + // Build list of charts out of the results object + const resultsList = results.map(x => { + if (!imageSupport || !x.src) { + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("li", { + "aria-label": "Select Chart: " + x.title, + role: "button", + className: "item no-image", + key: x.id, + onClick: () => handleClick(x.id), + title: x.title + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "type" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("span", { + className: 'icon ' + x.type + }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("h6", { + className: "title" + }, x.title))); + } else { + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("li", { + "aria-label": "Select Chart: " + x.title, + role: "button", + className: "item image", + key: x.id, + onClick: () => handleClick(x.id), + title: x.title + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("h6", { + className: "title" + }, x.title), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("img", { + src: x.src + cacheBuster, + alt: x.title + })); + } + }); + + // Handle clicks to a chart in the results list + const handleClick = id => { + setAttributes({ + chartId: id + }); + setSelectedChart(null); + }; + + // Handle user typing into the search field + const handleSearch = value => { + console.log('search', value); + doSearch(value); + }; + + // Actually actually carry out the debounced search + const doSearch = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useCallback)(lodash_debounce__WEBPACK_IMPORTED_MODULE_7___default()(value => { + console.log('debounce', value); + setSearch(value); + setPage(1); + getCharts(value); + }, 500), []); + + // Get option settings + const fetchOptions = () => { + _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default()({ + path: optionsUrl + }).then(result => { + setImageSupport(result.image_support); + setSiteUrl(result.siteurl); + setPostsAvailable(result.posts_avilable); + }); + }; + + // Get a single chart + const getChart = id => { + _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default()({ + path: `/m-chart/v1/chart/${id}` + }).then(result => { + setSelectedChart({ + id: result.id, + title: result.title || '-', + subtitle: result.subtitle, + width: result.width, + height: result.height, + type: result.type || '', + src: result.url || '' + }); + }).catch(() => {}); + }; + const getCharts = (value, getPage = 1) => { + setLoadProblem(false); + + // If we're getting a subsequent page we're adding to the existing results + if (getPage > 1) { + setLoadingMore(true); + } + + // Build the parameters + const params = new URLSearchParams(); + if (value) { + params.set('s', value); + } + if (getPage > 1) { + params.set('page', getPage); + } + const query = params.toString(); + + // Run the query and grab the results + _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default()({ + path: `/m-chart/v1/charts${query ? '?' + query : ''}` + }).then(result => { + const newCharts = result.posts.map(x => ({ + id: x.id, + title: x.title || '-', + subtitle: x.subtitle, + width: x.width, + height: x.height, + type: x.type || '', + src: x.url || '' + })); + + // Update the found value to match the current search + setAvailable(result.found_posts); + + // Either append or replace the existing results + if (getPage === 1) { + setResults(newCharts); + } else { + setResults(prev => [...prev, ...newCharts]); + } + setLoaded(true); + setLoadingMore(false); + }).catch(error => { + // If there's an error we'll note it + if (error.code === 'rest_no_route') { + setLoadProblem(true); + } + setLoadingMore(false); + }); + }; + return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + ...blockProps + }, !!attributes.chartId && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_4__.InspectorControls, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.PanelBody, { + title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Display settings', 'm-chart') + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.SelectControl, { + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Show', 'm-chart'), + value: attributes.show, + options: [{ + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Chart', 'm-chart'), + value: 'chart' + }, { + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Image', 'm-chart'), + value: 'image' + }, { + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Table', 'm-chart'), + value: 'table' + }], + onChange: value => setAttributes({ + show: value + }) + }))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_4__.BlockControls, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.ToolbarGroup, { + className: "m-chart-block" + }, !attributes.chartId && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.ToolbarButton, { + onClick: () => window.open(newUrl, "_blank"), + icon: "external" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('New chart', 'm-chart')), !!attributes.chartId && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.ToolbarButton, { + onClick: () => window.open(editUrl, "_blank"), + icon: "external" + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Edit chart', 'm-chart')), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.ToolbarButton, { + onClick: () => handleClick(0) + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Replace', 'm-chart'))))), !!attributes.chartId ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "wp-block m-chart-selector" + }, !selectedChart ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "center" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Spinner, null)) : (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "chart-selected" + }, !imageSupport || !selectedChart.src ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "no-image", + style: { + aspectRatio: selectedChart.width / selectedChart.height + } + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "type" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("span", { + className: 'icon ' + selectedChart.type + }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("h5", { + className: "title" + }, selectedChart.title), selectedChart.subtitle && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("h6", { + className: "subtitle" + }, selectedChart.subtitle))) : (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "image" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("img", { + className: "preview", + src: selectedChart.src + cacheBuster + })))) : (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "wp-block m-chart-selector" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Placeholder, { + className: "block-editor-m-chart-placeholder", + icon: (0,_wordpress_blocks__WEBPACK_IMPORTED_MODULE_2__.getBlockType)('m-chart/chart').icon.src, + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Chart', 'm-chart') + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "viewbox" + }, loadProblem ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('There was a problem loading charts', 'm-chart')) : (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, !loaded ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "center" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Spinner, null)) : postsAvailable === false ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('No charts found', 'm-chart'), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("br", null)), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.ExternalLink, { + href: newUrl + }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Create a new chart', 'm-chart')))) : (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "no-chart-selected" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { + className: "search-box" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.SearchControl, { + value: search, + placeholder: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Search by title', 'm-chart'), + onChange: value => handleSearch(value), + autoFocus: true + }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", { + className: "count" + }, available, " ", 1 === available ? (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('chart found', 'm-chart') : (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('charts found', 'm-chart'))), resultsList.length === 0 && search.length > 1 ? (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("p", null, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('No charts found', 'm-chart')) : (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("ul", { + ref: resultsRef, + className: imageSupport ? 'results image-support' : 'results no-image-support' + }, resultsList, loadingMore && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("li", { + className: "loading-more" + }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Spinner, null))))))))); +} +; + +/***/ }, + +/***/ "./components/block-src/chart/index.js" +/*!*********************************************!*\ + !*** ./components/block-src/chart/index.js ***! + \*********************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); +/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _wordpress_blocks__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/blocks */ "@wordpress/blocks"); +/* harmony import */ var _wordpress_blocks__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_blocks__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components"); +/* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _block_json__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./block.json */ "./components/block-src/chart/block.json"); +/* harmony import */ var _edit__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./edit */ "./components/block-src/chart/edit.js"); + + + + + + +const blockIcon = (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.SVG, { + viewBox: "0 0 24 24", + xmlns: "http://www.w3.org/2000/svg", + width: "24", + height: "24", + "aria-hidden": "true", + focusable: "false" +}, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.Path, { + d: "M 18.7 3 L 5.3 3 C 4 3 3 4 3 5.3 L 3 18.7 C 3 20 4 21 5.3 21 L 18.7 21 C 20 21 21 20 21 18.7 L 21 5.3 C 21 4 20 3 18.7 3 Z M 19.5 18.7 C 19.5 19.1 19.1 19.5 18.7 19.5 L 5.3 19.5 C 4.9 19.5 4.5 19.1 4.5 18.7 L 4.5 5.3 C 4.5 4.9 4.9 4.5 5.3 4.5 L 18.7 4.5 C 19.1 4.5 19.5 4.9 19.5 5.3 L 19.5 18.7 Z" +}), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.Path, { + d: "M 12.312 7 L 12.312 11.688 L 17 11.688 C 17 9.1 14.9 7 12.312 7 Z M 11.375 12.157 L 11.375 7.635 C 8.932 7.797 7 9.828 7 12.312 C 7 14.9 9.1 17 11.687 17 C 14.172 17 16.203 15.068 16.365 12.625 L 11.375 12.625 L 11.375 12.157 Z", + style: { + strokeWidth: 1 + } +})); +(0,_wordpress_blocks__WEBPACK_IMPORTED_MODULE_2__.registerBlockType)(_block_json__WEBPACK_IMPORTED_MODULE_4__, { + edit: _edit__WEBPACK_IMPORTED_MODULE_5__["default"], + save: () => null, + icon: blockIcon +}); + +/***/ }, + +/***/ "./node_modules/lodash/_Symbol.js" +/*!****************************************!*\ + !*** ./node_modules/lodash/_Symbol.js ***! + \****************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var root = __webpack_require__(/*! ./_root */ "./node_modules/lodash/_root.js"); + +/** Built-in value references. */ +var Symbol = root.Symbol; + +module.exports = Symbol; + + +/***/ }, + +/***/ "./node_modules/lodash/_baseGetTag.js" +/*!********************************************!*\ + !*** ./node_modules/lodash/_baseGetTag.js ***! + \********************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var Symbol = __webpack_require__(/*! ./_Symbol */ "./node_modules/lodash/_Symbol.js"), + getRawTag = __webpack_require__(/*! ./_getRawTag */ "./node_modules/lodash/_getRawTag.js"), + objectToString = __webpack_require__(/*! ./_objectToString */ "./node_modules/lodash/_objectToString.js"); + +/** `Object#toString` result references. */ +var nullTag = '[object Null]', + undefinedTag = '[object Undefined]'; + +/** Built-in value references. */ +var symToStringTag = Symbol ? Symbol.toStringTag : undefined; + +/** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ +function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return (symToStringTag && symToStringTag in Object(value)) + ? getRawTag(value) + : objectToString(value); +} + +module.exports = baseGetTag; + + +/***/ }, + +/***/ "./node_modules/lodash/_baseTrim.js" +/*!******************************************!*\ + !*** ./node_modules/lodash/_baseTrim.js ***! + \******************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var trimmedEndIndex = __webpack_require__(/*! ./_trimmedEndIndex */ "./node_modules/lodash/_trimmedEndIndex.js"); + +/** Used to match leading whitespace. */ +var reTrimStart = /^\s+/; + +/** + * The base implementation of `_.trim`. + * + * @private + * @param {string} string The string to trim. + * @returns {string} Returns the trimmed string. + */ +function baseTrim(string) { + return string + ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') + : string; +} + +module.exports = baseTrim; + + +/***/ }, + +/***/ "./node_modules/lodash/_freeGlobal.js" +/*!********************************************!*\ + !*** ./node_modules/lodash/_freeGlobal.js ***! + \********************************************/ +(module) { + +/** Detect free variable `global` from Node.js. */ +var freeGlobal = typeof globalThis == 'object' && globalThis && globalThis.Object === Object && globalThis; + +module.exports = freeGlobal; + + +/***/ }, + +/***/ "./node_modules/lodash/_getRawTag.js" +/*!*******************************************!*\ + !*** ./node_modules/lodash/_getRawTag.js ***! + \*******************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var Symbol = __webpack_require__(/*! ./_Symbol */ "./node_modules/lodash/_Symbol.js"); + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** Used to check objects for own properties. */ +var hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var nativeObjectToString = objectProto.toString; + +/** Built-in value references. */ +var symToStringTag = Symbol ? Symbol.toStringTag : undefined; + +/** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ +function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + + try { + value[symToStringTag] = undefined; + var unmasked = true; + } catch (e) {} + + var result = nativeObjectToString.call(value); + if (unmasked) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } + } + return result; +} + +module.exports = getRawTag; + + +/***/ }, + +/***/ "./node_modules/lodash/_objectToString.js" +/*!************************************************!*\ + !*** ./node_modules/lodash/_objectToString.js ***! + \************************************************/ +(module) { + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var nativeObjectToString = objectProto.toString; + +/** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ +function objectToString(value) { + return nativeObjectToString.call(value); +} + +module.exports = objectToString; + + +/***/ }, + +/***/ "./node_modules/lodash/_root.js" +/*!**************************************!*\ + !*** ./node_modules/lodash/_root.js ***! + \**************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var freeGlobal = __webpack_require__(/*! ./_freeGlobal */ "./node_modules/lodash/_freeGlobal.js"); + +/** Detect free variable `self`. */ +var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + +/** Used as a reference to the global object. */ +var root = freeGlobal || freeSelf || Function('return this')(); + +module.exports = root; + + +/***/ }, + +/***/ "./node_modules/lodash/_trimmedEndIndex.js" +/*!*************************************************!*\ + !*** ./node_modules/lodash/_trimmedEndIndex.js ***! + \*************************************************/ +(module) { + +/** Used to match a single whitespace character. */ +var reWhitespace = /\s/; + +/** + * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace + * character of `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the index of the last non-whitespace character. + */ +function trimmedEndIndex(string) { + var index = string.length; + + while (index-- && reWhitespace.test(string.charAt(index))) {} + return index; +} + +module.exports = trimmedEndIndex; + + +/***/ }, + +/***/ "./node_modules/lodash/debounce.js" +/*!*****************************************!*\ + !*** ./node_modules/lodash/debounce.js ***! + \*****************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var isObject = __webpack_require__(/*! ./isObject */ "./node_modules/lodash/isObject.js"), + now = __webpack_require__(/*! ./now */ "./node_modules/lodash/now.js"), + toNumber = __webpack_require__(/*! ./toNumber */ "./node_modules/lodash/toNumber.js"); + +/** Error message constants. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/* Built-in method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max, + nativeMin = Math.min; + +/** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ +function debounce(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = toNumber(wait) || 0; + if (isObject(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = setTimeout(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + timeWaiting = wait - timeSinceLastCall; + + return maxing + ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) + : timeWaiting; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return (lastCallTime === undefined || (timeSinceLastCall >= wait) || + (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); + } + + function timerExpired() { + var time = now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now()); + } + + function debounced() { + var time = now(), + isInvoking = shouldInvoke(time); + + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + clearTimeout(timerId); + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; +} + +module.exports = debounce; + + +/***/ }, + +/***/ "./node_modules/lodash/isObject.js" +/*!*****************************************!*\ + !*** ./node_modules/lodash/isObject.js ***! + \*****************************************/ +(module) { + +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ +function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); +} + +module.exports = isObject; + + +/***/ }, + +/***/ "./node_modules/lodash/isObjectLike.js" +/*!*********************************************!*\ + !*** ./node_modules/lodash/isObjectLike.js ***! + \*********************************************/ +(module) { + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return value != null && typeof value == 'object'; +} + +module.exports = isObjectLike; + + +/***/ }, + +/***/ "./node_modules/lodash/isSymbol.js" +/*!*****************************************!*\ + !*** ./node_modules/lodash/isSymbol.js ***! + \*****************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var baseGetTag = __webpack_require__(/*! ./_baseGetTag */ "./node_modules/lodash/_baseGetTag.js"), + isObjectLike = __webpack_require__(/*! ./isObjectLike */ "./node_modules/lodash/isObjectLike.js"); + +/** `Object#toString` result references. */ +var symbolTag = '[object Symbol]'; + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ +function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && baseGetTag(value) == symbolTag); +} + +module.exports = isSymbol; + + +/***/ }, + +/***/ "./node_modules/lodash/now.js" +/*!************************************!*\ + !*** ./node_modules/lodash/now.js ***! + \************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var root = __webpack_require__(/*! ./_root */ "./node_modules/lodash/_root.js"); + +/** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ +var now = function() { + return root.Date.now(); +}; + +module.exports = now; + + +/***/ }, + +/***/ "./node_modules/lodash/toNumber.js" +/*!*****************************************!*\ + !*** ./node_modules/lodash/toNumber.js ***! + \*****************************************/ +(module, __unused_webpack_exports, __webpack_require__) { + +var baseTrim = __webpack_require__(/*! ./_baseTrim */ "./node_modules/lodash/_baseTrim.js"), + isObject = __webpack_require__(/*! ./isObject */ "./node_modules/lodash/isObject.js"), + isSymbol = __webpack_require__(/*! ./isSymbol */ "./node_modules/lodash/isSymbol.js"); + +/** Used as references for various `Number` constants. */ +var NAN = 0 / 0; + +/** Used to detect bad signed hexadecimal string values. */ +var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + +/** Used to detect binary string values. */ +var reIsBinary = /^0b[01]+$/i; + +/** Used to detect octal string values. */ +var reIsOctal = /^0o[0-7]+$/i; + +/** Built-in method references without a dependency on `root`. */ +var freeParseInt = parseInt; + +/** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ +function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = baseTrim(value); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); +} + +module.exports = toNumber; + + +/***/ }, + +/***/ "./components/block-src/chart/editor.scss" +/*!************************************************!*\ + !*** ./components/block-src/chart/editor.scss ***! + \************************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +// extracted by mini-css-extract-plugin + + +/***/ }, + +/***/ "react" +/*!************************!*\ + !*** external "React" ***! + \************************/ +(module) { + +"use strict"; +module.exports = window["React"]; + +/***/ }, + +/***/ "@wordpress/api-fetch" +/*!**********************************!*\ + !*** external ["wp","apiFetch"] ***! + \**********************************/ +(module) { + +"use strict"; +module.exports = window["wp"]["apiFetch"]; + +/***/ }, + +/***/ "@wordpress/block-editor" +/*!*************************************!*\ + !*** external ["wp","blockEditor"] ***! + \*************************************/ +(module) { + +"use strict"; +module.exports = window["wp"]["blockEditor"]; + +/***/ }, + +/***/ "@wordpress/blocks" +/*!********************************!*\ + !*** external ["wp","blocks"] ***! + \********************************/ +(module) { + +"use strict"; +module.exports = window["wp"]["blocks"]; + +/***/ }, + +/***/ "@wordpress/components" +/*!************************************!*\ + !*** external ["wp","components"] ***! + \************************************/ +(module) { + +"use strict"; +module.exports = window["wp"]["components"]; + +/***/ }, + +/***/ "@wordpress/element" +/*!*********************************!*\ + !*** external ["wp","element"] ***! + \*********************************/ +(module) { + +"use strict"; +module.exports = window["wp"]["element"]; + +/***/ }, + +/***/ "@wordpress/i18n" +/*!******************************!*\ + !*** external ["wp","i18n"] ***! + \******************************/ +(module) { + +"use strict"; +module.exports = window["wp"]["i18n"]; + +/***/ }, + +/***/ "./components/block-src/chart/block.json" +/*!***********************************************!*\ + !*** ./components/block-src/chart/block.json ***! + \***********************************************/ +(module) { + +"use strict"; +module.exports = /*#__PURE__*/JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":3,"name":"m-chart/chart","title":"Chart","category":"layout","description":"Embed a chart made with M Chart into your posts and pages","textdomain":"m-chart","attributes":{"chartId":{"type":"integer","default":0},"show":{"type":"string","default":"chart"}},"supports":{"html":false},"editorScript":"m-chart-editor","editorStyle":"m-chart-editor-style","render":"file:./render.php"}'); + +/***/ } + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ if (!(moduleId in __webpack_modules__)) { +/******/ delete __webpack_module_cache__[moduleId]; +/******/ var e = new Error("Cannot find module '" + moduleId + "'"); +/******/ e.code = 'MODULE_NOT_FOUND'; +/******/ throw e; +/******/ } +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry needs to be wrapped in an IIFE because it needs to be in strict mode. +(() => { +"use strict"; +/*!***************************************!*\ + !*** ./components/block-src/index.js ***! + \***************************************/ +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _chart__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./chart */ "./components/block-src/chart/index.js"); + +})(); + +/******/ })() +; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/components/block/index.js.map b/components/block/index.js.map new file mode 100644 index 0000000..0664c93 --- /dev/null +++ b/components/block/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","mappings":"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A,eAAe,mBAAO,CAAC,qDAAY;AACnC,UAAU,mBAAO,CAAC,2CAAO;AACzB,eAAe,mBAAO,CAAC,qDAAY;;AAEnC;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,UAAU;AACrB,WAAW,QAAQ;AACnB,WAAW,QAAQ,WAAW;AAC9B,WAAW,SAAS;AACpB;AACA,WAAW,QAAQ;AACnB;AACA,WAAW,SAAS;AACpB;AACA,aAAa,UAAU;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA,+CAA+C,iBAAiB;AAChE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;;AC9LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,GAAG;AACd,aAAa,SAAS;AACtB;AACA;AACA,gBAAgB;AAChB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;;AC9BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,GAAG;AACd,aAAa,SAAS;AACtB;AACA;AACA,oBAAoB;AACpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;;AC5BA,iBAAiB,mBAAO,CAAC,2DAAe;AACxC,mBAAmB,mBAAO,CAAC,6DAAgB;;AAE3C;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,GAAG;AACd,aAAa,SAAS;AACtB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;;AC5BA,WAAW,mBAAO,CAAC,+CAAS;;AAE5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAa,QAAQ;AACrB;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;;ACtBA,eAAe,mBAAO,CAAC,uDAAa;AACpC,eAAe,mBAAO,CAAC,qDAAY;AACnC,eAAe,mBAAO,CAAC,qDAAY;;AAEnC;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,WAAW,GAAG;AACd,aAAa,QAAQ;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;;;;AC/DA;;;;;;;;;;;;ACAA,iC;;;;;;;;;;;ACAA,0C;;;;;;;;;;;ACAA,6C;;;;;;;;;;;ACAA,wC;;;;;;;;;;;ACAA,4C;;;;;;;;;;;ACAA,yC;;;;;;;;;;;ACAA,sC;;;;;;;;;;;;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;UACA;;;;;WC5BA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA,E;;;;;WCPA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA,E;;;;;WCPA,wF;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D,E","sources":["webpack://m-chart/./components/block-src/chart/edit.js","webpack://m-chart/./components/block-src/chart/index.js","webpack://m-chart/./node_modules/lodash/_Symbol.js","webpack://m-chart/./node_modules/lodash/_baseGetTag.js","webpack://m-chart/./node_modules/lodash/_baseTrim.js","webpack://m-chart/./node_modules/lodash/_freeGlobal.js","webpack://m-chart/./node_modules/lodash/_getRawTag.js","webpack://m-chart/./node_modules/lodash/_objectToString.js","webpack://m-chart/./node_modules/lodash/_root.js","webpack://m-chart/./node_modules/lodash/_trimmedEndIndex.js","webpack://m-chart/./node_modules/lodash/debounce.js","webpack://m-chart/./node_modules/lodash/isObject.js","webpack://m-chart/./node_modules/lodash/isObjectLike.js","webpack://m-chart/./node_modules/lodash/isSymbol.js","webpack://m-chart/./node_modules/lodash/now.js","webpack://m-chart/./node_modules/lodash/toNumber.js","webpack://m-chart/./components/block-src/chart/editor.scss?4ac6","webpack://m-chart/external window \"React\"","webpack://m-chart/external window [\"wp\",\"apiFetch\"]","webpack://m-chart/external window [\"wp\",\"blockEditor\"]","webpack://m-chart/external window [\"wp\",\"blocks\"]","webpack://m-chart/external window [\"wp\",\"components\"]","webpack://m-chart/external window [\"wp\",\"element\"]","webpack://m-chart/external window [\"wp\",\"i18n\"]","webpack://m-chart/webpack/bootstrap","webpack://m-chart/webpack/runtime/compat get default export","webpack://m-chart/webpack/runtime/define property getters","webpack://m-chart/webpack/runtime/hasOwnProperty shorthand","webpack://m-chart/webpack/runtime/make namespace object","webpack://m-chart/./components/block-src/index.js"],"sourcesContent":["import { SelectControl, Spinner, ToolbarGroup, ToolbarButton, Placeholder, ExternalLink, PanelBody, SearchControl } from '@wordpress/components';\nimport { getBlockType } from '@wordpress/blocks';\nimport { useState, useEffect, useRef, useMemo, useCallback } from '@wordpress/element';\nimport { useBlockProps, BlockControls, InspectorControls } from '@wordpress/block-editor';\nimport { __ } from '@wordpress/i18n';\nimport apiFetch from '@wordpress/api-fetch';\nimport debounce from 'lodash/debounce';\nimport \"./editor.scss\";\n\nexport default function edit( { attributes, setAttributes } ) {\n // State\n const [ results, setResults ] = useState( [] );\n const [ search, setSearch ] = useState( '' );\n const [ postsAvailable, setPostsAvailable ] = useState( false );\n const [ available, setAvailable ] = useState( 0 );\n const [ loaded, setLoaded ] = useState( false );\n const [ selectedChart, setSelectedChart ] = useState( null );\n const [ siteUrl, setSiteUrl ] = useState( null );\n const [ imageSupport, setImageSupport ] = useState( false );\n const [ loadProblem, setLoadProblem ] = useState( false );\n const [ page, setPage ] = useState( 1 );\n const [ loadingMore, setLoadingMore ] = useState( false );\n const resultsRef = useRef( null );\n\n // URLs\n const newUrl = `${ siteUrl }/wp-admin/post-new.php?post_type=m-chart`;\n const editUrl = `${ siteUrl }/wp-admin/post.php?post=${ attributes.chartId }&action=edit`;\n const optionsUrl = `/m-chart/v1/options`;\n\n // Blockprops\n const blockProps = useBlockProps( { className: 'm-chart-block-chart-selector' } );\n\n // Set a cache URL parameter based on the current moment in time to prevent cached images from messing up the UI\n const cacheBuster = `?cache=${performance.now()}`;\n\n // On load we fetch some option settings and run getCharts so we have some intiial reasults loaded into the UI\n useEffect( () => {\n fetchOptions();\n getCharts( search );\n }, [] );\n\n // Fetch the selected chart individually whenever chartId changes\n // Using attributes.chartId as a dependency handles the case where Gutenberg provides the saved attribute value after the initial render\n useEffect( () => {\n setSelectedChart( null );\n if ( attributes.chartId ) {\n getChart( parseInt( attributes.chartId, 10 ) );\n }\n }, [ attributes.chartId ] );\n\n // Load more charts when scrolling near the bottom of the results list\n useEffect( () => {\n const el = resultsRef.current;\n\n if ( ! el ) return;\n\n const handleScroll = () => {\n if ( loadingMore ) return;\n if ( results.length >= available ) return;\n \n // If we're close enough to the bottom of the list load the next page\n if ( el.scrollTop + el.clientHeight >= el.scrollHeight - 100 ) {\n const nextPage = page + 1;\n setPage( nextPage );\n getCharts( search, nextPage );\n }\n };\n\n el.addEventListener( 'scroll', handleScroll );\n \n return () => el.removeEventListener( 'scroll', handleScroll );\n }, [ results, available, loadingMore, page, search ] );\n\n // Build list of charts out of the results object\n const resultsList = results.map( ( x ) => {\n if ( ! imageSupport || ! x.src ) {\n return
  • handleClick( x.id ) } title={ x.title }>
    { x.title }
  • ;\n } else {\n return
  • handleClick( x.id ) } title={ x.title }>
    { x.title }
    {
  • ;\n }\n } );\n\n // Handle clicks to a chart in the results list\n const handleClick = ( id ) => {\n setAttributes( { chartId: id } );\n setSelectedChart( null );\n };\n\n // Handle user typing into the search field\n const handleSearch = ( value ) => {\n console.log( 'search', value );\n doSearch( value );\n };\n\n // Actually actually carry out the debounced search\n const doSearch = useCallback(\n debounce( ( value ) => {\n console.log( 'debounce', value );\n setSearch( value );\n setPage( 1 );\n getCharts( value );\n }, 500),\n []\n );\n\n // Get option settings\n const fetchOptions = () => {\n apiFetch( { path: optionsUrl } ).then( result => {\n setImageSupport( result.image_support );\n setSiteUrl( result.siteurl );\n setPostsAvailable( result.posts_avilable );\n } );\n };\n\n // Get a single chart\n const getChart = ( id ) => {\n apiFetch( { path: `/m-chart/v1/chart/${ id }` } ).then( result => {\n setSelectedChart( {\n id: result.id,\n title: result.title || '-',\n subtitle: result.subtitle,\n width: result.width,\n height: result.height,\n type: result.type || '',\n src: result.url || ''\n } );\n } ).catch( () => {} );\n };\n\n const getCharts = ( value, getPage = 1 ) => {\n setLoadProblem( false );\n\n // If we're getting a subsequent page we're adding to the existing results\n if ( getPage > 1 ) {\n setLoadingMore( true );\n }\n\n // Build the parameters\n const params = new URLSearchParams();\n\n if ( value ) {\n params.set( 's', value );\n }\n\n if ( getPage > 1 ) {\n params.set( 'page', getPage );\n }\n\n const query = params.toString();\n\n // Run the query and grab the results\n apiFetch( { path: `/m-chart/v1/charts${ query ? '?' + query : '' }` } )\n .then(\n result => {\n const newCharts = result.posts.map( x => ( {\n id: x.id,\n title: x.title || '-',\n subtitle: x.subtitle,\n width: x.width,\n height: x.height,\n type: x.type || '',\n src: x.url || ''\n } ) );\n\n // Update the found value to match the current search\n setAvailable( result.found_posts );\n\n // Either append or replace the existing results\n if ( getPage === 1 ) {\n setResults( newCharts );\n } else {\n setResults( prev => [ ...prev, ...newCharts ] );\n }\n\n setLoaded( true );\n setLoadingMore( false );\n } ).catch( ( error ) => {\n // If there's an error we'll note it\n if ( error.code === 'rest_no_route' ) {\n setLoadProblem( true );\n }\n\n setLoadingMore( false );\n } );\n };\n\n return (\n
    \n { !! attributes.chartId &&\n \n \n setAttributes( { show: value } ) }\n />\n \n \n }\n \n \n { ! attributes.chartId &&\n window.open( newUrl, \"_blank\" ) } icon=\"external\">{ __( 'New chart', 'm-chart' ) }\n }\n { !! attributes.chartId &&\n <>\n window.open( editUrl, \"_blank\" ) } icon=\"external\" >{ __( 'Edit chart', 'm-chart' ) }\n handleClick( 0 ) } >{ __( 'Replace', 'm-chart' ) }\n \n } \n \n \n { !! attributes.chartId ? (\n
    \n { ! selectedChart ?\n

    \n :\n
    \n { ! imageSupport || ! selectedChart.src ?\n
    \n
    \n \n
    { selectedChart.title }
    \n { selectedChart.subtitle && (
    { selectedChart.subtitle }
    )}\n
    \n
    \n :\n
    \n \n
    \n }\n
    \n }\n
    \n ) : (\n
    \n \n
    \n { loadProblem ?\n

    { __( 'There was a problem loading charts', 'm-chart' ) }

    \n :\n <>\n { !loaded ?\n

    \n \n

    \n :\n postsAvailable === false ?\n
    \n

    \n { __( 'No charts found', 'm-chart' ) }
    \n

    \n

    \n { __( 'Create a new chart', 'm-chart' ) }\n

    \n
    \n :\n
    \n
    \n handleSearch( value ) }\n autoFocus\n />\n

    { available } { 1 === available ? __( 'chart found', 'm-chart' ) : __( 'charts found', 'm-chart' ) }

    \n
    \n { resultsList.length === 0 && search.length > 1 ?\n

    { __( 'No charts found', 'm-chart' ) }

    \n :\n
      \n { resultsList }\n { loadingMore &&\n
    • \n }\n
    \n }\n
    \n }\n \n }\n
    \n
    \n
    \n ) }\n
    \n );\n}; ","import { __ } from '@wordpress/i18n';\nimport { registerBlockType } from '@wordpress/blocks';\nimport { SVG, Path } from '@wordpress/components';\nimport blockJson from './block.json';\nimport edit from './edit';\n\nconst blockIcon = (\n\n \n \n\n);\n\nregisterBlockType(\n blockJson,\n {\n edit: edit,\n save: () => null,\n icon: blockIcon,\n }\n);","var root = require('./_root');\n\n/** Built-in value references. */\nvar Symbol = root.Symbol;\n\nmodule.exports = Symbol;\n","var Symbol = require('./_Symbol'),\n getRawTag = require('./_getRawTag'),\n objectToString = require('./_objectToString');\n\n/** `Object#toString` result references. */\nvar nullTag = '[object Null]',\n undefinedTag = '[object Undefined]';\n\n/** Built-in value references. */\nvar symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n if (value == null) {\n return value === undefined ? undefinedTag : nullTag;\n }\n return (symToStringTag && symToStringTag in Object(value))\n ? getRawTag(value)\n : objectToString(value);\n}\n\nmodule.exports = baseGetTag;\n","var trimmedEndIndex = require('./_trimmedEndIndex');\n\n/** Used to match leading whitespace. */\nvar reTrimStart = /^\\s+/;\n\n/**\n * The base implementation of `_.trim`.\n *\n * @private\n * @param {string} string The string to trim.\n * @returns {string} Returns the trimmed string.\n */\nfunction baseTrim(string) {\n return string\n ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '')\n : string;\n}\n\nmodule.exports = baseTrim;\n","/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\nmodule.exports = freeGlobal;\n","var Symbol = require('./_Symbol');\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar nativeObjectToString = objectProto.toString;\n\n/** Built-in value references. */\nvar symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/**\n * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the raw `toStringTag`.\n */\nfunction getRawTag(value) {\n var isOwn = hasOwnProperty.call(value, symToStringTag),\n tag = value[symToStringTag];\n\n try {\n value[symToStringTag] = undefined;\n var unmasked = true;\n } catch (e) {}\n\n var result = nativeObjectToString.call(value);\n if (unmasked) {\n if (isOwn) {\n value[symToStringTag] = tag;\n } else {\n delete value[symToStringTag];\n }\n }\n return result;\n}\n\nmodule.exports = getRawTag;\n","/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar nativeObjectToString = objectProto.toString;\n\n/**\n * Converts `value` to a string using `Object.prototype.toString`.\n *\n * @private\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n */\nfunction objectToString(value) {\n return nativeObjectToString.call(value);\n}\n\nmodule.exports = objectToString;\n","var freeGlobal = require('./_freeGlobal');\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\nmodule.exports = root;\n","/** Used to match a single whitespace character. */\nvar reWhitespace = /\\s/;\n\n/**\n * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace\n * character of `string`.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {number} Returns the index of the last non-whitespace character.\n */\nfunction trimmedEndIndex(string) {\n var index = string.length;\n\n while (index-- && reWhitespace.test(string.charAt(index))) {}\n return index;\n}\n\nmodule.exports = trimmedEndIndex;\n","var isObject = require('./isObject'),\n now = require('./now'),\n toNumber = require('./toNumber');\n\n/** Error message constants. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n nativeMin = Math.min;\n\n/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\nfunction debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n timeWaiting = wait - timeSinceLastCall;\n\n return maxing\n ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)\n : timeWaiting;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n clearTimeout(timerId);\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n}\n\nmodule.exports = debounce;\n","/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return value != null && (type == 'object' || type == 'function');\n}\n\nmodule.exports = isObject;\n","/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return value != null && typeof value == 'object';\n}\n\nmodule.exports = isObjectLike;\n","var baseGetTag = require('./_baseGetTag'),\n isObjectLike = require('./isObjectLike');\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && baseGetTag(value) == symbolTag);\n}\n\nmodule.exports = isSymbol;\n","var root = require('./_root');\n\n/**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\nvar now = function() {\n return root.Date.now();\n};\n\nmodule.exports = now;\n","var baseTrim = require('./_baseTrim'),\n isObject = require('./isObject'),\n isSymbol = require('./isSymbol');\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = baseTrim(value);\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = toNumber;\n","// extracted by mini-css-extract-plugin\nexport {};","module.exports = window[\"React\"];","module.exports = window[\"wp\"][\"apiFetch\"];","module.exports = window[\"wp\"][\"blockEditor\"];","module.exports = window[\"wp\"][\"blocks\"];","module.exports = window[\"wp\"][\"components\"];","module.exports = window[\"wp\"][\"element\"];","module.exports = window[\"wp\"][\"i18n\"];","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\tif (!(moduleId in __webpack_modules__)) {\n\t\tdelete __webpack_module_cache__[moduleId];\n\t\tvar e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import \"./chart\";"],"names":["SelectControl","Spinner","ToolbarGroup","ToolbarButton","Placeholder","ExternalLink","PanelBody","SearchControl","getBlockType","useState","useEffect","useRef","useMemo","useCallback","useBlockProps","BlockControls","InspectorControls","__","apiFetch","debounce","edit","attributes","setAttributes","results","setResults","search","setSearch","postsAvailable","setPostsAvailable","available","setAvailable","loaded","setLoaded","selectedChart","setSelectedChart","siteUrl","setSiteUrl","imageSupport","setImageSupport","loadProblem","setLoadProblem","page","setPage","loadingMore","setLoadingMore","resultsRef","newUrl","editUrl","chartId","optionsUrl","blockProps","className","cacheBuster","performance","now","fetchOptions","getCharts","getChart","parseInt","el","current","handleScroll","length","scrollTop","clientHeight","scrollHeight","nextPage","addEventListener","removeEventListener","resultsList","map","x","src","createElement","title","role","key","id","onClick","handleClick","type","alt","handleSearch","value","console","log","doSearch","path","then","result","image_support","siteurl","posts_avilable","subtitle","width","height","url","catch","getPage","params","URLSearchParams","set","query","toString","newCharts","posts","found_posts","prev","error","code","label","show","options","onChange","window","open","icon","Fragment","style","aspectRatio","href","placeholder","autoFocus","ref","registerBlockType","SVG","Path","blockJson","blockIcon","viewBox","xmlns","focusable","d","strokeWidth","save"],"sourceRoot":""} \ No newline at end of file diff --git a/components/class-m-chart-admin.php b/components/class-m-chart-admin.php index d80be3d..80d430e 100755 --- a/components/class-m-chart-admin.php +++ b/components/class-m-chart-admin.php @@ -1,19 +1,19 @@ array( + private $safe_settings = [ + 'performance' => [ 'default', 'no-images', 'no-preview', - ), - 'csv_delimiter' => array( + ], + 'csv_delimiter' => [ ',', "\t", ' ', ';', - ), - ); + ], + ]; private $plugin_url; /** @@ -22,18 +22,18 @@ class M_Chart_Admin { public function __construct() { $this->plugin_url = m_chart()->plugin_url(); - add_action( 'admin_init', array( $this, 'admin_init' ) ); - add_action( 'admin_menu', array( $this, 'admin_menu' ) ); - add_action( 'current_screen', array( $this, 'current_screen' ) ); - add_action( 'admin_footer', array( $this, 'admin_footer' ) ); - add_action( 'wp_ajax_m_chart_export_csv', array( $this, 'ajax_export_csv' ) ); - add_action( 'wp_ajax_m_chart_get_chart_args', array( $this, 'ajax_get_chart_args' ) ); - add_action( 'wp_ajax_m_chart_import_csv', array( $this, 'ajax_import_csv' ) ); - add_action( 'edit_form_before_permalink', array( $this, 'edit_form_before_permalink' ) ); - add_action( 'manage_' . m_chart()->slug . '_posts_custom_column', array( $this, 'manage_posts_custom_column' ), 10, 2 ); - add_action( 'm_chart_settings_admin', array( $this, 'm_chart_settings_admin' ) ); - - add_filter( 'manage_' . m_chart()->slug . '_posts_columns', array( $this, 'manage_posts_columns' ) ); + add_action( 'admin_init', [ $this, 'admin_init' ] ); + add_action( 'admin_menu', [ $this, 'admin_menu' ] ); + add_action( 'current_screen', [ $this, 'current_screen' ] ); + add_action( 'admin_footer', [ $this, 'admin_footer' ] ); + add_action( 'wp_ajax_m_chart_export_csv', [ $this, 'ajax_export_csv' ] ); + add_action( 'wp_ajax_m_chart_get_chart_args', [ $this, 'ajax_get_chart_args' ] ); + add_action( 'wp_ajax_m_chart_import_csv', [ $this, 'ajax_import_csv' ] ); + add_action( 'edit_form_before_permalink', [ $this, 'edit_form_before_permalink' ] ); + add_action( 'manage_' . m_chart()->slug . '_posts_custom_column', [ $this, 'manage_posts_custom_column' ], 10, 2 ); + add_action( 'm_chart_settings_admin', [ $this, 'm_chart_settings_admin' ] ); + + add_filter( 'manage_' . m_chart()->slug . '_posts_columns', [ $this, 'manage_posts_columns' ] ); } /** @@ -42,7 +42,7 @@ public function __construct() { public function admin_init() { $this->save_settings(); - add_action( 'admin_notices', array( $this, 'library_warning' ) ); + add_action( 'admin_notices', [ $this, 'library_warning' ] ); } /** @@ -55,7 +55,7 @@ public function admin_menu() { esc_html__( 'Settings', 'm-chart' ), 'manage_options', m_chart()->slug . '-settings', - array( $this, 'm_chart_settings' ) + [ $this, 'm_chart_settings' ] ); // If multiple libraries are active we'll give you the option of using each one @@ -70,16 +70,16 @@ public function admin_menu() { } // Put the default library into the admin menu first - $args = array( + $args = [ 'post_type' => m_chart()->slug, 'library' => m_chart()->get_library(), - ); + ]; - $submenu[ 'edit.php?post_type=' . m_chart()->slug ][10] = array( + $submenu[ 'edit.php?post_type=' . m_chart()->slug ][10] = [ 'Add ' . $libraries[ m_chart()->get_library() ] . ' Chart', 'edit_posts', add_query_arg( $args, admin_url( 'post-new.php' ) ), - ); + ]; unset( $libraries[ m_chart()->get_library() ] ); @@ -87,16 +87,16 @@ public function admin_menu() { $key = 11; foreach ( $libraries as $library => $library_name ) { - $args = array( + $args = [ 'post_type' => m_chart()->slug, 'library' => $library, - ); + ]; - $submenu[ 'edit.php?post_type=' . m_chart()->slug ][ $key ] = array( + $submenu[ 'edit.php?post_type=' . m_chart()->slug ][ $key ] = [ 'Add ' . $library_name . ' Chart', 'edit_posts', add_query_arg( $args, admin_url( 'post-new.php' ) ), - ); + ]; $key++; } @@ -129,7 +129,7 @@ public function save_settings() { return; } - $validated_settings = array(); + $validated_settings = []; $submitted_settings = $_POST[ m_chart()->slug ]; $default_settings = apply_filters( 'm_chart_default_settings', m_chart()->settings ); @@ -149,33 +149,6 @@ public function save_settings() { } else { $validated_settings[ $setting ] = $default; } - } elseif ( 'lang_settings' === $setting ) { - // The language settings require a bit more checking - foreach ( $default_settings['lang_settings'] as $lang_setting => $lang_default ) { - $lang_value = $submitted_settings[ 'lang_settings' ][ $lang_setting ]; - - if ( 'numericSymbols' === $lang_setting ) { - // The numeric symbols are input as a comma separated string so we'll deal with that here - $numeric_symbols = explode( ',', $lang_value ); - $safe_symbols = array(); - - foreach ( $numeric_symbols as $symbol ) { - $safe_symbols[] = trim( $symbol ); - } - - $validated_settings[ $setting ][ $lang_setting ] = $safe_symbols; - } elseif ( 'numericSymbolMagnitude' === $lang_setting ) { - // Only want positive numbers for the numericSymbolMagnitude value - if ( is_numeric( $lang_value ) && 0 < $lang_value ) { - $validated_settings[ $setting ][ $lang_setting ] = absint( $lang_value ); - } else { - $validated_settings[ $setting ][ $lang_setting ] = $lang_default; - } - } else { - // The rest of the language settings are all single character values - $validated_settings[ $setting ][ $lang_setting ] = sanitize_text_field( substr( $lang_value, 0, 1 ) ); - } - } } else { // Make sure the value is safe before attempting to save it if ( preg_match( '#^[a-zA-Z0-9-_]+$#', $submitted_settings[ $setting ] ) ) { @@ -194,7 +167,7 @@ public function save_settings() { // Make sure the embed endpoint makes it into the rewrite rules flush_rewrite_rules(); - add_action( 'admin_notices', array( $this, 'save_success' ) ); + add_action( 'admin_notices', [ $this, 'save_success' ] ); } /** @@ -217,18 +190,18 @@ public function library_warning() { } $highcharts_check = get_posts( - array( + [ 'post_type' => m_chart()->slug, 'posts_per_page' => 1, 'post_status' => 'any', - 'tax_query' => array( - array( + 'tax_query' => [ + [ 'taxonomy' => m_chart()->slug . '-library', 'field' => 'slug', 'terms' => 'highcharts', - ), - ), - ) + ], + ], + ] ); if ( ! $highcharts_check ) { @@ -264,66 +237,51 @@ public function current_screen( $screen ) { // Only load these if we are on a post page if ( 'post' === $screen->base ) { - // jQuery Mobile Touch Events - wp_enqueue_script( - 'jquery-mobile-touch-events', - $this->plugin_url . '/components/external/jquery-mobile/jquery-mobile-touch-events.js', - array(), - m_chart()->version - ); - - // Jspreadsheet CE + // Jspreadsheet CE — needed by both chartjs (React) and other libraries (jQuery). wp_enqueue_style( 'jspreadsheet', $this->plugin_url . '/components/external/jspreadsheet/jspreadsheet.css', - array(), + [], m_chart()->version ); wp_enqueue_script( 'jspreadsheet', $this->plugin_url . '/components/external/jspreadsheet/jspreadsheet.js', - array( 'jquery', 'jsuites' ), + [ 'jsuites' ], m_chart()->version ); - // jSuites + // jSuites — required by Jspreadsheet. wp_enqueue_style( 'jsuites', $this->plugin_url . '/components/external/jsuites/jsuites.css', - array(), + [], m_chart()->version ); wp_enqueue_script( 'jsuites', $this->plugin_url . '/components/external/jsuites/jsuites.js', - array( 'jquery' ), + [], m_chart()->version ); - // Handlebars - wp_enqueue_script( - 'handlebars', - $this->plugin_url . '/components/external/handlebars/handlebars.js', - array(), - m_chart()->version - ); + // Admin UI React app + $admin_app_asset = require __DIR__ . '/admin-ui/index.asset.php'; - // canvg is useful for SVG -> Canvas conversions wp_enqueue_script( - 'canvg', - $this->plugin_url . '/components/external/canvg/umd.js', - array(), - m_chart()->version + 'm-chart-admin-ui', + $this->plugin_url . '/components/admin-ui/index.js', + $admin_app_asset['dependencies'], + $admin_app_asset['version'], + [ 'strategy' => 'defer' ] ); - // Admin panel JS - wp_enqueue_script( - 'm-chart-admin', - $this->plugin_url . '/components/js/m-chart-admin.js', - array( 'jquery', 'jspreadsheet', 'handlebars' ), - m_chart()->version + wp_set_script_translations( + 'm-chart-admin-ui', + 'm-chart', + plugin_dir_path( __DIR__ ) . 'components/languages/' ); // We need the library and post ID for a bunch of stuff below @@ -341,33 +299,99 @@ public function current_screen( $screen ) { $library = $_GET['library']; } - // Only load this if we are on an appropriate post page - if ( 'post' === $screen->base && 'chartjs' === $library ) { - wp_enqueue_script( - 'm-chart-chartjs-admin', - $this->plugin_url . '/components/js/m-chart-chartjs-admin.js', - array( 'm-chart-admin', 'chartjs', 'jquery' ), - m_chart()->version + if ( 'chartjs' === $library ) { + // Chart.js libs — enqueued explicitly so the React preview has window.Chart + wp_enqueue_script( 'chartjs-datalabels' ); // also loads chartjs + chartjs-helpers + } + + $post_meta = m_chart()->get_post_meta( $post_id ); + $spreadsheet_data = empty( $post_meta['data'] ) ? [ [ '' ] ] : $post_meta['data']['sets']; + unset( $post_meta['data'] ); // passed separately as spreadsheet_data + + // Collect library-specific config for the React admin app + $type_options = []; + $type_option_names = []; + $themes = []; + + if ( m_chart()->library( $library ) ) { + $library_class = m_chart()->library( $library ); + $type_options = $library_class->type_options; + $type_option_names = $library_class->type_option_names; + + foreach ( $library_class->get_themes() as $theme ) { + $themes[] = [ + 'slug' => $theme->slug, + 'name' => $theme->name, + ]; + } + } + + // Format unit terms as an array of {group, units} for easy JS mapping + $unit_terms = []; + + foreach ( m_chart()->get_unit_terms() as $group => $units ) { + $group_units = []; + + foreach ( $units as $unit ) { + $group_units[] = [ 'name' => $unit->name ]; + } + + $unit_terms[] = [ + 'group' => $group, + 'units' => $group_units, + ]; + } + + $chart_image = m_chart()->get_chart_image( $post_id ); + + // Compute initial chart args for the React preview (React-enabled libraries, existing posts only) + $initial_chart_args = null; + + if ( $post_id && m_chart()->library( $library ) ) { + $initial_chart_args = m_chart()->library( $library )->get_chart_args( + $post_id, + m_chart()->get_chart_default_args, + true, // force recompute + false // don't store in cache ); } - wp_localize_script( - 'm-chart-admin', - 'm_chart_admin', - array( - 'refresh_counter' => 0, - 'allow_form_submission' => false, - 'request' => false, - 'performance' => m_chart()->get_settings( 'performance' ), - 'image_support' => apply_filters( 'm_chart_image_support', 'no', $library ), - 'instant_preview_support' => apply_filters( 'm_chart_instant_preview_support', 'no', $library ), - 'image_multiplier' => m_chart()->get_settings( 'image_multiplier' ), - 'image_width' => m_chart()->get_settings( 'image_width' ), - 'library' => $library, - 'set_names' => m_chart()->get_post_meta( $post_id, 'set_names' ), - 'delete_confirm' => esc_attr__( 'Are you sure you want to delete this spreadsheet?', 'm-chart' ), - ) - ); + // Build CSV delimiter map for React's CsvControls component. + $csv_delimiters = []; + foreach ( m_chart()->csv_delimiters as $delimiter => $delimiter_name ) { + $csv_delimiters[ $delimiter ] = $delimiter_name; + } + + $localize_data = [ + 'slug' => m_chart()->slug, + 'version' => m_chart()->version, + 'refresh_counter' => 0, + 'allow_form_submission' => false, + 'request' => false, + 'performance' => m_chart()->get_settings( 'performance' ), + 'image_support' => apply_filters( 'm_chart_image_support', 'no', $library ), + 'instant_preview_support' => apply_filters( 'm_chart_instant_preview_support', 'no', $library ), + 'image_multiplier' => m_chart()->get_settings( 'image_multiplier' ), + 'image_width' => m_chart()->get_settings( 'image_width' ), + 'library' => $library, + 'set_names' => m_chart()->get_post_meta( $post_id, 'set_names' ), + 'delete_confirm' => esc_attr__( 'Are you sure you want to delete this spreadsheet?', 'm-chart' ), + 'post_id' => $post_id, + 'nonce' => wp_create_nonce( m_chart()->slug . '-save-post' ), + 'ajax_url' => admin_url( 'admin-ajax.php' ), + 'post_meta' => $post_meta, + 'spreadsheet_data' => $spreadsheet_data, + 'type_options' => $type_options, + 'type_option_names' => $type_option_names, + 'themes' => $themes, + 'unit_terms' => $unit_terms, + 'image_url' => $chart_image ? esc_url( $chart_image['url'] ) : '', + 'chart_args' => $initial_chart_args, + 'csv_delimiters' => $csv_delimiters, + 'default_delimiter' => m_chart()->get_settings( 'csv_delimiter' ), + ]; + + wp_localize_script( 'm-chart-admin-ui', 'm_chart_admin', $localize_data ); do_action( 'm_chart_admin_scripts', $library, $post_id ); } @@ -376,7 +400,7 @@ public function current_screen( $screen ) { wp_enqueue_style( 'm-chart-admin', $this->plugin_url . '/components/css/m-chart-admin.css', - array(), + [], m_chart()->version ); } @@ -395,7 +419,7 @@ public function meta_boxes() { add_meta_box( m_chart()->slug . '-spreadsheet', esc_html__( 'Data', 'm-chart' ), - array( $this, 'spreadsheet_meta_box' ), + [ $this, 'spreadsheet_meta_box' ], m_chart()->slug, 'normal', 'high' @@ -404,7 +428,7 @@ public function meta_boxes() { add_meta_box( m_chart()->slug, esc_html__( 'Chart', 'm-chart' ), - array( $this, 'chart_meta_box' ), + [ $this, 'chart_meta_box' ], m_chart()->slug, 'normal', 'high' @@ -422,12 +446,9 @@ public function meta_boxes() { * @param object the WP post object as returned by the metabox API */ public function spreadsheet_meta_box( $post ) { - $post_meta = m_chart()->get_post_meta( $post->ID ); - - // Setup default empty sheet data if needed - $sheet_data = empty( $post_meta['data'] ) ? array( array( '' ) ) : $post_meta['data']['sets']; - - require_once __DIR__ . '/templates/spreadsheet-meta-box.php'; + echo '
    '; + echo ''; + wp_nonce_field( m_chart()->slug . '-save-post', $this->get_field_name( 'nonce' ) ); } /** @@ -439,7 +460,6 @@ public function chart_meta_box( $post ) { // Force an instance of 1 since we NEVER show more than one chart at a time inside the admin panel m_chart()->instance = 1; - $chart = m_chart()->get_chart( $post->ID ); $post_meta = m_chart()->get_post_meta( $post->ID ); $image = m_chart()->get_chart_image( $post->ID ); $settings = m_chart()->get_settings(); @@ -457,18 +477,6 @@ public function admin_footer() { return; } ?> - - - - @@ -476,7 +484,7 @@ class="hide" /> } /** - * Inserts a subtitle field under the title field on the chart edit form and includes the handlebars templates we'll need + * Inserts a subtitle field under the title field on the chart edit form * * @param object the WP post object as returned by the metabox API */ @@ -485,10 +493,7 @@ public function edit_form_before_permalink( $post ) { return; } - $post_meta = m_chart()->get_post_meta( $post->ID ); - - require_once __DIR__ . '/templates/subtitle-field.php'; - require_once __DIR__ . '/templates/handlebars.php'; + echo '
    '; } /** @@ -548,7 +553,7 @@ public function m_chart_settings_admin() { * @return array array of columns with the custom column added */ public function manage_posts_columns( $columns ) { - $new_columns = array(); + $new_columns = []; foreach ( $columns as $column => $name ) { $new_columns[ $column ] = $name; @@ -669,12 +674,12 @@ public function attach_image() { // Check for an existing attached image $attachments = get_posts( - array( + [ 'post_type' => 'attachment', 'posts_per_page' => 1, 'post_parent' => $post->ID, 'meta_key' => m_chart()->slug . '-image', - ) + ] ); // If an existing image was found delete it @@ -776,7 +781,7 @@ public function ajax_import_csv() { $parse_csv->parse( $csv_data ); // This deals with Google Doc's crappy CSV exports which don't include columns at the end of a row if they are empty - $data_array = $this->fix_csv_data_array( $parse_csv->data ); + $data_array = $this->fix_csv_data( $parse_csv->data ); wp_send_json_success( $data_array ); } @@ -789,7 +794,7 @@ public function ajax_import_csv() { * * @return array the array of data with matching array value counts */ - public function fix_csv_data_array( $data_array ) { + public function fix_csv_data( $data_array ) { $count = 0; // Get largest row count @@ -824,7 +829,7 @@ public function ajax_export_csv() { // There is no nonce check here because a traditional WP nonce check would not work // Instead we confirm the user has necessary permissions for the post in question if ( ! is_numeric( $_REQUEST['post_id'] ) || ! current_user_can( 'edit_post', absint( $_REQUEST['post_id'] ) ) ) { - wp_die( 'Unauthorized access', 'You do not have permission to do that', array( 'response' => 401 ) ); + wp_die( 'Unauthorized access', 'You do not have permission to do that', [ 'response' => 401 ] ); } $post = get_post( absint( $_REQUEST['post_id'] ) ); @@ -865,14 +870,29 @@ public function ajax_get_chart_args() { wp_send_json_error( esc_html__( 'Invalid nonce', 'm-chart' ) ); } - // Does the post exist? - if ( ! $post = get_post( absint( $_POST['post_id'] ) ) ) { - wp_send_json_error( esc_html__( 'Invalid post', 'm-chart' ) ); - } + // Does the post exist? (post_id is 0 for new charts that haven't been saved yet) + $post_id = absint( $_POST['post_id'] ); - // Can the user edit this post? - if ( ! current_user_can( 'edit_post', $post->ID ) ) { - wp_send_json_error( esc_html__( 'Permission error', 'm-chart' ) ); + if ( $post_id ) { + if ( ! $post = get_post( $post_id ) ) { + wp_send_json_error( esc_html__( 'Invalid post', 'm-chart' ) ); + } + + if ( ! current_user_can( 'edit_post', $post->ID ) ) { + wp_send_json_error( esc_html__( 'Permission error', 'm-chart' ) ); + } + } else { + // New chart — no saved post yet, build a stub so the library can compute chart args + if ( ! current_user_can( 'edit_posts' ) ) { + wp_send_json_error( esc_html__( 'Permission error', 'm-chart' ) ); + } + + $post = new WP_Post( (object) [ + 'ID' => 0, + 'post_title' => '', + 'post_type' => m_chart()->slug, + 'post_status' => 'auto-draft', + ] ); } // Is this a valid library? diff --git a/components/class-m-chart-block.php b/components/class-m-chart-block.php index 15d370d..108dd71 100644 --- a/components/class-m-chart-block.php +++ b/components/class-m-chart-block.php @@ -1,14 +1,15 @@ 'm-chart', 'post_status' => 'publish' ]; - $graphs = new WP_Query( $args ); + // Check the performance setting + $performance = m_chart()->get_settings( 'performance' ); + + // Check if there's any posts available + $args = [ + 'post_type' => 'm-chart', + 'post_status' => 'publish', + 'posts_per_page' => 1 + ]; + + $posts = new WP_Query( $args ); return [ - 'siteurl' => get_option( 'siteurl' ), - 'image_support_active' => $image_support, - 'maxAvailable' => $graphs->found_posts, + 'siteurl' => get_option( 'siteurl' ), + 'image_support' => 'default' === $performance ? true : false, + 'posts_avilable' => $posts->have_posts() ? true : false, ]; } /** * Register api route to search graphs by title using a search string. */ - public function register_fetch_graphs() { + public function register_get_charts() { register_rest_route( 'm-chart/v1', - '/graphs(?:/(?P([a-zA-Z0-9_\- ,]|%20)+))?', + '/charts', [ 'methods' => 'GET', - 'callback' => [ $this, 'fetch_graphs' ], + 'callback' => [ $this, 'get_charts' ], 'permission_callback' => function () { return true; }, + 'args' => [ + 's' => [ + 'default' => '', + 'sanitize_callback' => 'sanitize_text_field', + ], + 'page' => [ + 'default' => 1, + 'validate_callback' => function ( $param ) { + return is_numeric( $param ) && intval( $param ) > 0; + }, + ], + ], ] ); } /** - * Callback function to fetch graphs possibly using a search term to query all titles. - * Returns an array with the total number of results and a max of the 24 most recent objects + * Register api route to fetch a single graph by post ID. */ - public function fetch_graphs( $request ) { - $args = [ 'post_type' => 'm-chart', 'post_status' => 'publish' ]; - $search_string = $request->get_param( 's' ); + public function register_get_chart() { + register_rest_route( + 'm-chart/v1', + '/chart/(?P\d+)', + [ + 'methods' => 'GET', + 'callback' => [ $this, 'get_chart' ], + 'permission_callback' => function () { + return true; + }, + 'args' => [ + 'id' => [ + 'validate_callback' => function ( $param ) { + return is_numeric( $param ); + }, + ], + ], + ] + ); + } + + /** + * Fetch a single published chart post by ID and return the data needed by the block + * + * @param WP_REST_Request $request The REST request object. + * @return array|WP_Error Chart data array or WP_Error if not found. + */ + public function get_chart( $request ) { + $post_id = intval( $request->get_param( 'id' ) ); + $post = get_post( $post_id ); - if ( ! ( empty( $search_string ) ) ) { - $search_string = sanitize_text_field( urldecode( $search_string ) ); - $args['s'] = $search_string; + if ( ! $post || $post->post_type !== 'm-chart' || $post->post_status !== 'publish' ) { + return new WP_Error( 'not_found', __( 'Chart not found', 'm-chart' ), [ 'status' => 404 ] ); } - $graphs = new WP_Query( $args ); - $total_number_of_possible_results = $graphs->found_posts; + $post_meta = get_post_meta( $post->ID, 'm-chart', true ); + $post_thumbnail_id = get_post_meta( $post->ID, '_thumbnail_id', true ); + + return [ + 'id' => intval( $post->ID ), + 'title' => html_entity_decode( get_the_title( $post->ID ) ), + 'subtitle' => isset( $post_meta['subtitle'] ) ? $post_meta['subtitle'] : '', + 'url' => get_the_post_thumbnail_url( $post->ID ), + 'type' => isset( $post_meta['type'] ) ? $post_meta['type'] : '', + 'height' => wp_get_attachment_metadata( $post_thumbnail_id )['height'] ?? 800, + 'width' => wp_get_attachment_metadata( $post_thumbnail_id )['width'] ?? 1200, + ]; + } + + /** + * Fetch charts with an optional search term + * + * @param WP_REST_Request $request The REST request object. + * @return array|WP_Error Chart data array or WP_Error if not found. + */ + public function get_charts( $request ) { + $args = [ + 'post_type' => 'm-chart', + 'post_status' => 'publish', + 'posts_per_page' => 9, + 'paged' => intval( $request->get_param( 'page' ) ) ?: 1, + ]; + + // If there's a search string add it to our args + $search_string = $request->get_param( 's' ); + + if ( ! empty( $search_string ) ) { + $args['s'] = $search_string; + } - // Limit the default number to prevent memory issues. - $args['numberposts'] = 24; - $posts = get_posts( $args ); + // Get the charts + $posts = new WP_Query( $args ); + // Buid a results array to return to the block $results = []; - foreach ( $posts as $post ) { - $result = []; - $post_meta = get_post_meta( $post->ID, 'm-chart', true ); - $post_thumbnail_id = get_post_meta( $post->ID, '_thumbnail_id', true ); - $result['id'] = strval( $post->ID ); - $result['title'] = html_entity_decode( get_the_title( $post->ID ) ); - $result['subtitle'] = isset( $post_meta ) && isset( $post_meta['subtitle'] ) ? $post_meta['subtitle'] : ''; - $result['url'] = get_the_post_thumbnail_url( $post->ID ); - $result['type'] = isset( $post_meta ) && isset( $post_meta['type'] ) ? $post_meta['type'] : ''; - $result['height'] = wp_get_attachment_metadata( $post_thumbnail_id )['height'] ?? 800; - $result['width'] = wp_get_attachment_metadata( $post_thumbnail_id )['width'] ?? 1200; - $results[] = $result; + if ( $posts->have_posts() ) { + foreach ( $posts->posts as $post ) { + $post_meta = get_post_meta( $post->ID, 'm-chart', true ); + $post_thumbnail_id = get_post_meta( $post->ID, '_thumbnail_id', true ); + $chart_image = m_chart()->get_chart_image( $post->ID ); + + $result = [ + 'id' => intval( $post->ID ), + 'title' => html_entity_decode( get_the_title( $post->ID ) ), + 'subtitle' => isset( $post_meta ) && isset( $post_meta['subtitle'] ) ? $post_meta['subtitle'] : '', + 'url' => $chart_image['url'], + 'type' => isset( $post_meta ) && isset( $post_meta['type'] ) ? $post_meta['type'] : '', + 'height' => $chart_image['height'] ?? 800, + 'width' => $chart_image['width'] ?? 1200, + ]; + + $results[] = $result; + } } - return [$total_number_of_possible_results, $results]; + return [ 'found_posts' => $posts->found_posts, 'posts' => $results ]; } } diff --git a/components/class-m-chart-chartjs.php b/components/class-m-chart-chartjs.php index b88423e..bcbc693 100755 --- a/components/class-m-chart-chartjs.php +++ b/components/class-m-chart-chartjs.php @@ -9,7 +9,7 @@ class M_Chart_Chartjs { public $post; public $post_meta; public $args; - public $type_options = array( + public $type_options = [ 'line', 'spline', 'area', @@ -24,46 +24,46 @@ class M_Chart_Chartjs { 'radar', 'radar-area', 'polar', - ); - public $type_option_names = array(); + ]; + public $type_option_names = []; public $theme_directories; - public $colors = array( + public $colors = [ '#ed6d85', // Pink '#f7d06b', // Yellow '#f2a354', // Orange '#56a0e5', // Blue '#6cbebf', // Turquoise '#47494b', // Gray - ); - public $points = array( - array( - 'point' => array( + ]; + public $points = [ + [ + 'point' => [ 'pointStyle' => 'circle', - ), - ), - array( - 'point' => array( + ], + ], + [ + 'point' => [ 'pointStyle' => 'rectRot', - ), - ), - array( - 'point' => array( + ], + ], + [ + 'point' => [ 'pointStyle' => 'rect', - ), - ), - array( - 'point' => array( + ], + ], + [ + 'point' => [ 'pointStyle' => 'triangle', - ), - ), - array( - 'point' => array( + ], + ], + [ + 'point' => [ 'pointStyle' => 'triangle', 'rotation' => 180, - ), - ), - ); - public $chart_types = array( + ], + ], + ]; + public $chart_types = [ 'column' => 'bar', 'stacked-column' => 'bar', 'bar' => 'bar', @@ -78,23 +78,23 @@ class M_Chart_Chartjs { 'radar' => 'radar', 'radar-area' => 'radar', 'polar' => 'polarArea', - ); + ]; public $helpers_loaded = false; /** * Constructor */ public function __construct() { - add_filter( 'm_chart_image_support', array( $this, 'm_chart_image_support' ), 10, 2 ); - add_filter( 'm_chart_iframe_scripts', array( $this, 'm_chart_iframe_scripts' ), 10, 2 ); + add_filter( 'm_chart_image_support', [ $this, 'm_chart_image_support' ], 10, 2 ); + add_filter( 'm_chart_iframe_scripts', [ $this, 'm_chart_iframe_scripts' ], 10, 2 ); - $this->theme_directories = array( + $this->theme_directories = [ get_stylesheet_directory() . '/m-chart-chartjs-themes/', // Child theme get_template_directory() . '/m-chart-chartjs-themes/', // Parent theme __DIR__ . '/chartjs-themes/', - ); + ]; - $this->type_option_names = array( + $this->type_option_names = [ 'line' => esc_html__( 'Line', 'm-chart' ), 'spline' => esc_html__( 'Spline', 'm-chart' ), 'area' => esc_html__( 'Area', 'm-chart' ), @@ -109,7 +109,7 @@ public function __construct() { 'radar' => esc_html__( 'Radar', 'm-chart' ), 'radar-area' => esc_html__( 'Radar Area', 'm-chart' ), 'polar' => esc_html__( 'Polar', 'm-chart' ), - ); + ]; } /** @@ -136,6 +136,7 @@ public function get_chart_data( $post_id, $args ) { * Enqueue any Chart.js plugins that we'll need */ public function enqueue_chartjs_plugins() { + wp_enqueue_script( 'chartjs-helper' ); wp_enqueue_script( 'chartjs-datalabels' ); } @@ -166,68 +167,71 @@ public function get_chart_args( $post_id, $args, $force = false, $cache = true ) } // Run the parse class on the data - $data = isset( $this->post_meta['data']['sets'][0] ) ? $this->post_meta['data']['sets'][0] : ''; + $data = isset( $this->post_meta['data']['sets'][0] ) ? $this->post_meta['data']['sets'][0] : []; m_chart()->parse()->parse_data( $data, $this->post_meta['parse_in'] ); $type = $this->post_meta['type']; - $chart_args = array( + $chart_args = [ 'type' => $this->chart_types[ $this->post_meta['type'] ], - 'options' => array( - 'plugins' => array( + 'options' => [ + 'plugins' => [ // @TODO Figure out how to support subtitles in Chart.js - 'title' => array( + 'title' => [ 'display' => true, 'text' => $this->esc_title( apply_filters( 'the_title', $this->post->post_title, $this->post->ID ) ), - 'font' => array( + 'font' => [ 'size' => 21, 'weight' => 'normal', - ), - 'padding' => array( + ], + 'padding' => [ 'bottom' => 15, - ), - ), - 'legend' => array( + ], + ], + 'legend' => [ 'display' => $this->post_meta['legend'] ? true : false, 'position' => 'bottom', - 'labels' => array( - 'font' => array( + 'labels' => [ + 'font' => [ 'weight' => 'bold', - ), + ], 'usePointStyle' => true, - ), - ), - 'tooltip' => array( + ], + ], + 'tooltip' => [ 'enabled' => true, - ), - ), - 'elements' => array( - 'point' => array( + ], + ], + 'layout' => [ + 'padding' => 20, + ], + 'elements' => [ + 'point' => [ 'hoverRadius' => 7, 'hitRadius' => 13, - ), - ), + ], + ], 'responsive' => true, 'maintainAspectRatio' => false, 'locale' => m_chart()->get_settings( 'locale' ), - ), - ); + ], + ]; // Subtitles are handled by a plugin so we have to conditionally set these values if ( '' != $this->post_meta['subtitle'] ) { $chart_args['options']['plugins']['title']['padding']['bottom'] = 10; - $chart_args['options']['plugins']['subtitle'] = array( + $chart_args['options']['plugins']['subtitle'] = [ 'display' => true, 'text' => $this->esc_title( $this->post_meta['subtitle'] ), - 'font' => array( + 'font' => [ 'size' => 18, 'weight' => 'normal', - ), - 'padding' => array( + ], + 'padding' => [ 'bottom' => 15, - ), - ); + ], + ]; } // If we're in the admin panel we need to bump up the devicePixelRatio to get a better image @@ -311,20 +315,18 @@ public function get_chart_args( $post_id, $args, $force = false, $cache = true ) // 'anchor' => 'end', //); - // Add some stuff for the helper class - $chart_args['value_prefix'] = m_chart()->parse()->data_prefix; - $chart_args['value_suffix'] = m_chart()->parse()->data_suffix; - $chart_args['locale'] = m_chart()->get_settings( 'locale' ); - $chart_args['labels_pos'] = m_chart()->parse()->value_labels_position; + // Add some stuff for the helper plugin + $chart_args['locale'] = m_chart()->get_settings( 'locale' ); + $chart_args['options']['plugins']['m-chart-helper']['labels_pos'] = m_chart()->parse()->value_labels_position; // Chart.js 3.x.x requires at least some form of data set (even if it's empty) or the chart object doesn't get generated if ( ! isset( $chart_args['data']['datasets'] ) ) { - $chart_args['data']['datasets'] = array( - array( + $chart_args['data']['datasets'] = [ + [ 'label' => '', - 'data' => array(), - ), - ); + 'data' => [], + ], + ]; } // Apply the theme @@ -360,17 +362,17 @@ public function get_chart_args( $post_id, $args, $force = false, $cache = true ) 'stacked-column' == $this->post_meta['type'] || 'stacked-bar' == $this->post_meta['type'] ) { - $chart_args['data']['datasets'][ $key ]['datalabels'] = array( + $chart_args['data']['datasets'][ $key ]['datalabels'] = [ 'align' => 'center', 'anchor' => 'center', 'color' => '#ffffff', - ); + ]; } else { - $chart_args['data']['datasets'][ $key ]['datalabels'] = array( + $chart_args['data']['datasets'][ $key ]['datalabels'] = [ 'align' => 'end', 'anchor' => 'end', 'color' => $this->colors[ $key % $color_count ], - ); + ]; } } } @@ -385,11 +387,11 @@ public function get_chart_args( $post_id, $args, $force = false, $cache = true ) $chart_args['data']['datasets'][0]['backgroundColor'][ $key ] = $this->colors[ $key % $color_count ]; if ( true == $this->post_meta['labels'] ) { - $chart_args['data']['datasets'][0]['datalabels'] = array( + $chart_args['data']['datasets'][0]['datalabels'] = [ 'align' => 'end', 'anchor' => 'end', 'color' => $this->colors, - ); + ]; } } } elseif ( @@ -399,11 +401,11 @@ public function get_chart_args( $post_id, $args, $force = false, $cache = true ) $chart_args['data']['datasets'][0]['backgroundColor'] = $this->colors; if ( true == $this->post_meta['labels'] ) { - $chart_args['data']['datasets'][0]['datalabels'] = array( + $chart_args['data']['datasets'][0]['datalabels'] = [ 'align' => 'end', 'anchor' => 'end', 'color' => $this->colors, - ); + ]; } } elseif ( isset( $chart_args['data']['datasets'] ) ) { foreach ( $chart_args['data']['datasets'] as $key => $dataset ) { @@ -444,11 +446,11 @@ public function get_chart_args( $post_id, $args, $force = false, $cache = true ) } if ( true == $this->post_meta['labels'] ) { - $chart_args['data']['datasets'][ $key ]['datalabels'] = array( + $chart_args['data']['datasets'][ $key ]['datalabels'] = [ 'align' => 'end', 'anchor' => 'end', 'color' => $color, - ); + ]; } } } @@ -457,14 +459,14 @@ public function get_chart_args( $post_id, $args, $force = false, $cache = true ) $chart_args['options']['plugins']['datalabels']['display'] = false; if ( true == $this->post_meta['labels'] ) { - $chart_args['options']['plugins']['datalabels'] = array( + $chart_args['options']['plugins']['datalabels'] = [ 'color' => 'black', - 'font' => array( + 'font' => [ 'weight' => 'bold', - ), + ], 'offset' => 3, 'display' => 'auto', - ); + ]; } if ( $theme ) { @@ -499,7 +501,7 @@ public function m_chart_update_post_meta( $post_id, $parsed_meta ) { $this->post_meta = $parsed_meta; - $this->get_chart_args( $post_id, array(), true ); + $this->get_chart_args( $post_id, [], true ); } /** @@ -527,8 +529,8 @@ public function m_chart_image_support( $supports_images, $library ) { public function get_value_labels_array() { $value_labels = m_chart()->parse()->value_labels; - if ( isset( $value_labels['first_column'] ) ) { - $label_key = 'rows' == $this->post_meta['parse_in'] ? 'first_row' : 'first_column'; + if ( isset( $value_labels[ M_Chart_Parse::LABELS_FIRST_COLUMN ] ) ) { + $label_key = M_Chart_Parse::PARSE_ROWS == $this->post_meta['parse_in'] ? M_Chart_Parse::LABELS_FIRST_ROW : M_Chart_Parse::LABELS_FIRST_COLUMN; return $value_labels[ $label_key ]; } @@ -545,10 +547,10 @@ public function get_value_labels_array() { */ public function add_axis_labels( $chart_args ) { // Note the additional layer in the array: [0] its needed for Chart.js to see the label settings - $chart_args['options']['scales']['x']['title'] = array( + $chart_args['options']['scales']['x']['title'] = [ 'display' => '' == $this->post_meta['x_title'] ? false : true, 'text' => $this->esc_title( $this->post_meta['x_title'] ), - ); + ]; // We've got x axis units so we'll add them to the axis label if ( '' != $this->post_meta['x_units'] ) { @@ -560,10 +562,10 @@ public function add_axis_labels( $chart_args ) { $chart_args['options']['scales']['x']['title']['text'] .= $x_units; } - $chart_args['options']['scales']['y']['title'] = array( + $chart_args['options']['scales']['y']['title'] = [ 'display' => '' == $this->post_meta['y_title'] ? false : true, 'text' => $this->esc_title( $this->post_meta['y_title'] ), - ); + ]; // We've got y axis units so we'll add them to the axis label if ( '' != $this->post_meta['y_units'] ) { @@ -587,13 +589,14 @@ public function add_axis_labels( $chart_args ) { */ public function add_data_sets( $chart_args ) { // When Chart.js encounters an empty data value it stops so we set them to NULL - $data_array = array_map( array( $this, 'fix_null_values' ), m_chart()->parse()->set_data ); + $data_array = array_map( [ $this, 'fix_null_values' ], m_chart()->parse()->set_data ); + $raw_data = m_chart()->parse()->raw_data; if ( 'pie' == $this->post_meta['type'] || 'doughnut' == $chart_args['type'] || 'polar' == $this->post_meta['type'] - || 'both' != m_chart()->parse()->value_labels_position + || M_Chart_Parse::LABELS_BOTH != m_chart()->parse()->value_labels_position && ( 'scatter' != $this->post_meta['type'] && 'bubble' != $this->post_meta['type'] @@ -603,7 +606,8 @@ public function add_data_sets( $chart_args ) { ) { foreach ( $chart_args['data']['labels'] as $key => $label ) { if ( isset( $data_array[ $key ] ) ) { - $chart_args['data']['datasets'][0]['data'][] = $data_array[ $key ]; + $chart_args['data']['datasets'][0]['data'][] = $data_array[ $key ]; + $chart_args['data']['datasets'][0]['rawData'][] = isset( $raw_data[ $key ] ) ? $raw_data[ $key ] : ''; } } } elseif ( @@ -615,12 +619,13 @@ public function add_data_sets( $chart_args ) { foreach ( $this->post_meta['data']['sets'] as $key => $data ) { $parse = m_chart()->parse()->parse_data( $data, $this->post_meta['parse_in'] ); - $data_array = array_map( array( $this, 'fix_null_values' ), $parse->set_data ); + $data_array = array_map( [ $this, 'fix_null_values' ], $parse->set_data ); - $chart_args['data']['datasets'][ $key ] = array( - 'label' => isset( $set_names[ $key ] ) ? $set_names[ $key ] : 'Sheet 1', - 'data' => $data_array, - ); + $chart_args['data']['datasets'][ $key ] = [ + 'label' => isset( $set_names[ $key ] ) ? $set_names[ $key ] : 'Sheet 1', + 'data' => $data_array, + 'rawData' => $parse->raw_data, + ]; } } elseif ( 'scatter' == $this->post_meta['type'] ) { $set_names = $this->post_meta['set_names']; @@ -628,19 +633,29 @@ public function add_data_sets( $chart_args ) { foreach ( $this->post_meta['data']['sets'] as $key => $data ) { $parse = m_chart()->parse()->parse_data( $data, $this->post_meta['parse_in'] ); - $data_array = array_map( array( $this, 'fix_null_values' ), $parse->set_data ); + $data_array = array_map( [ $this, 'fix_null_values' ], $parse->set_data ); - $new_data_array = array(); + $new_data_array = []; - $label_key = ( $this->post_meta['parse_in'] == 'rows' ) ? 'first_column' : 'first_row'; - - if ( 'both' == $parse->value_labels_position ) { - foreach ( $data_array as $data_key => $data ) { - $new_data_array[] = array( - 'x' => $data[0], - 'y' => $data[1], - 'label' => $parse->value_labels[ $label_key ][ $data_key ], - ); + if ( M_Chart_Parse::LABELS_BOTH == $parse->value_labels_position ) { + if ( M_Chart_Parse::PARSE_COLUMNS == $this->post_meta['parse_in'] ) { + // PARSE_COLUMNS: set_data is [x_values_array, y_values_array] — zip by index + foreach ( $data_array[0] as $i => $x ) { + $new_data_array[] = [ + 'x' => $x, + 'y' => $data_array[1][ $i ], + 'label' => $parse->value_labels[ M_Chart_Parse::LABELS_FIRST_COLUMN ][ $i ], + ]; + } + } else { + // PARSE_ROWS: set_data is [[x1,y1], [x2,y2], ...] — one entry per point + foreach ( $data_array as $data_key => $data ) { + $new_data_array[] = [ + 'x' => $data[0], + 'y' => $data[1], + 'label' => $parse->value_labels[ M_Chart_Parse::LABELS_FIRST_COLUMN ][ $data_key ], + ]; + } } } else { foreach ( $data_array as $data_key => $data ) { @@ -648,17 +663,22 @@ public function add_data_sets( $chart_args ) { continue; } - $new_data_array[] = array( + if ( ! isset( $data_array[ $data_key + 1 ] ) ) { + continue; + } + + $new_data_array[] = [ 'x' => $data, 'y' => $data_array[ $data_key + 1 ], - ); + ]; } } - $chart_args['data']['datasets'][ $key ] = array( - 'label' => isset( $set_names[ $key ] ) ? $set_names[ $key ] : 'Sheet 1', - 'data' => $new_data_array, - ); + $chart_args['data']['datasets'][ $key ] = [ + 'label' => isset( $set_names[ $key ] ) ? $set_names[ $key ] : 'Sheet 1', + 'data' => $new_data_array, + 'rawData' => $parse->raw_data, + ]; } } elseif ( 'bubble' == $this->post_meta['type'] ) { $set_names = $this->post_meta['set_names']; @@ -666,57 +686,74 @@ public function add_data_sets( $chart_args ) { foreach ( $this->post_meta['data']['sets'] as $key => $data ) { $parse = m_chart()->parse()->parse_data( $data, $this->post_meta['parse_in'] ); - $data_array = array_map( array( $this, 'fix_null_values' ), $parse->set_data ); - - $new_data_array = array(); + $data_array = array_map( [ $this, 'fix_null_values' ], $parse->set_data ); - $label_key = ( $this->post_meta['parse_in'] == 'rows' ) ? 'first_column' : 'first_row'; + $new_data_array = []; - if ( 'both' == $parse->value_labels_position ) { - foreach ( $data_array as $data_key => $data ) { - $new_data_array[] = array( - 'x' => $data[0], - 'y' => $data[1], - 'r' => $data[2], - 'label' => $parse->value_labels[ $label_key ][ $data_key ], - ); + if ( M_Chart_Parse::LABELS_BOTH == $parse->value_labels_position ) { + if ( M_Chart_Parse::PARSE_COLUMNS == $this->post_meta['parse_in'] ) { + // PARSE_COLUMNS: set_data is [x_values_array, y_values_array, r_values_array] — zip by index + foreach ( $data_array[0] as $i => $x ) { + $new_data_array[] = [ + 'x' => $x, + 'y' => $data_array[1][ $i ], + 'r' => $data_array[2][ $i ], + 'label' => $parse->value_labels[ M_Chart_Parse::LABELS_FIRST_COLUMN ][ $i ], + ]; + } + } else { + // PARSE_ROWS: set_data is [[x1,y1,r1], [x2,y2,r2], ...] — one entry per point + foreach ( $data_array as $data_key => $data ) { + $new_data_array[] = [ + 'x' => $data[0], + 'y' => $data[1], + 'r' => $data[2], + 'label' => $parse->value_labels[ M_Chart_Parse::LABELS_FIRST_COLUMN ][ $data_key ], + ]; + } } } else { foreach ( $data_array as $data_key => $data ) { - if ( $data_key % 2 ) { + if ( $data_key % 3 ) { continue; } - $new_data_array[] = array( + if ( ! isset( $data_array[ $data_key + 1 ] ) || ! isset( $data_array[ $data_key + 2 ] ) ) { + continue; + } + + $new_data_array[] = [ 'x' => $data, 'y' => $data_array[ $data_key + 1 ], 'r' => $data_array[ $data_key + 2 ], - ); + ]; } } - $chart_args['data']['datasets'][ $key ] = array( - 'label' => isset( $set_names[ $key ] ) ? $set_names[ $key ] : 'Sheet 1', - 'data' => $new_data_array, - ); + $chart_args['data']['datasets'][ $key ] = [ + 'label' => isset( $set_names[ $key ] ) ? $set_names[ $key ] : 'Sheet 1', + 'data' => $new_data_array, + 'rawData' => $parse->raw_data, + ]; } } else { - $set_data = array(); + $set_data = []; - $label_key = ( $this->post_meta['parse_in'] == 'rows' ) ? 'first_column' : 'first_row'; + $label_key = ( $this->post_meta['parse_in'] == M_Chart_Parse::PARSE_ROWS ) ? M_Chart_Parse::LABELS_FIRST_COLUMN : M_Chart_Parse::LABELS_FIRST_ROW; foreach ( $data_array as $key => $data_chunk ) { - $set_data[ $key ] = array( - 'label' => m_chart()->parse()->value_labels[ $label_key ][ $key ], - 'data' => array(), - ); + $set_data[ $key ] = [ + 'label' => m_chart()->parse()->value_labels[ $label_key ][ $key ], + 'data' => [], + 'rawData' => isset( $raw_data[ $key ] ) ? $raw_data[ $key ] : [], + ]; if ( is_array( $data_chunk ) ) { foreach ( $data_chunk as $data ) { $set_data[ $key ]['data'][] = $data; } } else { - $set_data[ $key ]['data'] = array(); + $set_data[ $key ]['data'] = []; } } @@ -744,6 +781,7 @@ public function m_chart_iframe_scripts( $scripts, $post_id ) { $type = m_chart()->get_post_meta( $post_id, 'type' ); + $scripts[] = 'chartjs-helper'; $scripts[] = 'chartjs-datalabels'; // Return the scripts @@ -753,28 +791,28 @@ public function m_chart_iframe_scripts( $scripts, $post_id ) { /** * Helper function escapes and modifies text/title values * - * @param string an string you want to use in Highcharts + * @param string an string you want to use in Chart.js * * @return string an escaped and modified string */ public function esc_title( $string ) { $string = html_entity_decode( $string, ENT_QUOTES ); - $find = array( + $find = [ "\n", "\r", '

    ', '—', '–', - ); + ]; - $replace = array( + $replace = [ '
    ', '
    ', '
    ', '-', '-', - ); + ]; $string = str_replace( $find, $replace, $string ); @@ -791,7 +829,7 @@ public function esc_title( $string ) { */ public function fix_null_values( $value ) { if ( is_array( $value ) ) { - return array_map( array( $this, 'fix_null_values' ), $value ); + return array_map( [ $this, 'fix_null_values' ], $value ); } if ( ! is_numeric( $value ) ) { @@ -811,23 +849,28 @@ public function fix_null_values( $value ) { public function hex_to_rgb( $hex ) { // Make sure the hex string is a proper hex string $hex = preg_replace( '#[^0-9A-Fa-f]#', '', $hex ); - $rgb = array(); - - if ( 6 === strlen( $hex ) ) { - // If a proper hex code, convert using bitwise operation, no overhead... faster - $color_value = hexdec( $hex ); - - $rgb['red'] = 0xFF & ( $color_value >> 0x10 ); - $rgb['green'] = 0xFF & ( $color_value >> 0x8 ); - $rgb['blue'] = 0xFF & $color_value; - } elseif ( 3 == strlen( $hex ) ) { - // If shorthand notation we need to do some string manipulations - $rgb['red'] = hexdec( str_repeat( substr( $hex, 0, 1 ), 2 ) ); - $rgb['green'] = hexdec( str_repeat( substr( $hex, 1, 1 ), 2 ) ); - $rgb['blue'] = hexdec( str_repeat( substr( $hex, 2, 1 ), 2 ) ); - } else { - // Invalid hex color code so we return false - return false; + $rgb = []; + + switch ( strlen( $hex ) ) { + case 6: + // If a proper hex code, convert using bitwise operation, no overhead... faster + $color_value = hexdec( $hex ); + + $rgb['red'] = 0xFF & ( $color_value >> 0x10 ); + $rgb['green'] = 0xFF & ( $color_value >> 0x8 ); + $rgb['blue'] = 0xFF & $color_value; + break; + + case 3: + // If shorthand notation we need to do some string manipulations + $rgb['red'] = hexdec( str_repeat( substr( $hex, 0, 1 ), 2 ) ); + $rgb['green'] = hexdec( str_repeat( substr( $hex, 1, 1 ), 2 ) ); + $rgb['blue'] = hexdec( str_repeat( substr( $hex, 2, 1 ), 2 ) ); + break; + + default: + // Invalid hex color code so we return false + return false; } return $rgb; @@ -839,7 +882,7 @@ public function hex_to_rgb( $hex ) { * @return array an array of themes */ public function get_themes() { - $themes = array(); + $themes = []; foreach ( $this->theme_directories as $directory ) { $themes = array_merge( $themes, $this->_get_themes_readdir( $directory ) ); @@ -881,11 +924,11 @@ private function get_theme( $slug ) { private function _get_themes_readdir( $theme_base ) { // Sanity check to make sure we have a real directory if ( ! is_dir( $theme_base ) ) { - return array(); + return []; } $theme_dir = new DirectoryIterator( $theme_base ); - $themes = array(); + $themes = []; foreach ( $theme_dir as $file ) { if ( ! $file->isFile() || ! preg_match( '#.php$#i', $file->getFilename() ) ) { @@ -901,12 +944,12 @@ private function _get_themes_readdir( $theme_base ) { if ( isset( $name ) && '' != $name ) { $file = basename( $file ); - $themes[ $file ] = (object) array( + $themes[ $file ] = (object) [ 'slug' => substr( $file, 0, -4 ), 'name' => $name, 'file' => $file, 'options' => require $theme_base . $file, - ); + ]; } } diff --git a/components/class-m-chart-parse.php b/components/class-m-chart-parse.php index ec64965..14a6155 100755 --- a/components/class-m-chart-parse.php +++ b/components/class-m-chart-parse.php @@ -1,42 +1,38 @@ data = $data; $this->parse_in = $parse_in; $this->value_labels_position = $this->get_value_labels_position(); @@ -46,41 +42,44 @@ public function parse_data( $data, $parse_in ) { } /** - * Helper function builds an array of the value labels for a data set - * - * @return array an array of value labels filtered out of the data set + * Populates $this->value_labels by reading label values from the data array based on the detected labels position */ - public function parse_value_labels() { - $this->value_labels = array(); + private function parse_value_labels(): void { + $this->value_labels = []; - if ( 'none' == $this->value_labels_position ) { - return $this->value_labels; - } + switch ( $this->value_labels_position ) { + case self::LABELS_NONE: + return; - if ( 'first_column' == $this->value_labels_position ) { - foreach ( (array) $this->data as $columns ) { - if ( '' != trim( (string) $columns[0] ) ) { - $this->value_labels[] = $this->clean_labels( $columns[0] ); + case self::LABELS_FIRST_COLUMN: + foreach ( (array) $this->data as $columns ) { + if ( '' != trim( (string) $columns[0] ) ) { + $this->value_labels[] = $this->clean_labels( $columns[0] ); + } } - } - } elseif ( 'first_row' == $this->value_labels_position ) { - foreach ( (array) $this->data[0] as $column ) { - if ( '' != trim( (string) $column ) ) { - $this->value_labels[] = $this->clean_labels( $column ); + break; + + case self::LABELS_FIRST_ROW: + foreach ( (array) $this->data[0] as $column ) { + if ( '' != trim( (string) $column ) ) { + $this->value_labels[] = $this->clean_labels( $column ); + } } - } - } elseif ( 'both' == $this->value_labels_position ) { - foreach ( (array) $this->data as $columns ) { - if ( '' != trim( (string) $columns[0] ) ) { - $this->value_labels['first_column'][] = $this->clean_labels( $columns[0] ); + break; + + case self::LABELS_BOTH: + foreach ( (array) $this->data as $columns ) { + if ( '' != trim( (string) $columns[0] ) ) { + $this->value_labels[ self::LABELS_FIRST_COLUMN ][] = $this->clean_labels( $columns[0] ); + } } - } - foreach ( (array) $this->data[0] as $column ) { - if ( '' != trim( (string) $column ) ) { - $this->value_labels['first_row'][] = $this->clean_labels( $column ); + foreach ( (array) $this->data[0] as $column ) { + if ( '' != trim( (string) $column ) ) { + $this->value_labels[ self::LABELS_FIRST_ROW ][] = $this->clean_labels( $column ); + } } - } + break; } $this->value_labels = apply_filters( 'm_chart_value_labels', $this->value_labels, $this->value_labels_position, $this->data ); @@ -91,190 +90,241 @@ public function parse_value_labels() { * * @return string the position of the labels in the given data set */ - public function get_value_labels_position() { + private function get_value_labels_position(): string { if ( ! isset( $this->data[0][0] ) && ! isset( $this->data[1][0] ) ) { - return 'none'; + return self::LABELS_NONE; } if ( '' == $this->data[0][0] ) { - return 'both'; - } elseif ( - ! is_numeric( $this->clean_data_point( $this->data[0][0], false ) ) - && ! is_numeric( $this->clean_data_point( $this->data[1][0], false ) ) - ) { - return 'first_column'; + return self::LABELS_BOTH; + } elseif ( ! is_numeric( trim( (string) $this->data[0][0] ) ) ) { + // If the first row has multiple non-numeric headers and the data rows start + // with numeric values the entire first row is column labels (e.g. scatter format) + if ( + isset( $this->data[0][1] ) && ! is_numeric( trim( (string) $this->data[0][1] ) ) + && isset( $this->data[1][0] ) && is_numeric( trim( (string) $this->data[1][0] ) ) + ) { + return self::LABELS_FIRST_ROW; + } + + return self::LABELS_FIRST_COLUMN; } - return 'first_row'; + return self::LABELS_FIRST_ROW; } /** - * Helper function cleans data point values and by default records suffix/prefixes to a class var + * Helper function cleans data point values * - * @param string $data_point a data point that may need to be cleaned or typed as an int + * @param mixed $data_point a data point that may need to be cleaned or typed as an int * - * @return int/string an integer of the cleaned data point or string if the cleaned value was not numeric + * @return float|string a float of the cleaned data point or string if the cleaned value was not numeric */ - public function clean_data_point( $data_point ) { + public function clean_data_point( mixed $data_point ): float|string { $data_point = trim( (string) $data_point ); - // Find any prefixes/suffixes in the data - if ( '' == $this->data_prefix && '' == $this->data_suffix ) { - $this->parse_suffix_prefix( $data_point ); - } - - // Remove prefixes and suffixes - $data_point = preg_replace( $this->prefix_patterns, '', $data_point ); - $data_point = preg_replace( $this->suffix_patterns, '', $data_point ); - - // Remove commas - $data_point = str_replace( ',', '', $data_point ); - - // Return value without typing it as an integer if it's not numeric - if ( ! is_numeric( $data_point ) ) { - return trim( $data_point ); + if ( preg_match( '/-?\d[\d,]*(?:\.\d+)?/', $data_point, $matches ) ) { + return floatval( str_replace( ',', '', $matches[0] ) ); } - // By typing it as an integer we prevent json_encode from later treating it as a string - return floatval( trim( $data_point ) ); + return $data_point; } /** - * Checks for any suffix/prefix vales in a data_point + * Helper function parses a data point into an M_Chart_Parsed_Data_Point value object for localized display + * Splits the cell string into prefix, numeric value, and suffix + * This means the number can be reformatted for any locale while preserving surrounding context + * + * @param mixed $data_point a raw cell value + * + * @return M_Chart_Parsed_Data_Point */ - public function parse_suffix_prefix( $data_point ) { - $prefix_patterns = apply_filters( 'm_chart_prefix_patterns', $this->prefix_patterns ); - $suffix_patterns = apply_filters( 'm_chart_suffix_patterns', $this->suffix_patterns ); - - if ( ! $this->data_prefix ) { - foreach ( $prefix_patterns as $pattern ) { - if ( preg_match( $pattern, $data_point, $match ) ) { - $this->data_prefix = $match[0]; - } - } - } + public function parse_data_point( mixed $data_point ): M_Chart_Parsed_Data_Point { + $data_point = trim( (string) $data_point ); - if ( ! $this->data_suffix ) { - foreach ( $suffix_patterns as $pattern ) { - if ( preg_match( $pattern, $data_point, $match ) ) { - $this->data_suffix = $match[0]; - } - } + if ( preg_match( '/(-?\d[\d,]*(?:\.\d+)?)/', $data_point, $matches, PREG_OFFSET_CAPTURE ) ) { + $number = $matches[1][0]; + $offset = $matches[1][1]; + + return M_Chart_Parsed_Data_Point::numeric( + floatval( str_replace( ',', '', $number ) ), + substr( $data_point, 0, $offset ), + substr( $data_point, $offset + strlen( $number ) ) + ); } + + return M_Chart_Parsed_Data_Point::text( $data_point ); } /** * Helper function cleans out label values * - * @param string $label a label string + * @param mixed $label a label string * * @return string the label string cleaned of any problem content */ - public function clean_labels( $label ) { - $label = trim( html_entity_decode( $label, ENT_QUOTES ) ); + public function clean_labels( mixed $label ): string { + $label = trim( html_entity_decode( (string) $label, ENT_QUOTES ) ); return preg_replace( '#<([a-z]+)([^>]+)*(?:>(.*)<\/\1>|\s+\/>)#', '$3', $label ); } /** - * Builds a cleaned and parsed array of data from the post's data + * Populates $this->set_data and $this->raw_data by delegating to the appropriate + * collector based on the parse direction and labels position, then normalizing both arrays */ - public function parse_set_data() { - // Reset these values in case a previous data set has altered them - $this->data_prefix = ''; - $this->data_suffix = ''; - - $set_data_array = array(); - - if ( 'rows' == $this->parse_in && 'first_column' == $this->value_labels_position ) { - foreach ( $this->data as $row ) { - foreach ( $row as $key => $column ) { - if ( '' == $column || 0 == $key ) { - continue; - } + private function parse_set_data(): void { + if ( self::PARSE_ROWS == $this->parse_in && self::LABELS_FIRST_COLUMN == $this->value_labels_position ) { + [ $set_data_array, $raw_data_array ] = $this->collect_rows_first_column(); + } elseif ( self::PARSE_ROWS == $this->parse_in && self::LABELS_BOTH == $this->value_labels_position ) { + [ $set_data_array, $raw_data_array ] = $this->collect_rows_both(); + } elseif ( self::PARSE_COLUMNS == $this->parse_in && self::LABELS_BOTH == $this->value_labels_position ) { + [ $set_data_array, $raw_data_array ] = $this->collect_columns_both(); + } else { + [ $set_data_array, $raw_data_array ] = $this->collect_default(); + } - $data_point = $this->clean_data_point( $column ); + $set_data_array = $this->normalize_data_array( $set_data_array ); + $raw_data_array = $this->normalize_data_array( $raw_data_array ); - $set_data_array[] = $data_point; - } - } - } elseif ( 'rows' == $this->parse_in && 'both' == $this->value_labels_position ) { - $limit = count( $this->data ); - $this_sets = array(); - - // Collect the sets of data - for ( $i = 1; $i < $limit; $i++ ) { - foreach ( $this->data[ $i ] as $c_key => $column ) { - if ( 0 != $c_key ) { - $data_point = $this->clean_data_point( $column ); - $key = $i - 1; - - if ( ! isset( $this_sets[ $key ]['is_null'] ) ) { - $this_sets[ $key ]['is_null'] = true; - } - - if ( is_numeric( $data_point ) ) { - $this_sets[ $key ]['is_null'] = false; - } - - $this_sets[ $key ]['data'][] = $data_point; - } - } - } + $this->set_data = apply_filters( 'm_chart_set_data', $set_data_array, $this->data, $this->parse_in ); + $this->raw_data = apply_filters( 'm_chart_raw_data', $raw_data_array, $this->data, $this->parse_in ); + } - // Compile the sets of data - foreach ( $this_sets as $key => $set ) { - if ( false == $set['is_null'] ) { - $set_data_array[ $key ] = $set['data']; + /** + * Collects data when parsing rows with labels in the first column + * + * @return array{0: array, 1: array} Two-element array of [set_data, raw_data] + */ + private function collect_rows_first_column(): array { + $set_data_array = []; + $raw_data_array = []; + + foreach ( $this->data as $row ) { + foreach ( $row as $key => $column ) { + if ( '' == $column || 0 == $key ) { + continue; } + + $set_data_array[] = $this->clean_data_point( $column ); + $raw_data_array[] = $this->parse_data_point( $column ); } - } elseif ( 'columns' == $this->parse_in && 'both' == $this->value_labels_position ) { - $limit = count( $this->data ); - $this_sets = array(); - - for ( $i = 1; $i < $limit; $i++ ) { - foreach ( $this->data[ $i ] as $key => $column ) { - if ( 0 == $key ) { - continue; - } + } + + return [ $set_data_array, $raw_data_array ]; + } + /** + * Collects data when parsing rows with labels in both the first row and first column + * + * @return array{0: array, 1: array} Two-element array of [set_data, raw_data] + */ + private function collect_rows_both(): array { + $set_data_array = []; + $raw_data_array = []; + $limit = count( $this->data ); + $this_sets = []; + $this_raw = []; + + for ( $i = 1; $i < $limit; $i++ ) { + foreach ( $this->data[ $i ] as $c_key => $column ) { + if ( 0 != $c_key ) { $data_point = $this->clean_data_point( $column ); - $a_key = $key - 1; + $key = $i - 1; - if ( ! isset( $this_sets[ $a_key ]['is_null'] ) ) { - $this_sets[ $a_key ]['is_null'] = true; + if ( ! isset( $this_sets[ $key ]['is_null'] ) ) { + $this_sets[ $key ]['is_null'] = true; } if ( is_numeric( $data_point ) ) { - $this_sets[ $a_key ]['is_null'] = false; + $this_sets[ $key ]['is_null'] = false; } - $this_sets[ $a_key ]['data'][] = $data_point; + $this_sets[ $key ]['data'][] = $data_point; + $this_raw[ $key ][] = $this->parse_data_point( $column ); } } + } - foreach ( $this_sets as $key => $set ) { - if ( false == $set['is_null'] ) { - $set_data_array[ $key ] = $set['data']; - } + foreach ( $this_sets as $key => $set ) { + if ( false == $set['is_null'] ) { + $set_data_array[ $key ] = $set['data']; + $raw_data_array[ $key ] = $this_raw[ $key ]; } - } elseif ( isset( $this->data[1] ) ) { - foreach ( $this->data as $key => $columns ) { - foreach ( $columns as $column ) { - if ( '' == $column || 0 == $key ) { - continue; - } + } - $data_point = $this->clean_data_point( $column ); + return [ $set_data_array, $raw_data_array ]; + } + + /** + * Collects data when parsing columns with labels in both the first row and first column + * + * @return array{0: array, 1: array} Two-element array of [set_data, raw_data] + */ + private function collect_columns_both(): array { + $set_data_array = []; + $raw_data_array = []; + $limit = count( $this->data ); + $this_sets = []; + $this_raw = []; + + for ( $i = 1; $i < $limit; $i++ ) { + foreach ( $this->data[ $i ] as $key => $column ) { + if ( 0 == $key ) { + continue; + } + + $data_point = $this->clean_data_point( $column ); + $a_key = $key - 1; - $set_data_array[] = $data_point; + if ( ! isset( $this_sets[ $a_key ]['is_null'] ) ) { + $this_sets[ $a_key ]['is_null'] = true; } + + if ( is_numeric( $data_point ) ) { + $this_sets[ $a_key ]['is_null'] = false; + } + + $this_sets[ $a_key ]['data'][] = $data_point; + $this_raw[ $a_key ][] = $this->parse_data_point( $column ); } } - $set_data_array = $this->normalize_data_array( $set_data_array ); + foreach ( $this_sets as $key => $set ) { + if ( false == $set['is_null'] ) { + $set_data_array[ $key ] = $set['data']; + $raw_data_array[ $key ] = $this_raw[ $key ]; + } + } - $this->set_data = apply_filters( 'm_chart_set_data', $set_data_array, $this->data, $this->parse_in ); + return [ $set_data_array, $raw_data_array ]; + } + + /** + * Collects data for the default case (first-row labels only, or no labels) + * + * @return array{0: array, 1: array} Two-element array of [set_data, raw_data] + */ + private function collect_default(): array { + $set_data_array = []; + $raw_data_array = []; + + if ( ! isset( $this->data[1] ) ) { + return [ $set_data_array, $raw_data_array ]; + } + + foreach ( $this->data as $key => $columns ) { + foreach ( $columns as $column ) { + if ( '' == $column || 0 == $key ) { + continue; + } + + $set_data_array[] = $this->clean_data_point( $column ); + $raw_data_array[] = $this->parse_data_point( $column ); + } + } + + return [ $set_data_array, $raw_data_array ]; } /** @@ -284,9 +334,10 @@ public function parse_set_data() { * * @return array a normalized array of parsed data */ - public function normalize_data_array( $data_array ) { - if ( 'rows' == $this->parse_in && 'both' == $this->value_labels_position ) { - $label_count = is_array( $this->value_labels['first_row'] ) ? count( $this->value_labels['first_row'] ) - 1 : 0; + private function normalize_data_array( array $data_array ): array { + if ( self::PARSE_ROWS == $this->parse_in && self::LABELS_BOTH == $this->value_labels_position ) { + $first_row_labels = $this->value_labels[ self::LABELS_FIRST_ROW ] ?? []; + $label_count = is_array( $first_row_labels ) ? count( $first_row_labels ) - 1 : 0; foreach ( $data_array as $key => $data ) { foreach ( $data as $t_key => $value ) { @@ -295,8 +346,9 @@ public function normalize_data_array( $data_array ) { } } } - } elseif ( 'columns' == $this->parse_in && 'both' == $this->value_labels_position ) { - $label_count = is_array( $this->value_labels['first_column'] ) ? count( $this->value_labels['first_column'] ) - 1 : 0; + } elseif ( self::PARSE_COLUMNS == $this->parse_in && self::LABELS_BOTH == $this->value_labels_position ) { + $first_col_labels = $this->value_labels[ self::LABELS_FIRST_COLUMN ] ?? []; + $label_count = is_array( $first_col_labels ) ? count( $first_col_labels ) - 1 : 0; foreach ( $data_array as $key => $data ) { foreach ( $data as $t_key => $value ) { @@ -306,16 +358,43 @@ public function normalize_data_array( $data_array ) { } } } - //else { - // $label_count = count( $this->value_labels ) - 1; - // - // foreach ( $data_array as $key => $data ) { - // if ( $key > $label_count ) { - // unset( $data_array[ $key ] ); - // } - // } - //} return $data_array; } + + /** + * Formats an M_Chart_Parsed_Data_Point for table display + * Numeric cells are formatted with the locale-aware NumberFormatter non-numeric cells are returned as plain text + * + * @param ?M_Chart_Parsed_Data_Point $raw the parsed data point to format, or null for an empty cell + * + * @return string the formatted cell value + */ + public function format_raw( ?M_Chart_Parsed_Data_Point $raw ): string { + if ( null === $raw ) { + return ''; + } + + // If the value is a number return the formatted and prefixed/suffixed version of it + if ( $raw->is_numeric() ) { + $formatter = $this->get_formatter(); + $number = $formatter ? $formatter->format( $raw->value ) : (string) $raw->value; + return $raw->prefix . $number . $raw->suffix; + } + + return $raw->text; + } + + /** + * Returns a locale-aware NumberFormatter, creating and caching it on first use + * + * @return ?NumberFormatter a NumberFormatter instance, or null if the intl extension is unavailable + */ + private function get_formatter(): ?NumberFormatter { + if ( null === $this->formatter ) { + $locale = m_chart()->get_settings( 'locale' ); + $this->formatter = class_exists( 'NumberFormatter' ) ? new NumberFormatter( $locale, NumberFormatter::DECIMAL ) : null; + } + return $this->formatter; + } } diff --git a/components/class-m-chart-parsed-data-point.php b/components/class-m-chart-parsed-data-point.php new file mode 100644 index 0000000..6430114 --- /dev/null +++ b/components/class-m-chart-parsed-data-point.php @@ -0,0 +1,57 @@ +value = $value; + $this->prefix = $prefix; + $this->suffix = $suffix; + $this->text = $text; + } + + /** + * Creates a numeric data point with optional surrounding context. + * + * @param float $value The extracted numeric value. + * @param string $prefix Everything before the number in the original string (e.g. "$"). + * @param string $suffix Everything after the number in the original string (e.g. "°F"). + * + * @return self + */ + public static function numeric( float $value, string $prefix = '', string $suffix = '' ): self { + return new self( $value, $prefix, $suffix, '' ); + } + + /** + * Creates a non-numeric data point (e.g. "N/A", empty cell). + * + * @param string $text The original cell string. + * + * @return self + */ + public static function text( string $text = '' ): self { + return new self( null, '', '', $text ); + } + + /** + * Returns true if this data point contains a numeric value. + * + * @return bool + */ + public function is_numeric(): bool { + return null !== $this->value; + } +} diff --git a/components/class-m-chart.php b/components/class-m-chart.php index d67dae6..a41d5a8 100755 --- a/components/class-m-chart.php +++ b/components/class-m-chart.php @@ -1,10 +1,10 @@ 'chartjs', 'type' => 'line', 'parse_in' => 'rows', @@ -21,23 +21,23 @@ class M_Chart { 'legend' => true, 'source' => '', 'source_url' => '', - 'data' => array(), - 'set_names' => array(), - ); - public $get_chart_default_args = array( + 'data' => [], + 'set_names' => [], + ]; + public $get_chart_default_args = [ 'show' => 'chart', 'width' => 'responsive', 'share' => '', - ); - public $parse_options = array( + ]; + public $parse_options = [ 'columns', 'rows', - ); + ]; public $options_set; public $plugin_url; public $is_iframe = false; public $instance = 1; - public $settings = array( + public $settings = [ 'library' => 'chartjs', 'show_library' => 'no', 'performance' => 'default', @@ -47,34 +47,22 @@ class M_Chart { 'default_theme' => '_default', 'locale' => 'en-US', 'csv_delimiter' => ',', - 'lang_settings' => array( - 'decimalPoint' => '.', - 'thousandsSep' => ',', - 'numericSymbols' => array( - 'K', // Thousands - 'M', // Millions - 'B', // Billions - 'T', // Trillions - 'P', // Quadrillions - 'E', // Quintillions - ), - 'numericSymbolMagnitude' => 1000, - ), - ); - public $csv_delimiters = array( + ]; + public $csv_delimiters = [ ',' => 'Comma', "\t" => 'Tab', ' ' => 'Space', ';' => 'Semicolon', - ); + ]; public $library_class; + public $icon = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjY0IDY0IDI0IDI0IiB3aWR0aD0iMjRweCIgaGVpZ2h0PSIyNHB4Ij4KICA8cGF0aCBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik0gNzYuNjU3IDY1LjUgTCA3Ni42NTcgNzUuMzQ0IEwgODYuNSA3NS4zNDQgQyA4Ni41IDY5LjkwOSA4Mi4wOTEgNjUuNSA3Ni42NTcgNjUuNSBaIE0gNzQuNjg4IDc2LjMyOCBMIDc0LjY4OCA2Ni44MzMgQyA2OS41NTcgNjcuMTc0IDY1LjUgNzEuNDM5IDY1LjUgNzYuNjU3IEMgNjUuNSA4Mi4wOTEgNjkuOTA5IDg2LjUgNzUuMzQzIDg2LjUgQyA4MC41NjIgODYuNSA4NC44MjggODIuNDQzIDg1LjE2NyA3Ny4zMTMgTCA3NC42ODggNzcuMzEzIEwgNzQuNjg4IDc2LjMyOCBaIi8+Cjwvc3ZnPg=='; private $admin; private $parse; private $block; - private $libraries = array( + private $libraries = [ 'chartjs' => 'Chart.js', - ); + ]; /** * Constructor @@ -82,23 +70,23 @@ class M_Chart { public function __construct() { $this->plugin_url = $this->plugin_url(); - add_action( 'init', array( $this, 'init' ) ); - add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) ); - add_action( 'save_post', array( $this, 'save_post' ) ); - add_action( 'shortcode_ui_before_do_shortcode', array( $this, 'shortcode_ui_before_do_shortcode' ) ); + add_action( 'init', [ $this, 'init' ] ); + add_action( 'plugins_loaded', [ $this, 'plugins_loaded' ] ); + add_action( 'save_post', [ $this, 'save_post' ] ); + add_action( 'shortcode_ui_before_do_shortcode', [ $this, 'shortcode_ui_before_do_shortcode' ] ); // Doing this early as possible because it sets is_iframe which we might need to use for other things - add_action( 'template_redirect', array( $this, 'template_redirect' ), 0 ); - add_action( 'm_chart_update_post_meta', array( $this, 'm_chart_update_post_meta' ), 10, 2 ); + add_action( 'template_redirect', [ $this, 'template_redirect' ], 0 ); + add_action( 'm_chart_update_post_meta', [ $this, 'm_chart_update_post_meta' ], 10, 2 ); // Doing this before the default so it's already done before anything else - add_filter( 'm_chart_get_chart_image_tag', array( $this, 'm_chart_get_chart_image_tag' ), 9, 3 ); - add_filter( 'the_content', array( $this, 'the_content' ) ); - add_filter( 'm_chart_image_support', array( $this, 'm_chart_image_support' ), 10, 2 ); - add_filter( 'm_chart_instant_preview_support', array( $this, 'm_chart_instant_preview_support' ), 10, 2 ); - add_filter( 'm_chart_library_class', array( $this, 'm_chart_library_class' ), 10, 2 ); - - add_shortcode( 'chart', array( $this, 'chart_shortcode' ) ); - add_shortcode( 'chart-share', array( $this, 'share_shortcode' ) ); + add_filter( 'm_chart_get_chart_image_tag', [ $this, 'm_chart_get_chart_image_tag' ], 9, 3 ); + add_filter( 'the_content', [ $this, 'the_content' ] ); + add_filter( 'm_chart_image_support', [ $this, 'm_chart_image_support' ], 10, 2 ); + add_filter( 'm_chart_instant_preview_support', [ $this, 'm_chart_instant_preview_support' ], 10, 2 ); + add_filter( 'm_chart_library_class', [ $this, 'm_chart_library_class' ], 10, 2 ); + + add_shortcode( 'chart', [ $this, 'chart_shortcode' ] ); + add_shortcode( 'chart-share', [ $this, 'share_shortcode' ] ); // Initiate the block class $this->block(); @@ -144,6 +132,7 @@ public function library( $library = false ) { */ public function parse() { if ( ! $this->parse ) { + require_once __DIR__ . '/class-m-chart-parsed-data-point.php'; require_once __DIR__ . '/class-m-chart-parse.php'; $this->parse = new M_Chart_Parse(); } @@ -158,10 +147,10 @@ public function init() { // Register the units taxonomy register_taxonomy( $this->slug . '-units', - array( $this->slug ), - array( + [ $this->slug ], + [ 'hierarchical' => true, - 'labels' => array( + 'labels' => [ 'name' => esc_html__( 'Chart Units', 'm-chart' ), 'singular_name' => esc_html__( 'Chart Unit', 'm-chart' ), 'search_items' => esc_html__( 'Search Chart Units', 'm-chart' ), @@ -173,34 +162,34 @@ public function init() { 'add_new_item' => esc_html__( 'Add New Chart Unit', 'm-chart' ), 'new_item_name' => esc_html__( 'Chart Unit Name', 'm-chart' ), 'menu_name' => esc_html__( 'Chart Units', 'm-chart' ), - ), + ], 'show_ui' => true, 'query_var' => true, - 'rewrite' => array( + 'rewrite' => [ 'slug' => $this->slug . '-units', - ), - ) + ], + ] ); // Register the library taxonomy register_taxonomy( $this->slug . '-library', - array( $this->slug ), - array( + [ $this->slug ], + [ 'hierarchical' => false, 'show_ui' => false, 'query_var' => true, - 'rewrite' => array( + 'rewrite' => [ 'slug' => $this->slug . '-library', - ), - ) + ], + ] ); // Register the charts custom post type register_post_type( $this->slug, - array( - 'labels' => array( + [ + 'labels' => [ 'name' => esc_html__( 'Charts', 'm-chart' ), 'singular_name' => esc_html__( 'Chart', 'm-chart' ), 'add_new' => esc_html__( 'Add Chart', 'm-chart' ), @@ -213,59 +202,59 @@ public function init() { 'search_items' => esc_html__( 'Search Charts', 'm-chart' ), 'not_found' => esc_html__( 'No Charts found', 'm-chart' ), 'not_found_in_trash' => esc_html__( 'No Charts found in the Trash', 'm-chart' ), - ), - 'register_meta_box_cb' => is_admin() ? array( $this->admin(), 'meta_boxes' ) : null, + ], + 'register_meta_box_cb' => is_admin() ? [ $this->admin(), 'meta_boxes' ] : null, 'public' => true, 'show_in_rest' => true, 'hierarchical' => false, 'exclude_from_search' => false, 'menu_position' => 9, - 'menu_icon' => 'dashicons-chart-pie', + 'menu_icon' => 'data:image/svg+xml;base64,' . $this->icon, 'query_var' => true, 'can_export' => true, 'has_archive' => 'charts', 'description' => esc_html__( 'Manage data sets and display them as charts in WordPress.', 'm-chart' ), - 'rewrite' => array( + 'rewrite' => [ 'slug' => 'chart', - ), - 'supports' => array( + ], + 'supports' => [ 'author', 'title', 'excerpt', 'comments', - ), - 'taxonomies' => array( + ], + 'taxonomies' => [ 'category', 'post_tag', $this->slug . '-units', - ), - ) + ], + ] ); // Register the graphing library scripts wp_register_script( - 'chartjs-helpers', - $this->plugin_url . '/components/js/m-chart-chartjs-helpers.min.js', - array( 'jquery' ), + 'chartjs', + $this->plugin_url . '/components/external/chartjs/chart.js', + [ 'jquery' ], $this->version ); wp_register_script( - 'chartjs', - $this->plugin_url . '/components/external/chartjs/chart.js', - array( 'jquery', 'chartjs-helpers' ), + 'chartjs-helper', + $this->plugin_url . '/components/js/m-chart-chartjs-helper.min.js', + [ 'chartjs' ], $this->version ); wp_register_script( 'chartjs-datalabels', $this->plugin_url . '/components/external/chartjs/chartjs-plugin-datalabels.js', - array( 'jquery', 'chartjs' ), + [ 'chartjs' ], $this->version ); // jQuery needs to be in the header since the charts are inline - wp_enqueue_script( 'jquery', false, array(), false, false ); + wp_enqueue_script( 'jquery', false, [], false, false ); // Add endpoint needed for iframe embed support add_rewrite_endpoint( 'embed', EP_PERMALINK ); @@ -290,7 +279,7 @@ public function plugins_loaded() { } /** - * VIP's CDN was breaking Highcharts ability to handle embedded SVGs so this should circumvent that + * WP VIP's CDN was breaking Highcharts ability to handle embedded SVGs so this should circumvent that * If you wanted to say, watermark your charts, SVGs suddenly become very important * * @param string $path option additional path to be used (e.g. components) @@ -322,7 +311,7 @@ public function plugin_url( $path = '' ) { * @param int $post_id WP post ID of the post you want post meta from * @param string $field optional field to be returend instead of all post meta * - * @return string URL to the plugin directory with path if parameter was passed + * @return array $post_meta the meta for a give post ID */ public function get_post_meta( $post_id, $field = false ) { $raw_post_meta = get_post_meta( $post_id, $this->slug, true ); @@ -371,13 +360,13 @@ public function get_post_meta( $post_id, $field = false ) { // If the data has the old legacy format we need to update it if ( isset( $post_meta['data'] ) && ! isset( $post_meta['data']['sets'] ) ) { $data = $post_meta['data']; - $post_meta['data'] = array(); + $post_meta['data'] = []; $post_meta['data']['sets'][] = $data; } // If there's no set_names value set we'll set it to an empty array if ( ! isset( $post_meta['set_names'] ) ) { - $post_meta['set_names'] = array(); + $post_meta['set_names'] = []; } $post_meta = apply_filters( 'm_chart_get_post_meta', $post_meta, $raw_post_meta, $post_id ); @@ -402,7 +391,7 @@ public function update_post_meta( $post_id, $meta ) { $parsed_meta = $this->validate_post_meta( $meta ); // Set unit terms - $terms = array(); + $terms = []; if ( '' != $parsed_meta['y_units'] ) { $terms[] = $parsed_meta['y_units']; @@ -444,7 +433,7 @@ public function validate_post_meta( $meta ) { $chart_meta[ $field ]['sets'] = $meta[ $field ]; } elseif ( 'set_names' == $field ) { $chart_meta[ $field ] = array_values( $meta[ $field ] ); - } elseif ( in_array( $field, array( 'labels', 'y_min', 'legend' ) ) ) { + } elseif ( in_array( $field, [ 'labels', 'y_min', 'legend' ] ) ) { $chart_meta[ $field ] = (bool) $meta[ $field ]; } elseif ( 'height' == $field ) { $chart_meta[ $field ] = absint( $meta[ $field ] ); @@ -470,7 +459,7 @@ public function validate_post_meta( $meta ) { $chart_meta['theme'] = $meta['theme']; } - // If the data value is not an array we asume it is JSON encoded (i.e. from Jspreadsheet) + // If the data value is not an array we asume it is JSON encoded (i.e. from Jspreadsheet CE) if ( ! is_array( $chart_meta['data']['sets'] ) && '' != $chart_meta['data']['sets'] ) { $chart_meta['data']['sets'] = json_decode( stripslashes( $chart_meta['data']['sets'] ) ); } @@ -529,7 +518,7 @@ public function save_post( $post_id ) { * * @return string HTML and Javascript needed to display a chart (or if appropriate an HTML image tag) */ - public function get_chart( $post_id = null, $args = array() ) { + public function get_chart( $post_id = null, $args = [] ) { if ( ! $post_id ) { $post_id = get_the_ID(); } @@ -607,6 +596,7 @@ class=""> // If we haven't enqueued the right library yet lets do it if ( ! wp_script_is( $library, 'enqueued' ) ) { wp_enqueue_script( $library ); + wp_enqueue_script( 'chartjs-helper' ); } $template = __DIR__ . '/templates/' . $library . '-chart.php'; @@ -673,13 +663,13 @@ public function get_chart_image( $post_id ) { return false; } - return array( + return [ 'url' => $thumbnail[0], 'file' => basename( $thumbnail[0] ), 'width' => $thumbnail[1], 'height' => $thumbnail[2], 'name' => get_the_title( $thumbnail_id ), - ); + ]; } /** @@ -697,13 +687,13 @@ public function m_chart_get_chart_image_tag( $img, $post_id ) { $url = $this->plugin_url . '/components/images/chart-placeholder.png'; - return array( + return [ 'url' => $url, 'file' => basename( $url ), 'width' => 640, 'height' => 480, 'name' => get_the_title( $post_id ), - ); + ]; } /** @@ -786,7 +776,7 @@ public function m_chart_library_class( $library_class, $library ) { * * @return string HTML needed to display a chart via an iframe */ - public function get_chart_iframe( $post_id, $args = array() ) { + public function get_chart_iframe( $post_id, $args = [] ) { $post_meta = $this->get_post_meta( $post_id ); $src_url = add_query_arg( $args, get_permalink( $post_id ) . 'embed/' ); @@ -857,7 +847,7 @@ public function unicode_aware_stripslashes( $string ) { * @return array an array of generated and/or compiled unit terms */ public function get_unit_terms() { - $terms = get_terms( $this->slug . '-units', array( 'hide_empty' => false ) ); + $terms = get_terms( $this->slug . '-units', [ 'hide_empty' => false ] ); if ( empty( $terms ) ) { $terms = $this->generate_unit_terms(); @@ -874,8 +864,8 @@ public function get_unit_terms() { * @return array an array of compiled unit terms */ public function compile_unit_terms( $terms ) { - $compiled_terms = array(); - $parents = array(); + $compiled_terms = []; + $parents = []; foreach ( $terms as $unit ) { if ( 0 == $unit->parent ) { @@ -903,14 +893,14 @@ public function generate_unit_terms() { // Load the default terms array $default_terms = require __DIR__ . '/array-default-unit-terms.php'; - $terms = array(); + $terms = []; foreach ( $default_terms as $parent_term => $child_terms ) { $parent = (object) wp_insert_term( $parent_term, $this->slug . '-units' ); $terms[] = get_term( $parent->term_id, $this->slug . '-units' ); foreach ( $child_terms as $child_term ) { - $term = (object) wp_insert_term( $child_term, $this->slug . '-units', array( 'parent' => $parent->term_id ) ); + $term = (object) wp_insert_term( $child_term, $this->slug . '-units', [ 'parent' => $parent->term_id ] ); $terms[] = get_term( $term->term_id, $this->slug . '-units' ); } } @@ -935,7 +925,7 @@ public function template_redirect() { wp_die( esc_html__( 'The chart could not be found', 'm-chart' ), esc_html__( 'Chart not found', 'm-chart' ), - array( 'response' => 404 ) + [ 'response' => 404 ] ); } @@ -945,17 +935,17 @@ public function template_redirect() { wp_die( esc_html__( 'Embeds of this type are not enabled', 'm-chart' ), esc_html__( 'Embeds disabled', 'm-chart' ), - array( 'response' => 403 ) + [ 'response' => 403 ] ); exit; } $this->is_iframe = true; - $scripts = array( + $scripts = [ 'jquery', $this->get_post_meta( $post->ID, 'library' ), - ); + ]; unset( $_GET['action'], $_GET['share'] ); @@ -1020,9 +1010,6 @@ public function get_settings( $setting = false ) { $settings = (array) get_option( $this->slug, $default_settings ); $settings = wp_parse_args( $settings, $default_settings ); - // Make sure the lang_settings aren't missing anything we'll be expecting later on - $settings['lang_settings'] = wp_parse_args( $settings['lang_settings'], $this->settings['lang_settings'] ); - // Make sure the set library is still valid if ( ! $this->is_valid_library( $settings['library'] ) ) { $settings['library'] = 'chartjs'; @@ -1103,19 +1090,19 @@ public function array_merge_recursive( &$a, $b ) { public function upgrade_to_1_7() { // Get all charts $charts = get_posts( - array( + [ 'post_type' => m_chart()->slug, 'posts_per_page' => -1, 'post_status' => 'any', - 'tax_query' => array( - array( + 'tax_query' => [ + [ 'taxonomy' => 'post_tag', 'field' => 'slug', 'terms' => 'highcharts', 'operator' => 'NOT IN', - ), - ), - ) + ], + ], + ] ); // Add highcharts post_tag to all charts @@ -1133,19 +1120,19 @@ public function upgrade_to_1_7() { public function upgrade_to_1_7_4() { // Get all charts tagged with highcharts $highcharts_charts = get_posts( - array( + [ 'post_type' => m_chart()->slug, 'posts_per_page' => -1, 'post_status' => 'any', - 'tax_query' => array( - array( + 'tax_query' => [ + [ 'taxonomy' => 'post_tag', 'field' => 'slug', 'terms' => 'highcharts', 'operator' => 'IN', - ), - ), - ) + ], + ], + ] ); foreach ( $highcharts_charts as $chart ) { @@ -1157,19 +1144,19 @@ public function upgrade_to_1_7_4() { // Get all charts tagged with chartjs $chartjs_charts = get_posts( - array( + [ 'post_type' => m_chart()->slug, 'posts_per_page' => -1, 'post_status' => 'any', - 'tax_query' => array( - array( + 'tax_query' => [ + [ 'taxonomy' => 'post_tag', 'field' => 'slug', 'terms' => 'chartjs', 'operator' => 'IN', - ), - ), - ) + ], + ], + ] ); foreach ( $chartjs_charts as $chart ) { diff --git a/components/css/m-chart-admin.css b/components/css/m-chart-admin.css index 21179fc..e4833b6 100644 --- a/components/css/m-chart-admin.css +++ b/components/css/m-chart-admin.css @@ -113,6 +113,9 @@ #m-chart-spreadsheet { border-left: none; } +#m-chart-spreadsheet.closed { + border-bottom: 0.0625rem solid #cdd0d4; +} #m-chart-spreadsheet .postbox-header { border-bottom: none; } @@ -237,9 +240,12 @@ #m-chart-spreadsheet .nav-tab-wrapper .nav-tab:last-of-type { margin-right: 0.625rem; } -#m-chart-spreadsheet .nav-tab-wrapper .nav-tab.do-not-delete span { +#m-chart-spreadsheet .nav-tab-wrapper .nav-tab.do-not-delete span.dashicons-dismiss { display: none; } +#m-chart-spreadsheet .nav-tab-wrapper .nav-tab.do-not-delete { + padding-left: 0.625rem; +} #m-chart-spreadsheet #spreadsheets .jtabs-content { border-left: 1px solid #cdd0d4; overflow: auto; @@ -252,17 +258,17 @@ #m-chart-spreadsheet #spreadsheets .jtabs-content .jss_content table { margin-right: -0.0625rem; } +#m-chart-spreadsheet #m-chart-csv::after { + clear: both; + content: ""; + display: table; +} #m-chart-spreadsheet #m-chart-csv { background-color: #f5f5f5; border-left: 0.0625rem solid #cdd0d4; border-top: 0.0625rem solid #dddddd; padding: 0.9375rem 0.625rem 0.625rem; } -#m-chart-spreadsheet #m-chart-csv::after { - clear: both; - content: ""; - display: table; -} #m-chart-spreadsheet #m-chart-csv::after { clear: both; } @@ -285,6 +291,7 @@ #m-chart-spreadsheet #m-chart-csv div.confirmation select { margin-top: 0.375rem; margin-bottom: 0.25rem; + margin-left: 0.4375rem; } @media (max-width: 600px) { #m-chart-spreadsheet #m-chart-csv div.confirmation select { @@ -325,15 +332,15 @@ border-top: 1px solid #dddddd; padding: 0 0.625rem 0.625rem; } -#m-chart .inside .settings .row { - border-top: none; - padding: 0; -} #m-chart .inside .settings .row::after { clear: both; content: ""; display: table; } +#m-chart .inside .settings .row { + border-top: none; + padding: 0; +} #m-chart .inside .settings .row p { margin-bottom: 0; } @@ -384,15 +391,15 @@ #m-chart .inside .settings .row.six p:first-child { float: left; } -#m-chart .inside .row { - border-top: 1px solid #dddddd; - padding: 0 0.625rem 0.625rem; -} #m-chart .inside .row::after { clear: both; content: ""; display: table; } +#m-chart .inside .row { + border-top: 1px solid #dddddd; + padding: 0 0.625rem 0.625rem; +} #m-chart .inside .row.seven p { float: right; margin-bottom: 0; @@ -401,6 +408,23 @@ #m-chart .inside .row.seven p:first-child { float: left; } +#m-chart .inside .row.seven p.shortcode { + position: relative; +} +#m-chart .inside .row.seven p.shortcode .button { + bottom: 0.1875rem; + line-height: 1.375rem; + min-height: 1.5rem; + position: absolute; + right: 0.125rem; +} +@media (max-width: 782px) { + #m-chart .inside .row.seven p.shortcode .button { + bottom: 0; + line-height: 1.875rem; + min-height: 2rem; + } +} #m-chart .inside .row.seven p.image { position: relative; } diff --git a/components/css/m-chart-admin.css.map b/components/css/m-chart-admin.css.map index 1e4a102..65347b9 100644 --- a/components/css/m-chart-admin.css.map +++ b/components/css/m-chart-admin.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../sass/_misc.scss","../sass/_spreadsheet.scss","../sass/_global-mixins-and-variables.scss","../sass/_chart-settings.scss","../sass/_settings.scss"],"names":[],"mappings":"AAQE;AAAA;AAAA;EACC;;;AAKH;EACC;;;AAIA;EACC;;AAGD;EACC;;;AAKD;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIA;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAIF;EACC;;AAEA;EACC;EACA;;AAIF;EACC;;AAEA;EACC;EACA;;AAIF;EACC;;AAEA;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;;AAOH;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;;;AC/JH;EACC;;AAEA;EACC;;AAGC;EACC;EACA;;AAKH;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAVD;IAWE;;;AAGD;EACC;;AAGD;EACC,OCR4B;EDS5B;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC,OC3Ba;;AD8Bd;EACC;;AAGD;EAjBD;IAkBE;;;AAIF;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAXD;IAYE;;;AAGD;EAfD;IAgBE;IACA;;;AAGD;EACC,OCpD2B;EDqD3B;EACA;EACA;;AAEA;EACC,OCjEU;;ADoEX;EAVD;IAWE;;;AAIF;EACC;EACA;EACA;EACA;;AAEA;EAND;IAOE;IACA;IACA;;;AAGD;EACC;;AAIF;EACC;;AAIA;EACC,OCjGY;;ADmGZ;EACC,OCnGS;;ADwGV;EACC,OC1GW;ED2GX,yBC3GW;;ADgHd;EACC;;AAIA;EACC;;AAOH;EACC;EACA;EACA;;AAEA;EACC;EACA;;AAEA;EACC;;AAMJ;EAEC,kBC3I+B;ED4I/B;EACA;EACA;;AC7KD;EACC;EACA;EACA;;AD4KA;EACC;;AAGD;EACC;;AAEA;EACC;;AAKD;EACC;;AAGD;EACC;;AAGD;EACC;EACA;;AAEA;EACC;EACA;;AAGA;EALD;IAME;;;AAKH;EACC;EACA;;AAIF;AAAA;EAEC,OC1LoB;;AD6LrB;AAAA;EAEC;;AAEA;AAAA;EACC;;AAKD;EACC,OCzMkB;;AD2MlB;EACC,OC3MkB;;;AClCtB;EACC;;AAEA;EACC;;AAGD;EACC,kBDwB8B;ECvB9B;EACA;;AAEA;EAEC;EACA;;ADdH;EACC;EACA;EACA;;ACaE;EACC;;AAIA;EACC;EACA;EACA;;AAEA;EACC;;AAMF;EACC;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAKD;EACC;;AAEA;EACC;;AAQH;EACC;EACA;;AAEA;EACC;EACA;;AAMF;EACC;;AAEA;EACC;EACA;;AAMF;EACC;EACA;;AAEA;EACC;;AAOL;EAEC;EACA;;ADlGF;EACC;EACA;EACA;;ACkGE;EACC;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAEA;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;IACC;IACA;IACA;;;;ACpIN;EACC,OFmC0B;;;AE9B7B;EACC;EACA","file":"m-chart-admin.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../sass/_misc.scss","../sass/_spreadsheet.scss","../sass/_global-mixins-and-variables.scss","../sass/_chart-settings.scss","../sass/_settings.scss"],"names":[],"mappings":"AAQE;AAAA;AAAA;EACC;;;AAKH;EACC;;;AAIA;EACC;;AAGD;EACC;;;AAKD;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIA;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAIF;EACC;;AAEA;EACC;EACA;;AAIF;EACC;;AAEA;EACC;EACA;;AAIF;EACC;;AAEA;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;AAKD;EACC;EACA;;;AAOH;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;;;AC/JH;EACC;;AAEA;EACC;;AAGD;EACC;;AAGC;EACC;EACA;;AAKH;EACC;EACA;;AAGD;EACC;;AAGD;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAVD;IAWE;;;AAGD;EACC;;AAGD;EACC,OC+B4B;ED9B5B;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC,OCYa;;ADTd;EACC;;AAGD;EAjBD;IAkBE;;;AAIF;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAXD;IAYE;;;AAGD;EAfD;IAgBE;IACA;;;AAGD;EACC,OCb2B;EDc3B;EACA;EACA;;AAEA;EACC,OC1BU;;AD6BX;EAVD;IAWE;;;AAIF;EACC;EACA;EACA;EACA;;AAEA;EAND;IAOE;IACA;IACA;;;AAGD;EACC;;AAIF;EACC;;AAIA;EACC,OC1DY;;AD4DZ;EACC,OC5DS;;ADiEV;EACC,OCnEW;EDoEX,yBCpEW;;ADyEd;EACC;;AAIA;EACC;;AAFF;EAKC;;AAMF;EACC;EACA;EACA;;AAEA;EACC;EACA;;AAEA;EACC;;ACxKJ;EACC;EACA;EACA;;AD2KD;EAEC,kBCtG+B;EDuG/B;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAEA;EACC;;AAKD;EACC;;AAGD;EACC;;AAGD;EACC;EACA;;AAEA;EACC;EACA;EACA;;AAGA;EAND;IAOE;;;AAKH;EACC;EACA;;AAIF;AAAA;EAEC,OCtJoB;;ADyJrB;AAAA;EAEC;;AAEA;AAAA;EACC;;AAKD;EACC,OCrKkB;;ADuKlB;EACC,OCvKkB;;;AC7EtB;EACC;;AAEA;EACC;;AAGD;EACC,kBDmE8B;EClE9B;EACA;;ADTF;EACC;EACA;EACA;;ACQC;EAEC;EACA;;AAEA;EACC;;AAIA;EACC;EACA;EACA;;AAEA;EACC;;AAMF;EACC;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAKD;EACC;;AAEA;EACC;;AAQH;EACC;EACA;;AAEA;EACC;EACA;;AAMF;EACC;;AAEA;EACC;EACA;;AAMF;EACC;EACA;;AAEA;EACC;;ADxFN;EACC;EACA;EACA;;AC4FA;EAEC;EACA;;AAGC;EACC;EACA;EACA;;AAEA;EACC;;AAGD;EACC;;AAEA;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;IACC;IACA;IACA;;;AAKH;EACC;;AAEA;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;IACC;IACA;IACA;;;;ACxJN;EACC,OF8E0B;;;AEzE7B;EACC;EACA","file":"m-chart-admin.css"} \ No newline at end of file diff --git a/components/external/canvg/MIT-LICENSE.txt b/components/external/canvg/MIT-LICENSE.txt deleted file mode 100644 index 40f19bd..0000000 --- a/components/external/canvg/MIT-LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2010-2011 Gabe Lerner (gabelerner@gmail.com) - http://code.google.com/p/canvg/ - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/components/external/canvg/umd.js b/components/external/canvg/umd.js deleted file mode 100755 index cb4aa6a..0000000 --- a/components/external/canvg/umd.js +++ /dev/null @@ -1,17 +0,0 @@ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).canvg={})}(this,(function(t){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function r(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function n(t){var e={exports:{}};return t(e,e.exports),e.exports}var i,o,a=function(t){return t&&t.Math==Math&&t},u=a("object"==typeof globalThis&&globalThis)||a("object"==typeof window&&window)||a("object"==typeof self&&self)||a("object"==typeof e&&e)||function(){return this}()||Function("return this")(),s=function(t,e){try{Object.defineProperty(u,t,{value:e,configurable:!0,writable:!0})}catch(r){u[t]=e}return e},c="__core-js_shared__",l=u[c]||s(c,{}),f=n((function(t){(t.exports=function(t,e){return l[t]||(l[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.18.2",mode:"global",copyright:"© 2021 Denis Pushkarev (zloirock.ru)"})})),h=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},p=function(t){return Object(h(t))},v={}.hasOwnProperty,y=Object.hasOwn||function(t,e){return v.call(p(t),e)},d=0,g=Math.random(),m=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++d+g).toString(36)},x=function(t){return"function"==typeof t},b=function(t){return x(t)?t:void 0},w=function(t,e){return arguments.length<2?b(u[t]):u[t]&&u[t][e]},S=w("navigator","userAgent")||"",O=u.process,k=u.Deno,T=O&&O.versions||k&&k.version,A=T&&T.v8;A?o=(i=A.split("."))[0]<4?1:i[0]+i[1]:S&&(!(i=S.match(/Edge\/(\d+)/))||i[1]>=74)&&(i=S.match(/Chrome\/(\d+)/))&&(o=i[1]);var R=o&&+o,P=function(t){try{return!!t()}catch(t){return!0}},E=!!Object.getOwnPropertySymbols&&!P((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&R&&R<41})),C=E&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,M=f("wks"),N=u.Symbol,_=C?N:N&&N.withoutSetter||m,I=function(t){return y(M,t)&&(E||"string"==typeof M[t])||(E&&y(N,t)?M[t]=N[t]:M[t]=_("Symbol."+t)),M[t]},V={};V[I("toStringTag")]="z";var L="[object z]"===String(V),B=!P((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),D=function(t){return"object"==typeof t?null!==t:x(t)},j=u.document,F=D(j)&&D(j.createElement),z=function(t){return F?j.createElement(t):{}},U=!B&&!P((function(){return 7!=Object.defineProperty(z("div"),"a",{get:function(){return 7}}).a})),H=function(t){if(D(t))return t;throw TypeError(String(t)+" is not an object")},X=C?function(t){return"symbol"==typeof t}:function(t){var e=w("Symbol");return x(e)&&Object(t)instanceof e},Y=function(t){try{return String(t)}catch(t){return"Object"}},G=function(t){if(x(t))return t;throw TypeError(Y(t)+" is not a function")},W=function(t,e){var r=t[e];return null==r?void 0:G(r)},q=I("toPrimitive"),$=function(t,e){if(!D(t)||X(t))return t;var r,n=W(t,q);if(n){if(void 0===e&&(e="default"),r=n.call(t,e),!D(r)||X(r))return r;throw TypeError("Can't convert object to primitive value")}return void 0===e&&(e="number"),function(t,e){var r,n;if("string"===e&&x(r=t.toString)&&!D(n=r.call(t)))return n;if(x(r=t.valueOf)&&!D(n=r.call(t)))return n;if("string"!==e&&x(r=t.toString)&&!D(n=r.call(t)))return n;throw TypeError("Can't convert object to primitive value")}(t,e)},Q=function(t){var e=$(t,"string");return X(e)?e:String(e)},Z=Object.defineProperty,K={f:B?Z:function(t,e,r){if(H(t),e=Q(e),H(r),U)try{return Z(t,e,r)}catch(t){}if("get"in r||"set"in r)throw TypeError("Accessors not supported");return"value"in r&&(t[e]=r.value),t}},J=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},tt=B?function(t,e,r){return K.f(t,e,J(1,r))}:function(t,e,r){return t[e]=r,t},et=Function.toString;x(l.inspectSource)||(l.inspectSource=function(t){return et.call(t)});var rt,nt,it,ot=l.inspectSource,at=u.WeakMap,ut=x(at)&&/native code/.test(ot(at)),st=f("keys"),ct=function(t){return st[t]||(st[t]=m(t))},lt={},ft="Object already initialized",ht=u.WeakMap;if(ut||l.state){var pt=l.state||(l.state=new ht),vt=pt.get,yt=pt.has,dt=pt.set;rt=function(t,e){if(yt.call(pt,t))throw new TypeError(ft);return e.facade=t,dt.call(pt,t,e),e},nt=function(t){return vt.call(pt,t)||{}},it=function(t){return yt.call(pt,t)}}else{var gt=ct("state");lt[gt]=!0,rt=function(t,e){if(y(t,gt))throw new TypeError(ft);return e.facade=t,tt(t,gt,e),e},nt=function(t){return y(t,gt)?t[gt]:{}},it=function(t){return y(t,gt)}}var mt={set:rt,get:nt,has:it,enforce:function(t){return it(t)?nt(t):rt(t,{})},getterFor:function(t){return function(e){var r;if(!D(e)||(r=nt(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return r}}},xt=Function.prototype,bt=B&&Object.getOwnPropertyDescriptor,wt=y(xt,"name"),St={EXISTS:wt,PROPER:wt&&"something"===function(){}.name,CONFIGURABLE:wt&&(!B||B&&bt(xt,"name").configurable)},Ot=n((function(t){var e=St.CONFIGURABLE,r=mt.get,n=mt.enforce,i=String(String).split("String");(t.exports=function(t,r,o,a){var c,l=!!a&&!!a.unsafe,f=!!a&&!!a.enumerable,h=!!a&&!!a.noTargetGet,p=a&&void 0!==a.name?a.name:r;x(o)&&("Symbol("===String(p).slice(0,7)&&(p="["+String(p).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),(!y(o,"name")||e&&o.name!==p)&&tt(o,"name",p),(c=n(o)).source||(c.source=i.join("string"==typeof p?p:""))),t!==u?(l?!h&&t[r]&&(f=!0):delete t[r],f?t[r]=o:tt(t,r,o)):f?t[r]=o:s(r,o)})(Function.prototype,"toString",(function(){return x(this)&&r(this).source||ot(this)}))})),kt={}.toString,Tt=function(t){return kt.call(t).slice(8,-1)},At=I("toStringTag"),Rt="Arguments"==Tt(function(){return arguments}()),Pt=L?Tt:function(t){var e,r,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(r=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),At))?r:Rt?Tt(e):"Object"==(n=Tt(e))&&x(e.callee)?"Arguments":n},Et=L?{}.toString:function(){return"[object "+Pt(this)+"]"};L||Ot(Object.prototype,"toString",Et,{unsafe:!0});var Ct={}.propertyIsEnumerable,Mt=Object.getOwnPropertyDescriptor,Nt={f:Mt&&!Ct.call({1:2},1)?function(t){var e=Mt(this,t);return!!e&&e.enumerable}:Ct},_t="".split,It=P((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==Tt(t)?_t.call(t,""):Object(t)}:Object,Vt=function(t){return It(h(t))},Lt=Object.getOwnPropertyDescriptor,Bt={f:B?Lt:function(t,e){if(t=Vt(t),e=Q(e),U)try{return Lt(t,e)}catch(t){}if(y(t,e))return J(!Nt.f.call(t,e),t[e])}},Dt=Math.ceil,jt=Math.floor,Ft=function(t){var e=+t;return e!=e||0===e?0:(e>0?jt:Dt)(e)},zt=Math.max,Ut=Math.min,Ht=function(t,e){var r=Ft(t);return r<0?zt(r+e,0):Ut(r,e)},Xt=Math.min,Yt=function(t){return t>0?Xt(Ft(t),9007199254740991):0},Gt=function(t){return Yt(t.length)},Wt=function(t){return function(e,r,n){var i,o=Vt(e),a=Gt(o),u=Ht(n,a);if(t&&r!=r){for(;a>u;)if((i=o[u++])!=i)return!0}else for(;a>u;u++)if((t||u in o)&&o[u]===r)return t||u||0;return!t&&-1}},qt={includes:Wt(!0),indexOf:Wt(!1)},$t=qt.indexOf,Qt=function(t,e){var r,n=Vt(t),i=0,o=[];for(r in n)!y(lt,r)&&y(n,r)&&o.push(r);for(;e.length>i;)y(n,r=e[i++])&&(~$t(o,r)||o.push(r));return o},Zt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Kt=Zt.concat("length","prototype"),Jt={f:Object.getOwnPropertyNames||function(t){return Qt(t,Kt)}},te={f:Object.getOwnPropertySymbols},ee=w("Reflect","ownKeys")||function(t){var e=Jt.f(H(t)),r=te.f;return r?e.concat(r(t)):e},re=function(t,e){for(var r=ee(e),n=K.f,i=Bt.f,o=0;oo;o++)if((u=d(t[o]))&&u instanceof Ce)return u;return new Ce(!1)}n=Pe(t,i)}for(s=n.next;!(c=s.call(n)).done;){try{u=d(c.value)}catch(t){Ee(n,"throw",t)}if("object"==typeof u&&u&&u instanceof Ce)return u}return new Ce(!1)},Ne=I("iterator"),_e=!1;try{var Ie=0,Ve={next:function(){return{done:!!Ie++}},return:function(){_e=!0}};Ve[Ne]=function(){return this},Array.from(Ve,(function(){throw 2}))}catch(t){}var Le,Be,De,je,Fe=function(t,e){if(!e&&!_e)return!1;var r=!1;try{var n={};n[Ne]=function(){return{next:function(){return{done:r=!0}}}},t(n)}catch(t){}return r},ze=[],Ue=w("Reflect","construct"),He=/^\s*(?:class|function)\b/,Xe=He.exec,Ye=!He.exec((function(){})),Ge=function(t){if(!x(t))return!1;try{return Ue(Object,ze,t),!0}catch(t){return!1}},We=!Ue||P((function(){var t;return Ge(Ge.call)||!Ge(Object)||!Ge((function(){t=!0}))||t}))?function(t){if(!x(t))return!1;switch(Pt(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}return Ye||!!Xe.call(He,ot(t))}:Ge,qe=I("species"),$e=function(t,e){var r,n=H(t).constructor;return void 0===n||null==(r=H(n)[qe])?e:function(t){if(We(t))return t;throw TypeError(Y(t)+" is not a constructor")}(r)},Qe=w("document","documentElement"),Ze=/(?:ipad|iphone|ipod).*applewebkit/i.test(S),Ke="process"==Tt(u.process),Je=u.setImmediate,tr=u.clearImmediate,er=u.process,rr=u.MessageChannel,nr=u.Dispatch,ir=0,or={},ar="onreadystatechange";try{Le=u.location}catch(t){}var ur=function(t){if(or.hasOwnProperty(t)){var e=or[t];delete or[t],e()}},sr=function(t){return function(){ur(t)}},cr=function(t){ur(t.data)},lr=function(t){u.postMessage(String(t),Le.protocol+"//"+Le.host)};Je&&tr||(Je=function(t){for(var e=[],r=arguments.length,n=1;r>n;)e.push(arguments[n++]);return or[++ir]=function(){(x(t)?t:Function(t)).apply(void 0,e)},Be(ir),ir},tr=function(t){delete or[t]},Ke?Be=function(t){er.nextTick(sr(t))}:nr&&nr.now?Be=function(t){nr.now(sr(t))}:rr&&!Ze?(je=(De=new rr).port2,De.port1.onmessage=cr,Be=Te(je.postMessage,je,1)):u.addEventListener&&x(u.postMessage)&&!u.importScripts&&Le&&"file:"!==Le.protocol&&!P(lr)?(Be=lr,u.addEventListener("message",cr,!1)):Be=ar in z("script")?function(t){Qe.appendChild(z("script")).onreadystatechange=function(){Qe.removeChild(this),ur(t)}}:function(t){setTimeout(sr(t),0)});var fr,hr,pr,vr,yr,dr,gr,mr,xr={set:Je,clear:tr},br=/ipad|iphone|ipod/i.test(S)&&void 0!==u.Pebble,wr=/web0s(?!.*chrome)/i.test(S),Sr=Bt.f,Or=xr.set,kr=u.MutationObserver||u.WebKitMutationObserver,Tr=u.document,Ar=u.process,Rr=u.Promise,Pr=Sr(u,"queueMicrotask"),Er=Pr&&Pr.value;Er||(fr=function(){var t,e;for(Ke&&(t=Ar.domain)&&t.exit();hr;){e=hr.fn,hr=hr.next;try{e()}catch(t){throw hr?vr():pr=void 0,t}}pr=void 0,t&&t.enter()},Ze||Ke||wr||!kr||!Tr?!br&&Rr&&Rr.resolve?((gr=Rr.resolve(void 0)).constructor=Rr,mr=gr.then,vr=function(){mr.call(gr,fr)}):vr=Ke?function(){Ar.nextTick(fr)}:function(){Or.call(u,fr)}:(yr=!0,dr=Tr.createTextNode(""),new kr(fr).observe(dr,{characterData:!0}),vr=function(){dr.data=yr=!yr}));var Cr,Mr,Nr,_r,Ir=Er||function(t){var e={fn:t,next:void 0};pr&&(pr.next=e),hr||(hr=e,vr()),pr=e},Vr=function(t){var e,r;this.promise=new t((function(t,n){if(void 0!==e||void 0!==r)throw TypeError("Bad Promise constructor");e=t,r=n})),this.resolve=G(e),this.reject=G(r)},Lr={f:function(t){return new Vr(t)}},Br=function(t){try{return{error:!1,value:t()}}catch(t){return{error:!0,value:t}}},Dr="object"==typeof window,jr=xr.set,Fr=I("species"),zr="Promise",Ur=mt.get,Hr=mt.set,Xr=mt.getterFor(zr),Yr=he&&he.prototype,Gr=he,Wr=Yr,qr=u.TypeError,$r=u.document,Qr=u.process,Zr=Lr.f,Kr=Zr,Jr=!!($r&&$r.createEvent&&u.dispatchEvent),tn=x(u.PromiseRejectionEvent),en="unhandledrejection",rn=!1,nn=ce(zr,(function(){var t=ot(Gr),e=t!==String(Gr);if(!e&&66===R)return!0;if(R>=51&&/native code/.test(t))return!1;var r=new Gr((function(t){t(1)})),n=function(t){t((function(){}),(function(){}))};return(r.constructor={})[Fr]=n,!(rn=r.then((function(){}))instanceof n)||!e&&Dr&&!tn})),on=nn||!Fe((function(t){Gr.all(t).catch((function(){}))})),an=function(t){var e;return!(!D(t)||!x(e=t.then))&&e},un=function(t,e){if(!t.notified){t.notified=!0;var r=t.reactions;Ir((function(){for(var n=t.value,i=1==t.state,o=0;r.length>o;){var a,u,s,c=r[o++],l=i?c.ok:c.fail,f=c.resolve,h=c.reject,p=c.domain;try{l?(i||(2===t.rejection&&fn(t),t.rejection=1),!0===l?a=n:(p&&p.enter(),a=l(n),p&&(p.exit(),s=!0)),a===c.promise?h(qr("Promise-chain cycle")):(u=an(a))?u.call(a,f,h):f(a)):h(n)}catch(t){p&&!s&&p.exit(),h(t)}}t.reactions=[],t.notified=!1,e&&!t.rejection&&cn(t)}))}},sn=function(t,e,r){var n,i;Jr?((n=$r.createEvent("Event")).promise=e,n.reason=r,n.initEvent(t,!1,!0),u.dispatchEvent(n)):n={promise:e,reason:r},!tn&&(i=u["on"+t])?i(n):t===en&&function(t,e){var r=u.console;r&&r.error&&(1===arguments.length?r.error(t):r.error(t,e))}("Unhandled promise rejection",r)},cn=function(t){jr.call(u,(function(){var e,r=t.facade,n=t.value;if(ln(t)&&(e=Br((function(){Ke?Qr.emit("unhandledRejection",n,r):sn(en,r,n)})),t.rejection=Ke||ln(t)?2:1,e.error))throw e.value}))},ln=function(t){return 1!==t.rejection&&!t.parent},fn=function(t){jr.call(u,(function(){var e=t.facade;Ke?Qr.emit("rejectionHandled",e):sn("rejectionhandled",e,t.value)}))},hn=function(t,e,r){return function(n){t(e,n,r)}},pn=function(t,e,r){t.done||(t.done=!0,r&&(t=r),t.value=e,t.state=2,un(t,!0))},vn=function(t,e,r){if(!t.done){t.done=!0,r&&(t=r);try{if(t.facade===e)throw qr("Promise can't be resolved itself");var n=an(e);n?Ir((function(){var r={done:!1};try{n.call(e,hn(vn,r,t),hn(pn,r,t))}catch(e){pn(r,e,t)}})):(t.value=e,t.state=1,un(t,!1))}catch(e){pn({done:!1},e,t)}}};if(nn&&(Wr=(Gr=function(t){be(this,Gr,zr),G(t),Cr.call(this);var e=Ur(this);try{t(hn(vn,e),hn(pn,e))}catch(t){pn(e,t)}}).prototype,(Cr=function(t){Hr(this,{type:zr,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:0,value:void 0})}).prototype=pe(Wr,{then:function(t,e){var r=Xr(this),n=Zr($e(this,Gr));return n.ok=!x(t)||t,n.fail=x(e)&&e,n.domain=Ke?Qr.domain:void 0,r.parent=!0,r.reactions.push(n),0!=r.state&&un(r,!1),n.promise},catch:function(t){return this.then(void 0,t)}}),Mr=function(){var t=new Cr,e=Ur(t);this.promise=t,this.resolve=hn(vn,e),this.reject=hn(pn,e)},Lr.f=Zr=function(t){return t===Gr||t===Nr?new Mr(t):Kr(t)},x(he)&&Yr!==Object.prototype)){_r=Yr.then,rn||(Ot(Yr,"then",(function(t,e){var r=this;return new Gr((function(t,e){_r.call(r,t,e)})).then(t,e)}),{unsafe:!0}),Ot(Yr,"catch",Wr.catch,{unsafe:!0}));try{delete Yr.constructor}catch(t){}ve&&ve(Yr,Wr)}fe({global:!0,wrap:!0,forced:nn},{Promise:Gr}),ge(Gr,zr,!1),xe(zr),Nr=w(zr),fe({target:zr,stat:!0,forced:nn},{reject:function(t){var e=Zr(this);return e.reject.call(void 0,t),e.promise}}),fe({target:zr,stat:!0,forced:nn},{resolve:function(t){return function(t,e){if(H(t),D(e)&&e.constructor===t)return e;var r=Lr.f(t);return(0,r.resolve)(e),r.promise}(this,t)}}),fe({target:zr,stat:!0,forced:on},{all:function(t){var e=this,r=Zr(e),n=r.resolve,i=r.reject,o=Br((function(){var r=G(e.resolve),o=[],a=0,u=1;Me(t,(function(t){var s=a++,c=!1;o.push(void 0),u++,r.call(e,t).then((function(t){c||(c=!0,o[s]=t,--u||n(o))}),i)})),--u||n(o)}));return o.error&&i(o.value),r.promise},race:function(t){var e=this,r=Zr(e),n=r.reject,i=Br((function(){var i=G(e.resolve);Me(t,(function(t){i.call(e,t).then(r.resolve,n)}))}));return i.error&&n(i.value),r.promise}});var yn=Bt.f;fe({target:"Reflect",stat:!0},{deleteProperty:function(t,e){var r=yn(H(t),e);return!(r&&!r.configurable)&&delete t[e]}});var dn=n((function(t){var e=function(t){var e,r=Object.prototype,n=r.hasOwnProperty,i="function"==typeof Symbol?Symbol:{},o=i.iterator||"@@iterator",a=i.asyncIterator||"@@asyncIterator",u=i.toStringTag||"@@toStringTag";function s(t,e,r){return Object.defineProperty(t,e,{value:r,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{s({},"")}catch(t){s=function(t,e,r){return t[e]=r}}function c(t,e,r,n){var i=e&&e.prototype instanceof d?e:d,o=Object.create(i.prototype),a=new P(n||[]);return o._invoke=function(t,e,r){var n=f;return function(i,o){if(n===p)throw new Error("Generator is already running");if(n===v){if("throw"===i)throw o;return C()}for(r.method=i,r.arg=o;;){var a=r.delegate;if(a){var u=T(a,r);if(u){if(u===y)continue;return u}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if(n===f)throw n=v,r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n=p;var s=l(t,e,r);if("normal"===s.type){if(n=r.done?v:h,s.arg===y)continue;return{value:s.arg,done:r.done}}"throw"===s.type&&(n=v,r.method="throw",r.arg=s.arg)}}}(t,r,a),o}function l(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}t.wrap=c;var f="suspendedStart",h="suspendedYield",p="executing",v="completed",y={};function d(){}function g(){}function m(){}var x={};s(x,o,(function(){return this}));var b=Object.getPrototypeOf,w=b&&b(b(E([])));w&&w!==r&&n.call(w,o)&&(x=w);var S=m.prototype=d.prototype=Object.create(x);function O(t){["next","throw","return"].forEach((function(e){s(t,e,(function(t){return this._invoke(e,t)}))}))}function k(t,e){function r(i,o,a,u){var s=l(t[i],t,o);if("throw"!==s.type){var c=s.arg,f=c.value;return f&&"object"==typeof f&&n.call(f,"__await")?e.resolve(f.__await).then((function(t){r("next",t,a,u)}),(function(t){r("throw",t,a,u)})):e.resolve(f).then((function(t){c.value=t,a(c)}),(function(t){return r("throw",t,a,u)}))}u(s.arg)}var i;this._invoke=function(t,n){function o(){return new e((function(e,i){r(t,n,e,i)}))}return i=i?i.then(o,o):o()}}function T(t,r){var n=t.iterator[r.method];if(n===e){if(r.delegate=null,"throw"===r.method){if(t.iterator.return&&(r.method="return",r.arg=e,T(t,r),"throw"===r.method))return y;r.method="throw",r.arg=new TypeError("The iterator does not provide a 'throw' method")}return y}var i=l(n,t.iterator,r.arg);if("throw"===i.type)return r.method="throw",r.arg=i.arg,r.delegate=null,y;var o=i.arg;return o?o.done?(r[t.resultName]=o.value,r.next=t.nextLoc,"return"!==r.method&&(r.method="next",r.arg=e),r.delegate=null,y):o:(r.method="throw",r.arg=new TypeError("iterator result is not an object"),r.delegate=null,y)}function A(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function R(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function P(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(A,this),this.reset(!0)}function E(t){if(t){var r=t[o];if(r)return r.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var i=-1,a=function r(){for(;++i=0;--o){var a=this.tryEntries[o],u=a.completion;if("root"===a.tryLoc)return i("end");if(a.tryLoc<=this.prev){var s=n.call(a,"catchLoc"),c=n.call(a,"finallyLoc");if(s&&c){if(this.prev=0;--r){var i=this.tryEntries[r];if(i.tryLoc<=this.prev&&n.call(i,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),R(r),y}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var i=n.arg;R(r)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,r,n){return this.delegate={iterator:E(t),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=e),y}},t}(t.exports);try{regeneratorRuntime=e}catch(t){"object"==typeof globalThis?globalThis.regeneratorRuntime=e:Function("r","regeneratorRuntime = r")(e)}})),gn=r(n((function(t){function e(t,e,r,n,i,o,a){try{var u=t[o](a),s=u.value}catch(t){return void r(t)}u.done?e(s):Promise.resolve(s).then(n,i)}t.exports=function(t){return function(){var r=this,n=arguments;return new Promise((function(i,o){var a=t.apply(r,n);function u(t){e(a,i,o,u,s,"next",t)}function s(t){e(a,i,o,u,s,"throw",t)}u(void 0)}))}},t.exports.default=t.exports,t.exports.__esModule=!0})));var mn=Object.freeze({__proto__:null,offscreen:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=t.DOMParser,r={window:null,ignoreAnimation:!0,ignoreMouse:!0,DOMParser:e,createCanvas:function(t,e){return new OffscreenCanvas(t,e)},createImage:function(t){return gn(dn.mark((function e(){var r,n,i;return dn.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch(t);case 2:return r=e.sent,e.next=5,r.blob();case 5:return n=e.sent,e.next=8,createImageBitmap(n);case 8:return i=e.sent,e.abrupt("return",i);case 10:case"end":return e.stop()}}),e)})))()}};return"undefined"==typeof DOMParser&&void 0!==e||Reflect.deleteProperty(r,"DOMParser"),r},node:function(t){var e=t.DOMParser,r=t.canvas;return{window:null,ignoreAnimation:!0,ignoreMouse:!0,DOMParser:e,fetch:t.fetch,createCanvas:r.createCanvas,createImage:r.loadImage}}}),xn=Array.isArray||function(t){return"Array"==Tt(t)},bn=I("species"),wn=function(t,e){return new(function(t){var e;return xn(t)&&(e=t.constructor,(We(e)&&(e===Array||xn(e.prototype))||D(e)&&null===(e=e[bn]))&&(e=void 0)),void 0===e?Array:e}(t))(0===e?0:e)},Sn=[].push,On=function(t){var e=1==t,r=2==t,n=3==t,i=4==t,o=6==t,a=7==t,u=5==t||o;return function(s,c,l,f){for(var h,v,y=p(s),d=It(y),g=Te(c,l,3),m=Gt(d),x=0,b=f||wn,w=e?b(s,m):r||a?b(s,0):void 0;m>x;x++)if((u||x in d)&&(v=g(h=d[x],x,y),t))if(e)w[x]=v;else if(v)switch(t){case 3:return!0;case 5:return h;case 6:return x;case 2:Sn.call(w,h)}else switch(t){case 4:return!1;case 7:Sn.call(w,h)}return o?-1:n||i?i:w}},kn={forEach:On(0),map:On(1),filter:On(2),some:On(3),every:On(4),find:On(5),findIndex:On(6),filterReject:On(7)},Tn=I("species"),An=function(t){return R>=51||!P((function(){var e=[];return(e.constructor={})[Tn]=function(){return{foo:1}},1!==e[t](Boolean).foo}))},Rn=kn.map,Pn=An("map");fe({target:"Array",proto:!0,forced:!Pn},{map:function(t){return Rn(this,t,arguments.length>1?arguments[1]:void 0)}});var En=function(t){if("Symbol"===Pt(t))throw TypeError("Cannot convert a Symbol value to a string");return String(t)},Cn="\t\n\v\f\r                \u2028\u2029\ufeff",Mn="["+Cn+"]",Nn=RegExp("^"+Mn+Mn+"*"),_n=RegExp(Mn+Mn+"*$"),In=function(t){return function(e){var r=En(h(e));return 1&t&&(r=r.replace(Nn,"")),2&t&&(r=r.replace(_n,"")),r}},Vn={start:In(1),end:In(2),trim:In(3)},Ln=Vn.trim,Bn=u.parseFloat,Dn=u.Symbol,jn=Dn&&Dn.iterator,Fn=1/Bn(Cn+"-0")!=-1/0||jn&&!P((function(){Bn(Object(jn))}))?function(t){var e=Ln(En(t)),r=Bn(e);return 0===r&&"-"==e.charAt(0)?-0:r}:Bn;fe({global:!0,forced:parseFloat!=Fn},{parseFloat:Fn});var zn,Un=function(){var t=H(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.dotAll&&(e+="s"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e},Hn=u.RegExp,Xn={UNSUPPORTED_Y:P((function(){var t=Hn("a","y");return t.lastIndex=2,null!=t.exec("abcd")})),BROKEN_CARET:P((function(){var t=Hn("^r","gy");return t.lastIndex=2,null!=t.exec("str")}))},Yn=Object.keys||function(t){return Qt(t,Zt)},Gn=B?Object.defineProperties:function(t,e){H(t);for(var r,n=Yn(e),i=n.length,o=0;i>o;)K.f(t,r=n[o++],e[r]);return t},Wn=ct("IE_PROTO"),qn=function(){},$n=function(t){return" diff --git a/components/templates/chartjs-chart.php.zip b/components/templates/chartjs-chart.php.zip deleted file mode 100644 index b3cd6a6..0000000 Binary files a/components/templates/chartjs-chart.php.zip and /dev/null differ diff --git a/components/templates/chartjs-settings.php b/components/templates/chartjs-settings.php deleted file mode 100755 index 76653de..0000000 --- a/components/templates/chartjs-settings.php +++ /dev/null @@ -1,169 +0,0 @@ - esc_html__( 'Columns', 'm-chart' ), - 'rows' => esc_html__( 'Rows', 'm-chart' ), -); - -$y_min_disabled = $post_meta['y_min'] ? '' : 'disabled="disabled" '; -?> -
    -
    -

    -
    - -

    -

    -
    - -

    -

    -
    - -

    -
    -
    -

    -
    - -

    -

    -  
    - -

    -

    -  
    - -

    -

     
    - -

    -
    -
    -

    -
    - -

    -

    -
    - -

    -
    -
    -

    - - /> -

    -
    -
    -

    -
    - -

    -

    -
    - -

    -
    -
    -
    -

    -
    - -

    -

    -
    - - - - -

    -
    - \ No newline at end of file diff --git a/components/templates/handlebars.php b/components/templates/handlebars.php deleted file mode 100755 index 3e20cd2..0000000 --- a/components/templates/handlebars.php +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/components/templates/spreadsheet-meta-box.php b/components/templates/spreadsheet-meta-box.php deleted file mode 100755 index 465b3ea..0000000 --- a/components/templates/spreadsheet-meta-box.php +++ /dev/null @@ -1,46 +0,0 @@ - - -
    -
    -
    -
    - -
    -
    -
    -
    - -
    - - -
    -

    -

    -

    -
    - - File:
    - -
    -
    -
    -
    - -slug . '-save-post', $this->get_field_name( 'nonce' ) ); \ No newline at end of file diff --git a/components/templates/subtitle-field.php b/components/templates/subtitle-field.php deleted file mode 100755 index c345204..0000000 --- a/components/templates/subtitle-field.php +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/components/templates/table.php b/components/templates/table.php index dda5809..7334929 100755 --- a/components/templates/table.php +++ b/components/templates/table.php @@ -6,13 +6,13 @@ $set_name = ': ' . $post_meta['set_names'][ $set ]; } - if ( isset( m_chart()->parse()->value_labels['first_row'] ) ) { - $first_row = m_chart()->parse()->value_labels['first_row']; - $labels = m_chart()->parse()->value_labels['first_column']; + if ( isset( m_chart()->parse()->value_labels[ M_Chart_Parse::LABELS_FIRST_ROW ] ) ) { + $first_row = m_chart()->parse()->value_labels[ M_Chart_Parse::LABELS_FIRST_ROW ]; + $labels = m_chart()->parse()->value_labels[ M_Chart_Parse::LABELS_FIRST_COLUMN ]; $row_column = false; - if ( count( $first_row ) == count( m_chart()->parse()->set_data[0] ) ) { + if ( count( $first_row ) == count( m_chart()->parse()->raw_data[0] ) ) { $row_column = true; } ?> @@ -35,17 +35,12 @@ $label ) { if ( $row_column ) { - $value = m_chart()->parse()->set_data[ $row ][ $column ]; + $raw = m_chart()->parse()->raw_data[ $row ][ $column ] ?? null; } else { - $value = m_chart()->parse()->set_data[ $column ][ $row ]; - } - - if ( is_numeric( $value ) ) { - $value = number_format_i18n( $value, strlen( substr( strrchr( $value, '.'), 1 ) ) ); - $value = '' != $value ? m_chart()->parse()->data_prefix . $value . m_chart()->parse()->data_suffix : ''; + $raw = m_chart()->parse()->raw_data[ $column ][ $row ] ?? null; } ?> - + parse()->format_raw( $raw ) ); ?> @@ -67,16 +62,12 @@ parse()->set_data ) / count( $first_row ); + $row_count = 1; + $total_rows = count( m_chart()->parse()->raw_data ) / count( $first_row ); - foreach ( m_chart()->parse()->set_data as $key => $value ) { - if ( is_numeric( $value ) ) { - $value = number_format_i18n( $value, strlen( substr( strrchr( $value, '.'), 1 ) ) ); - $value = '' != $value ? m_chart()->parse()->data_prefix . $value . m_chart()->parse()->data_suffix : ''; - } + foreach ( m_chart()->parse()->raw_data as $key => $raw ) { ?> - + parse()->format_raw( $raw ) ); ?> - \ No newline at end of file + diff --git a/docs/.vuepress/config.mjs b/docs/.vuepress/config.mjs new file mode 100644 index 0000000..23ecd87 --- /dev/null +++ b/docs/.vuepress/config.mjs @@ -0,0 +1,77 @@ +import { defineUserConfig } from 'vuepress' +import { defaultTheme } from '@vuepress/theme-default' +import { viteBundler } from '@vuepress/bundler-vite' +import { searchPlugin } from '@vuepress/plugin-search' + +export default defineUserConfig( { + lang: 'en-US', + title: 'M Chart', + description: 'WordPress chart plugin powered by Chart.js', + base: '/m-chart/', + bundler: viteBundler(), + + plugins: [ + searchPlugin(), + ], + + theme: defaultTheme( { + logo: '/logo.png', + repo: 'methnen/m-chart', + docsDir: 'docs', + editLink: false, + + navbar: [ + { text: 'Users', link: '/guide/' }, + { text: 'Highcharts', link: '/highcharts/' }, + { text: 'Developers', link: '/developer/' }, + { text: 'Download', link: 'https://wordpress.org/plugins/m-chart/' }, + ], + + sidebar: { + '/guide/': [ + { + text: 'User Guide', + children: [ + '/guide/README.md', + '/guide/libraries.md', + '/guide/chart-types.md', + '/guide/creating-a-chart.md', + '/guide/sheets.md', + '/guide/scatter-bubble.md', + '/guide/radar.md', + '/guide/shortcode.md', + '/guide/block.md', + '/guide/csv.md', + '/guide/settings.md', + '/guide/themes.md', + '/guide/duplicating.md', + '/guide/examples.md', + ], + }, + ], + '/developer/': [ + { + text: 'Developer Guide', + children: [ + '/developer/README.md', + '/developer/hooks.md', + '/developer/javascript-events.md', + '/developer/admin-ui-hooks.md', + '/developer/migrating-v2.md', + ], + }, + ], + '/highcharts/': [ + { + text: 'Highcharts', + children: [ + '/highcharts/README.md', + '/highcharts/themes.md', + '/highcharts/hooks.md', + '/highcharts/javascript-events.md', + ], + }, + ], + }, + } ), +} ) diff --git a/docs/.vuepress/public/examples/m-chart-example-charts-chartjs.zip b/docs/.vuepress/public/examples/m-chart-example-charts-chartjs.zip new file mode 100644 index 0000000..f81a1f9 Binary files /dev/null and b/docs/.vuepress/public/examples/m-chart-example-charts-chartjs.zip differ diff --git a/docs/.vuepress/public/examples/m-chart-example-charts-highcharts.zip b/docs/.vuepress/public/examples/m-chart-example-charts-highcharts.zip new file mode 100644 index 0000000..b80ce31 Binary files /dev/null and b/docs/.vuepress/public/examples/m-chart-example-charts-highcharts.zip differ diff --git a/docs/.vuepress/public/logo.png b/docs/.vuepress/public/logo.png new file mode 100644 index 0000000..f4d2943 Binary files /dev/null and b/docs/.vuepress/public/logo.png differ diff --git a/docs/.vuepress/public/types/area.png b/docs/.vuepress/public/types/area.png new file mode 100644 index 0000000..602780b Binary files /dev/null and b/docs/.vuepress/public/types/area.png differ diff --git a/docs/.vuepress/public/types/bar.png b/docs/.vuepress/public/types/bar.png new file mode 100644 index 0000000..a925507 Binary files /dev/null and b/docs/.vuepress/public/types/bar.png differ diff --git a/docs/.vuepress/public/types/bubble.png b/docs/.vuepress/public/types/bubble.png new file mode 100644 index 0000000..fc39121 Binary files /dev/null and b/docs/.vuepress/public/types/bubble.png differ diff --git a/docs/.vuepress/public/types/column.png b/docs/.vuepress/public/types/column.png new file mode 100644 index 0000000..e264843 Binary files /dev/null and b/docs/.vuepress/public/types/column.png differ diff --git a/docs/.vuepress/public/types/doughnut.png b/docs/.vuepress/public/types/doughnut.png new file mode 100644 index 0000000..f70623b Binary files /dev/null and b/docs/.vuepress/public/types/doughnut.png differ diff --git a/docs/.vuepress/public/types/line.png b/docs/.vuepress/public/types/line.png new file mode 100644 index 0000000..99b8f26 Binary files /dev/null and b/docs/.vuepress/public/types/line.png differ diff --git a/docs/.vuepress/public/types/pie.png b/docs/.vuepress/public/types/pie.png new file mode 100644 index 0000000..486d5f0 Binary files /dev/null and b/docs/.vuepress/public/types/pie.png differ diff --git a/docs/.vuepress/public/types/polar.png b/docs/.vuepress/public/types/polar.png new file mode 100644 index 0000000..770b651 Binary files /dev/null and b/docs/.vuepress/public/types/polar.png differ diff --git a/docs/.vuepress/public/types/polor.png b/docs/.vuepress/public/types/polor.png new file mode 100644 index 0000000..d468dfd Binary files /dev/null and b/docs/.vuepress/public/types/polor.png differ diff --git a/docs/.vuepress/public/types/radar-area.png b/docs/.vuepress/public/types/radar-area.png new file mode 100644 index 0000000..5920464 Binary files /dev/null and b/docs/.vuepress/public/types/radar-area.png differ diff --git a/docs/.vuepress/public/types/radar.png b/docs/.vuepress/public/types/radar.png new file mode 100644 index 0000000..8b12036 Binary files /dev/null and b/docs/.vuepress/public/types/radar.png differ diff --git a/docs/.vuepress/public/types/scatter.png b/docs/.vuepress/public/types/scatter.png new file mode 100644 index 0000000..7ff040b Binary files /dev/null and b/docs/.vuepress/public/types/scatter.png differ diff --git a/docs/.vuepress/public/types/spline.png b/docs/.vuepress/public/types/spline.png new file mode 100644 index 0000000..120d952 Binary files /dev/null and b/docs/.vuepress/public/types/spline.png differ diff --git a/docs/.vuepress/public/types/stacked-bar.png b/docs/.vuepress/public/types/stacked-bar.png new file mode 100644 index 0000000..0995be2 Binary files /dev/null and b/docs/.vuepress/public/types/stacked-bar.png differ diff --git a/docs/.vuepress/public/types/stacked-column.png b/docs/.vuepress/public/types/stacked-column.png new file mode 100644 index 0000000..74597e0 Binary files /dev/null and b/docs/.vuepress/public/types/stacked-column.png differ diff --git a/docs/.vuepress/public/ui/block.png b/docs/.vuepress/public/ui/block.png new file mode 100644 index 0000000..79c5334 Binary files /dev/null and b/docs/.vuepress/public/ui/block.png differ diff --git a/docs/.vuepress/public/ui/chart-preview-settings.png b/docs/.vuepress/public/ui/chart-preview-settings.png new file mode 100644 index 0000000..b02998f Binary files /dev/null and b/docs/.vuepress/public/ui/chart-preview-settings.png differ diff --git a/docs/.vuepress/public/ui/chart-settings.png b/docs/.vuepress/public/ui/chart-settings.png new file mode 100644 index 0000000..284ef24 Binary files /dev/null and b/docs/.vuepress/public/ui/chart-settings.png differ diff --git a/docs/.vuepress/public/ui/csv-import.png b/docs/.vuepress/public/ui/csv-import.png new file mode 100644 index 0000000..a828e1e Binary files /dev/null and b/docs/.vuepress/public/ui/csv-import.png differ diff --git a/docs/.vuepress/public/ui/csv.png b/docs/.vuepress/public/ui/csv.png new file mode 100644 index 0000000..b5b207a Binary files /dev/null and b/docs/.vuepress/public/ui/csv.png differ diff --git a/docs/.vuepress/public/ui/data.png b/docs/.vuepress/public/ui/data.png new file mode 100644 index 0000000..e172676 Binary files /dev/null and b/docs/.vuepress/public/ui/data.png differ diff --git a/docs/.vuepress/public/ui/scatter-data.png b/docs/.vuepress/public/ui/scatter-data.png new file mode 100644 index 0000000..cd81398 Binary files /dev/null and b/docs/.vuepress/public/ui/scatter-data.png differ diff --git a/docs/.vuepress/public/ui/settings.png b/docs/.vuepress/public/ui/settings.png new file mode 100644 index 0000000..6bfd7cf Binary files /dev/null and b/docs/.vuepress/public/ui/settings.png differ diff --git a/docs/.vuepress/styles/index.scss b/docs/.vuepress/styles/index.scss new file mode 100644 index 0000000..87243a7 --- /dev/null +++ b/docs/.vuepress/styles/index.scss @@ -0,0 +1,79 @@ +// M Chart brand colors — derived from the M letterform only (background coral #e8714e excluded) +// Logo palette (M letterform): +// Bright red-orange: #cc3311 +// Dark maroon: #8c1717 +// Light highlight: #f5a090 + +:root { + --vp-c-accent: #cc3311; + --vp-c-accent-bg: #f0440e; + --vp-c-accent-hover: #8c1717; + --vp-c-accent-soft: rgb(204 51 17 / 14%); + + // Code block — actual variable names from @vuepress/highlighter-helper base.css + --code-c-bg: #f6f8fa; + --code-c-text: #24292e; + --code-c-line-number: #6e7781; + + // Copy button + --copy-code-c-text: #57606a; + --copy-code-c-hover: #d0d7de; +} + +[data-theme='dark'] { + --vp-c-accent: #f5a090; + --vp-c-accent-bg: #cc3311; + --vp-c-accent-hover: #f0440e; + --vp-c-accent-soft: rgb(245 160 144 / 16%); + + --code-c-bg: #0d1117; + --code-c-text: #e6edf3; + --code-c-line-number: #8b949e; + + // Copy button + --copy-code-c-text: #8b949e; + --copy-code-c-hover: #21262d; +} + +// Remove gutter border — it renders as dark nubs at the top/bottom corners +div[class*='language-'].line-numbers-mode::after { + border-right: none; +} + +// GitHub light token colors +div[class*='language-'] .token.comment { color: #6a737d; } +div[class*='language-'] .token.keyword { color: #d73a49; } +div[class*='language-'] .token.string { color: #032f62; } +div[class*='language-'] .token.number { color: #005cc5; } +div[class*='language-'] .token.boolean { color: #005cc5; } +div[class*='language-'] .token.operator { color: #d73a49; } +div[class*='language-'] .token.function { color: #6f42c1; } +div[class*='language-'] .token.class-name { color: #6f42c1; } +div[class*='language-'] .token.tag { color: #22863a; } +div[class*='language-'] .token.attr-name { color: #6f42c1; } +div[class*='language-'] .token.attr-value { color: #032f62; } +div[class*='language-'] .token.builtin { color: #005cc5; } +div[class*='language-'] .token.property { color: #005cc5; } +div[class*='language-'] .token.selector { color: #6f42c1; } +div[class*='language-'] .token.variable { color: #e36209; } +div[class*='language-'] .token.regex { color: #032f62; } +div[class*='language-'] .token.punctuation { color: #24292e; } + +// GitHub dark token colors +[data-theme='dark'] div[class*='language-'] .token.comment { color: #8b949e; } +[data-theme='dark'] div[class*='language-'] .token.keyword { color: #ff7b72; } +[data-theme='dark'] div[class*='language-'] .token.string { color: #a5d6ff; } +[data-theme='dark'] div[class*='language-'] .token.number { color: #79c0ff; } +[data-theme='dark'] div[class*='language-'] .token.boolean { color: #79c0ff; } +[data-theme='dark'] div[class*='language-'] .token.operator { color: #ff7b72; } +[data-theme='dark'] div[class*='language-'] .token.function { color: #d2a8ff; } +[data-theme='dark'] div[class*='language-'] .token.class-name { color: #d2a8ff; } +[data-theme='dark'] div[class*='language-'] .token.tag { color: #7ee787; } +[data-theme='dark'] div[class*='language-'] .token.attr-name { color: #d2a8ff; } +[data-theme='dark'] div[class*='language-'] .token.attr-value { color: #a5d6ff; } +[data-theme='dark'] div[class*='language-'] .token.builtin { color: #79c0ff; } +[data-theme='dark'] div[class*='language-'] .token.property { color: #79c0ff; } +[data-theme='dark'] div[class*='language-'] .token.selector { color: #d2a8ff; } +[data-theme='dark'] div[class*='language-'] .token.variable { color: #ffa657; } +[data-theme='dark'] div[class*='language-'] .token.regex { color: #a5d6ff; } +[data-theme='dark'] div[class*='language-'] .token.punctuation { color: #e6edf3; } diff --git a/docs/developer/README.md b/docs/developer/README.md new file mode 100644 index 0000000..6e683ce --- /dev/null +++ b/docs/developer/README.md @@ -0,0 +1,39 @@ +# Developer Guide + +M Chart is extensible via PHP hooks and a JavaScript `wp.hooks` API. This section documents the available hooks for themes and plugins that want to customize chart behavior. + +## Quick Links + +- [PHP Hooks & Filters](./hooks.md) — Server-side action and filter hooks +- [JavaScript Events](./javascript-events.md) — Front-end `CustomEvent` API (v2.0+) +- [Admin UI Hooks](./admin-ui-hooks.md) — `wp.hooks` API for the React admin interface +- [Migrating to v2](./migrating-v2.md) — Breaking changes and upgrade guide + +## Build Environment + +To work on the plugin source you will need Node.js installed. Then from the plugin root: + +```sh +npm install +``` + +**Build commands:** + +| Command | Description | +|---------|-------------| +| `npm run build` | Build everything (CSS, JS, block, admin app) | +| `npm run build:css` | Build CSS only | +| `npm run build:js` | Minify JS helpers only | +| `npm run build:block` | Build the Gutenberg block only | +| `npm run build:admin-ui` | Build the React admin app only | + +**Watch commands:** + +| Command | Description | +|---------|-------------| +| `npm run watch` | Watch everything | +| `npm run watch:admin-ui` | Watch the React admin app only | + +::: tip +For additional build commands, translation tooling, and test setup, see [DEVELOPERS.md](https://github.com/methnen/m-chart/blob/main/DEVELOPERS.md) in the repository. +::: diff --git a/docs/developer/admin-ui-hooks.md b/docs/developer/admin-ui-hooks.md new file mode 100644 index 0000000..0377d44 --- /dev/null +++ b/docs/developer/admin-ui-hooks.md @@ -0,0 +1,107 @@ +# Admin UI JavaScript Hooks + +Available since version 2.0. The React-based chart admin UI uses the WordPress `wp.hooks` API for extensibility. Library plugins (e.g. M Chart Highcharts Library) use these hooks to replace or augment the default Chart.js interface. + +## Filter Hooks + +### `m_chart.render_chart` + +Filters chart rendering in the admin preview. Return `true` to signal that your code has handled rendering, preventing the default Chart.js renderer from running. + +```js +wp.hooks.addFilter( + 'm_chart.render_chart', + 'my-plugin/render', + ( handled, chartArgs, canvas ) => { + if ( handled ) { + return handled; + } + + // Render your own chart on `canvas` using `chartArgs` + MyLibrary.render( canvas, chartArgs ); + + return true; // Prevent Chart.js from also rendering + } +); +``` + +**Parameters passed to the filter:** +- `handled` _(bool)_ — Whether a previous filter already handled rendering +- `chartArgs` _(object)_ — The full chart arguments object from the server +- `canvas` _(HTMLCanvasElement)_ — The canvas element to render into + +--- + +### `m_chart.settings_component` + +Filters the React component rendered in the chart type/settings panel. Use this to replace the default Chart.js settings UI with a custom component (e.g. Highcharts-specific settings). + +```js +wp.hooks.addFilter( + 'm_chart.settings_component', + 'my-plugin/settings', + ( DefaultComponent ) => MySettingsComponent +); +``` + +**Parameters:** +- `DefaultComponent` _(React component)_ — The default Chart.js settings component + +--- + +### `m_chart.multi_sheet_types` + +Filters the set of chart types that show the multi-sheet tab bar in the spreadsheet editor. By default only `scatter`, `bubble`, `radar`, and `radar-area` show multiple sheets. + +```js +wp.hooks.addFilter( + 'm_chart.multi_sheet_types', + 'my-plugin/multi-sheet', + ( types ) => { + // Add a custom type + return new Set( [ ...types, 'my-custom-type' ] ); + } +); +``` + +**Parameters:** +- `types` _(Set)_ — The current set of multi-sheet chart type strings + +## Action Hooks + +### `m_chart.chart_args_success` + +Fires after the admin successfully fetches new chart arguments from the server (after any spreadsheet or settings change). + +```js +wp.hooks.addAction( + 'm_chart.chart_args_success', + 'my-plugin/on-args', + ( chartArgs ) => { + console.log( 'New chart args:', chartArgs ); + } +); +``` + +**Parameters:** +- `chartArgs` _(object)_ — The chart arguments returned from the server + +--- + +### `m_chart.render_done` + +Fires after the chart instance has been created or updated in the admin preview. + +```js +wp.hooks.addAction( + 'm_chart.render_done', + 'my-plugin/render-done', + ( chart ) => { + // `chart` is the chart instance for the active library + console.log( chart.data ); + } +); +``` + +**Parameters:** +- `chart` _(object)_ — The chart instance for the active library. This is a Chart.js instance when Chart.js is active, or a Highcharts instance when the [M Chart Highcharts Library](../highcharts/javascript-events.md) is active. diff --git a/docs/developer/hooks.md b/docs/developer/hooks.md new file mode 100644 index 0000000..8488d41 --- /dev/null +++ b/docs/developer/hooks.md @@ -0,0 +1,178 @@ +# PHP Hooks & Filters + +These WordPress action and filter hooks are available for plugins and themes to customize M Chart behavior on the server side. + +## Action Hooks + +### `m_chart_update_post_meta` + +Fires after chart post meta is saved. + +```php +add_action( 'm_chart_update_post_meta', function( $post_id, $post_meta, $meta ) { + // Do something after chart data is saved +}, 10, 3 ); +``` + +**Parameters:** +- `$post_id` _(int)_ — The chart post ID +- `$post_meta` _(array)_ — The parsed, sanitized post meta that was saved +- `$meta` _(array)_ — The raw post meta as submitted (before parsing) + +--- + +### `m_chart_get_chart_start` + +Fires at the start of chart output generation, before any HTML is rendered. + +```php +add_action( 'm_chart_get_chart_start', function( $post_id, $args ) { + // Runs before chart HTML is generated +}, 10, 2 ); +``` + +**Parameters:** +- `$post_id` _(int)_ — The chart post ID +- `$args` _(array)_ — Display arguments: `show` (string), `width` (string), `share` (string) + +--- + +### `m_chart_get_chart_end` + +Fires at the end of chart output generation, after all HTML has been rendered. + +```php +add_action( 'm_chart_get_chart_end', function( $post_id, $args ) { + // Runs after chart HTML is generated +}, 10, 2 ); +``` + +**Parameters:** +- `$post_id` _(int)_ — The chart post ID +- `$args` _(array)_ — Display arguments: `show` (string), `width` (string), `share` (string) + +--- + +### `m_chart_after_chart_args` + +Fires after the Chart.js chart arguments array has been fully assembled. Use this to modify chart args at the PHP level before they are JSON-encoded and passed to JavaScript. + +```php +add_action( 'm_chart_after_chart_args', function( $chart_args, $post_id ) { + // Modify $chart_args here +}, 10, 2 ); +``` + +--- + +### `m_chart_admin_scripts` + +Fires in the admin footer after M Chart has enqueued its scripts. Use to enqueue additional scripts for the chart edit screen. + +```php +add_action( 'm_chart_admin_scripts', function( $library, $post_id ) { + if ( 'chartjs' !== $library ) { + return; + } + + wp_enqueue_script( 'my-chartjs-extension', get_template_directory_uri() . '/js/my-extension.js' ); +}, 10, 2 ); +``` + +**Parameters:** +- `$library` _(string)_ — The active charting library slug (e.g. `'chartjs'`, `'highcharts'`) +- `$post_id` _(int)_ — The chart post ID + +--- + +### `m_chart_admin_footer_javascript` + +Fires inside the admin footer `