@qkix/strapi-plugin-chartkit 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,7 +29,7 @@
29
29
  npm install @qkix/strapi-plugin-chartkit
30
30
  ```
31
31
 
32
- Add a **Chart** field to a content type. That's it — no config file, no provider.
32
+ Add a **Chart** field to a content type. That's it - no config file, no provider.
33
33
 
34
34
  ## Why server-side SVG
35
35
 
@@ -42,7 +42,7 @@ receives finished markup:
42
42
 
43
43
  - **No runtime.** Nothing to download, parse, or execute.
44
44
  - **Nothing to hydrate**, so no layout shift and no flash of empty box.
45
- - **It scales and prints** — it is vector, not a canvas bitmap.
45
+ - **It scales and prints** - it is vector, not a canvas bitmap.
46
46
  - **Screen readers get a real description**: `role="img"` with a `<title>` and
47
47
  the description you wrote.
48
48
 
@@ -53,7 +53,7 @@ receives finished markup:
53
53
  Bar, grouped and stacked bars, line, area and stacked area, pie and donut.
54
54
 
55
55
  Two of those tiles are the details that usually go wrong. A missing reading
56
- stays a **gap** — never a zero pretending to be a measurement. And negative
56
+ stays a **gap** - never a zero pretending to be a measurement. And negative
57
57
  values get a real baseline instead of being flipped or clipped.
58
58
 
59
59
  ## Editing
@@ -61,7 +61,7 @@ values get a real baseline instead of being flipped or clipped.
61
61
  <img src="https://raw.githubusercontent.com/qkix/strapi-plugins/main/packages/strapi-plugin-chartkit/docs/editor.png" alt="The chart editor: live preview, chart settings and a data grid" width="700" />
62
62
 
63
63
  A live preview above a spreadsheet-style grid. **The preview is the real
64
- renderer** — the same function that draws the published page — so a chart cannot
64
+ renderer** - the same function that draws the published page - so a chart cannot
65
65
  look right here and wrong there.
66
66
 
67
67
  The editor opens in a dialog. Cancel genuinely cancels: it edits a draft and
@@ -80,24 +80,24 @@ part of every charting tool.
80
80
  | ------------------------- | ------------------------------------- |
81
81
  | tab, comma or semicolon | detected per paste |
82
82
  | `1.234,50` and `1,234.50` | 1234.5 either way |
83
- | `€1 234,50` | 1234.5 — currency and spaces stripped |
83
+ | `€1 234,50` | 1234.5 - currency and spaces stripped |
84
84
  | `"North, inland"` | one cell; a quoted delimiter survives |
85
- | `n/a`, `—`, empty | a **gap**, never a zero |
85
+ | `n/a`, `-`, empty | a **gap**, never a zero |
86
86
 
87
- It shows **what it parsed before replacing anything** — above, four categories,
87
+ It shows **what it parsed before replacing anything** - above, four categories,
88
88
  two series, and one unreadable cell called out. The parser has to guess whether
89
89
  the first row is a header, and a wrong guess that silently overwrites your data
90
90
  is far worse than one you can cancel.
91
91
 
92
92
  **From the Media Library.** Pick a `.csv`, `.tsv` or `.txt` you already
93
- uploaded. Anything else is refused before it is fetched — handing a PDF to a CSV
93
+ uploaded. Anything else is refused before it is fetched - handing a PDF to a CSV
94
94
  parser produces a confident table of nonsense.
95
95
 
96
96
  **By hand**, in the grid.
97
97
 
98
98
  ## Rendering it
99
99
 
100
- The field stores a `ChartSpec` — the same object the renderers take. Nothing
100
+ The field stores a `ChartSpec` - the same object the renderers take. Nothing
101
101
  sits in between:
102
102
 
103
103
  ```tsx
@@ -139,7 +139,7 @@ a chart looks like it belongs wherever it lands:
139
139
 
140
140
  This is the reason the SVG is hand-written rather than produced by an existing
141
141
  SSR chart renderer. The obvious candidate, ECharts in SSR mode, writes its
142
- palette into the markup as literal hex values — which makes class-based dark
142
+ palette into the markup as literal hex values - which makes class-based dark
143
143
  mode impossible without post-processing the output.
144
144
 
145
145
  ## Also a block, if you use Better Blocks
@@ -162,15 +162,15 @@ export default {
162
162
  };
163
163
  ```
164
164
 
165
- The front end needs its own half — `chartBlockPlugin` from whichever renderer
166
- you use — or a chart authored in a document is stored correctly and drawn as
165
+ The front end needs its own half - `chartBlockPlugin` from whichever renderer
166
+ you use - or a chart authored in a document is stored correctly and drawn as
167
167
  nothing.
168
168
 
169
169
  ## Field options
170
170
 
171
171
  | Option | Effect |
172
172
  | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
173
- | **Number formatting locale** | How axis numbers are formatted **in the admin preview** — a BCP 47 tag such as `de-DE`. The front end passes its own at render. |
173
+ | **Number formatting locale** | How axis numbers are formatted **in the admin preview** - a BCP 47 tag such as `de-DE`. The front end passes its own at render. |
174
174
  | **Required** | Standard Strapi validation. |
175
175
 
176
176
  ## Old charts stay working
@@ -179,15 +179,15 @@ The spec is versioned, and the field **migrates on read**, so opening a chart
179
179
  saved against an older schema edits and saves the current shape. Nothing has to
180
180
  be resaved for a site to keep working: `renderChart` migrates in memory too.
181
181
 
182
- If a field holds something that is not a readable chart — a value from an import
183
- script, or a field that used to be a different type — it says so and goes
182
+ If a field holds something that is not a readable chart - a value from an import
183
+ script, or a field that used to be a different type - it says so and goes
184
184
  **read-only** rather than offering to overwrite it.
185
185
 
186
186
  ## Packages
187
187
 
188
188
  | Package | For |
189
189
  | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
190
- | [`@qkix/strapi-plugin-chartkit`](https://www.npmjs.com/package/@qkix/strapi-plugin-chartkit) | this plugin — the field in the Strapi admin |
190
+ | [`@qkix/strapi-plugin-chartkit`](https://www.npmjs.com/package/@qkix/strapi-plugin-chartkit) | this plugin - the field in the Strapi admin |
191
191
  | [`@qkix/chartkit-core`](https://www.npmjs.com/package/@qkix/chartkit-core) | the spec, the validator and the SVG renderer. No dependencies. |
192
192
  | [`@qkix/chartkit-react-renderer`](https://www.npmjs.com/package/@qkix/chartkit-react-renderer) | rendering a stored chart in React / Next.js |
193
193
  | [`@qkix/chartkit-astro-renderer`](https://www.npmjs.com/package/@qkix/chartkit-astro-renderer) | rendering a stored chart in Astro |
@@ -196,12 +196,17 @@ script, or a field that used to be a different type — it says so and goes
196
196
  ## Not there yet
197
197
 
198
198
  Being honest about the edges: no time axes, no tooltips or other
199
- interactivity, and no per-chart color overrides — palettes are set in your site's
199
+ interactivity, and no per-chart color overrides - palettes are set in your site's
200
200
  CSS. Data is stored with the chart rather than queried from a collection.
201
201
 
202
202
  Issues and ideas welcome at
203
203
  [qkix/strapi-plugins](https://github.com/qkix/strapi-plugins/issues).
204
204
 
205
+ ## Roadmap
206
+
207
+ What is worth building next, what is deliberately not planned, and why:
208
+ [ROADMAP.md](https://github.com/qkix/strapi-plugins/blob/main/packages/strapi-plugin-chartkit/ROADMAP.md).
209
+
205
210
  ## License
206
211
 
207
212
  MIT
@@ -1 +1 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const a=require("react"),n=require("react/jsx-runtime"),o=require("@qkix/chartkit-core"),c=(e,t,i)=>{const s=e[t];return s?typeof s=="function"?s():Promise.resolve(s):new Promise((y,u)=>{(typeof queueMicrotask=="function"?queueMicrotask:setTimeout)(u.bind(null,new Error("Unknown variable dynamic import: "+t+(t.split("/").length!==i?". Note that variables only represent file names one level deep.":""))))})},r="chartkit",l="chart",d=({setPlugin:e})=>{const t=a.useRef(e);return a.useEffect(()=>{t.current(r)},[]),null},f=()=>n.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[n.jsx("path",{d:"M2 14V2",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"}),n.jsx("path",{d:"M2 14h12",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"}),n.jsx("rect",{x:"4",y:"7",width:"2.5",height:"5",fill:"currentColor"}),n.jsx("rect",{x:"8",y:"4",width:"2.5",height:"8",fill:"currentColor"}),n.jsx("rect",{x:"12",y:"9",width:"2.5",height:"3",fill:"currentColor"})]});function p(e){if(e==null||e==="")return{status:"empty"};let t=e;if(typeof e=="string"){try{t=JSON.parse(e)}catch{return{status:"unreadable",reason:"This field contains text that is not JSON."}}if(t===null||t==="")return{status:"empty"}}if(typeof t!="object"||t===null||Array.isArray(t))return{status:"unreadable",reason:"This field contains JSON that is not a chart."};const i=o.migrateChartSpec(t);return i.status==="skipped"?{status:"unreadable",reason:`Chartkit cannot read what is in this field: ${i.reason}.`}:{status:"ok",spec:i.spec}}function h(e){return JSON.stringify(e)}function m(){return{version:o.CHART_SPEC_VERSION,type:"bar",data:{source:"inline",labels:["Q1","Q2","Q3","Q4"],series:[{name:"Series 1",values:[null,null,null,null]}]}}}const g={register(e){e.registerPlugin({id:r,name:r,initializer:d,isReady:!1}),e.customFields.register({name:l,pluginId:r,type:"json",icon:f,intlLabel:{id:`${r}.field.label`,defaultMessage:"Chart"},intlDescription:{id:`${r}.field.description`,defaultMessage:"A chart, rendered as SVG with no client-side JavaScript"},components:{Input:async()=>({default:(await Promise.resolve().then(()=>require("../_chunks/ChartField-CKCDQc6g.js"))).default})},options:{base:[{sectionTitle:{id:`${r}.section.preview`,defaultMessage:"Preview"},items:[{name:"options.previewLocale",type:"text",placeholder:{id:`${r}.previewLocale.placeholder`,defaultMessage:"en-US"},intlLabel:{id:`${r}.previewLocale`,defaultMessage:"Number formatting locale"},description:{id:`${r}.previewLocale.description`,defaultMessage:"How axis numbers are formatted in the admin preview — a BCP 47 tag such as de-DE or pl-PL. Affects the preview only; the front end passes its own locale when it renders. Left empty, the browser's is used."}}]}],advanced:[{sectionTitle:null,items:[{name:"required",type:"checkbox",intlLabel:{id:`${r}.required`,defaultMessage:"Required field"},description:{id:`${r}.required.description`,defaultMessage:"You won't be able to create an entry if this field is empty"}}]}]}})},async registerTrads({locales:e}){return Promise.all(e.map(async t=>{try{const{default:i}=await c(Object.assign({"./translations/en.json":()=>Promise.resolve().then(()=>require("../_chunks/en-BcPniuRQ.js"))}),`./translations/${t}.json`,3);return{data:i,locale:t}}catch{return{data:{},locale:t}}}))}};exports.FIELD_NAME=l;exports.PLUGIN_ID=r;exports.default=g;exports.readValue=p;exports.starterSpec=m;exports.writeValue=h;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const a=require("react"),n=require("react/jsx-runtime"),o=require("@qkix/chartkit-core"),c=(e,t,i)=>{const s=e[t];return s?typeof s=="function"?s():Promise.resolve(s):new Promise((y,u)=>{(typeof queueMicrotask=="function"?queueMicrotask:setTimeout)(u.bind(null,new Error("Unknown variable dynamic import: "+t+(t.split("/").length!==i?". Note that variables only represent file names one level deep.":""))))})},r="chartkit",l="chart",d=({setPlugin:e})=>{const t=a.useRef(e);return a.useEffect(()=>{t.current(r)},[]),null},f=()=>n.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:[n.jsx("path",{d:"M2 14V2",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"}),n.jsx("path",{d:"M2 14h12",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"}),n.jsx("rect",{x:"4",y:"7",width:"2.5",height:"5",fill:"currentColor"}),n.jsx("rect",{x:"8",y:"4",width:"2.5",height:"8",fill:"currentColor"}),n.jsx("rect",{x:"12",y:"9",width:"2.5",height:"3",fill:"currentColor"})]});function p(e){if(e==null||e==="")return{status:"empty"};let t=e;if(typeof e=="string"){try{t=JSON.parse(e)}catch{return{status:"unreadable",reason:"This field contains text that is not JSON."}}if(t===null||t==="")return{status:"empty"}}if(typeof t!="object"||t===null||Array.isArray(t))return{status:"unreadable",reason:"This field contains JSON that is not a chart."};const i=o.migrateChartSpec(t);return i.status==="skipped"?{status:"unreadable",reason:`Chartkit cannot read what is in this field: ${i.reason}.`}:{status:"ok",spec:i.spec}}function h(e){return JSON.stringify(e)}function m(){return{version:o.CHART_SPEC_VERSION,type:"bar",data:{source:"inline",labels:["Q1","Q2","Q3","Q4"],series:[{name:"Series 1",values:[null,null,null,null]}]}}}const g={register(e){e.registerPlugin({id:r,name:r,initializer:d,isReady:!1}),e.customFields.register({name:l,pluginId:r,type:"json",icon:f,intlLabel:{id:`${r}.field.label`,defaultMessage:"Chart"},intlDescription:{id:`${r}.field.description`,defaultMessage:"A chart, rendered as SVG with no client-side JavaScript"},components:{Input:async()=>({default:(await Promise.resolve().then(()=>require("../_chunks/ChartField-CKCDQc6g.js"))).default})},options:{base:[{sectionTitle:{id:`${r}.section.preview`,defaultMessage:"Preview"},items:[{name:"options.previewLocale",type:"text",placeholder:{id:`${r}.previewLocale.placeholder`,defaultMessage:"en-US"},intlLabel:{id:`${r}.previewLocale`,defaultMessage:"Number formatting locale"},description:{id:`${r}.previewLocale.description`,defaultMessage:"How axis numbers are formatted in the admin preview - a BCP 47 tag such as de-DE or pl-PL. Affects the preview only; the front end passes its own locale when it renders. Left empty, the browser's is used."}}]}],advanced:[{sectionTitle:null,items:[{name:"required",type:"checkbox",intlLabel:{id:`${r}.required`,defaultMessage:"Required field"},description:{id:`${r}.required.description`,defaultMessage:"You won't be able to create an entry if this field is empty"}}]}]}})},async registerTrads({locales:e}){return Promise.all(e.map(async t=>{try{const{default:i}=await c(Object.assign({"./translations/en.json":()=>Promise.resolve().then(()=>require("../_chunks/en-BcPniuRQ.js"))}),`./translations/${t}.json`,3);return{data:i,locale:t}}catch{return{data:{},locale:t}}}))}};exports.FIELD_NAME=l;exports.PLUGIN_ID=r;exports.default=g;exports.readValue=p;exports.starterSpec=m;exports.writeValue=h;
@@ -86,7 +86,7 @@ const M = {
86
86
  },
87
87
  components: {
88
88
  // Lazy, so the editor and its data grid are not in the bundle of every
89
- // admin page — only of the ones that actually show a chart field.
89
+ // admin page - only of the ones that actually show a chart field.
90
90
  Input: async () => ({
91
91
  default: (await import("../_chunks/ChartField-N1BDNZ3c.mjs")).default
92
92
  })
@@ -112,7 +112,7 @@ const M = {
112
112
  },
113
113
  description: {
114
114
  id: `${r}.previewLocale.description`,
115
- defaultMessage: "How axis numbers are formatted in the admin preview — a BCP 47 tag such as de-DE or pl-PL. Affects the preview only; the front end passes its own locale when it renders. Left empty, the browser's is used."
115
+ defaultMessage: "How axis numbers are formatted in the admin preview - a BCP 47 tag such as de-DE or pl-PL. Affects the preview only; the front end passes its own locale when it renders. Left empty, the browser's is used."
116
116
  }
117
117
  }
118
118
  ]
@@ -2,7 +2,7 @@
2
2
  * The chart custom field.
3
3
  *
4
4
  * A preview, and a button that opens the editor. The editor itself is
5
- * `@qkix/chartkit-editor` — the same component the chart block inside Better
5
+ * `@qkix/chartkit-editor` - the same component the chart block inside Better
6
6
  * Blocks mounts, which is why it is a package rather than living in one of the
7
7
  * two plugins that need it.
8
8
  *
@@ -10,7 +10,7 @@ declare const _default: {
10
10
  export default _default;
11
11
  /**
12
12
  * Re-exported so an app that stores a chart in this field and renders it
13
- * elsewhere — a preview route, a custom admin page — does not need a second
13
+ * elsewhere - a preview route, a custom admin page - does not need a second
14
14
  * install to get at the spec's type or to draw one.
15
15
  */
16
16
  export { readValue, writeValue, starterSpec } from './value';
@@ -4,8 +4,8 @@
4
4
  * Small, and separated out anyway, because it is where the field can lose
5
5
  * someone's work. Strapi hands a custom field whatever is in the document: a
6
6
  * JSON string on one code path and an already-parsed object on another, `null`
7
- * for a new entry, and — for a field that used to be something else, or a
8
- * document written by an import script — text that is not a chart at all.
7
+ * for a new entry, and - for a field that used to be something else, or a
8
+ * document written by an import script - text that is not a chart at all.
9
9
  *
10
10
  * None of that may throw. A field that throws while rendering takes the whole
11
11
  * edit view down, and the author loses every other field on the page along with
@@ -35,14 +35,14 @@ export type ReadValue = {
35
35
  * Migrates on the way in, so an author opening a chart saved against an older
36
36
  * schema edits the current shape and saves the current shape. `renderChart`
37
37
  * migrates in memory too, which is what keeps the published page correct in the
38
- * meantime — but a document only stops being old once someone writes it back.
38
+ * meantime - but a document only stops being old once someone writes it back.
39
39
  */
40
40
  export declare function readValue(value: unknown): ReadValue;
41
41
  /**
42
42
  * The value to store.
43
43
  *
44
44
  * A string, because Strapi's `json` custom fields are read back as strings and
45
- * writing an object here makes the field's own round trip asymmetric — it would
45
+ * writing an object here makes the field's own round trip asymmetric - it would
46
46
  * work until the page is reloaded.
47
47
  */
48
48
  export declare function writeValue(spec: ChartSpec): string;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Deliberately just a registration. Charts render from their spec, so there is
5
5
  * no chart data to store beyond the field itself, no routes to serve it over
6
- * and no service to compute it — a Chartkit content type would be a second
6
+ * and no service to compute it - a Chartkit content type would be a second
7
7
  * place for the same numbers to live and drift.
8
8
  */
9
9
  declare const _default: {
@@ -2,7 +2,7 @@ import type { Core } from '@strapi/strapi';
2
2
  /**
3
3
  * Registers the chart custom field.
4
4
  *
5
- * `json`, because a chart is a spec — a type, some labels, some series — and
5
+ * `json`, because a chart is a spec - a type, some labels, some series - and
6
6
  * the alternatives are worse. A relation would need a content type nobody
7
7
  * asked for; text would need every consumer to parse it and guess at what
8
8
  * failure means.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qkix/strapi-plugin-chartkit",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Charts for Strapi v5, rendered as server-side SVG with no client-side JavaScript",
5
5
  "strapi": {
6
6
  "kind": "plugin",
@@ -43,8 +43,8 @@
43
43
  "dist"
44
44
  ],
45
45
  "dependencies": {
46
- "@qkix/chartkit-core": "^0.2.3",
47
- "@qkix/chartkit-editor": "^0.1.5"
46
+ "@qkix/chartkit-editor": "^0.1.5",
47
+ "@qkix/chartkit-core": "^0.2.4"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@strapi/sdk-plugin": "^5.3.2",
@@ -59,9 +59,9 @@
59
59
  "styled-components": "^6.1.17",
60
60
  "typescript": "^5.8.3",
61
61
  "vitest": "^3.2.4",
62
- "@qkix/eslint-config": "0.0.0",
62
+ "@qkix/tsconfig": "0.0.0",
63
63
  "@qkix/strapi-test-harness": "0.0.0",
64
- "@qkix/tsconfig": "0.0.0"
64
+ "@qkix/eslint-config": "0.0.0"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@strapi/admin": "^5.0.0",