@onetype/framework 2.0.52 → 2.0.54

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.
Files changed (154) hide show
  1. package/addons/core/database/back/items/commands/create.js +1 -1
  2. package/addons/core/database/back/items/commands/delete.js +3 -3
  3. package/addons/core/database/back/items/commands/update.js +1 -1
  4. package/addons/float/popup/css/popup.css +2 -2
  5. package/addons/render/directives/front/functions/process.js +3 -1
  6. package/addons/render/directives/front/items/self/160-slot.js +9 -1
  7. package/addons/render/directives/front/items/self/660-form.js +19 -13
  8. package/addons/render/editor/addon.js +13 -0
  9. package/addons/render/editor/functions/block/add.js +57 -0
  10. package/addons/render/editor/functions/block/delete.js +32 -0
  11. package/addons/render/editor/functions/block/find.js +30 -0
  12. package/addons/render/editor/functions/render/block.js +114 -0
  13. package/addons/render/editor/functions/render/blocks.js +31 -0
  14. package/addons/render/editor/items/elements/editor.js +69 -0
  15. package/addons/render/editor/items/self/paragraph.js +21 -0
  16. package/addons/render/editor/styles/editor.css +252 -0
  17. package/addons/render/elements/front/functions/types/colors/badge.js +19 -0
  18. package/addons/render/elements/front/functions/types/colors/status.js +24 -0
  19. package/addons/render/elements/front/functions/types/escape.js +8 -0
  20. package/addons/render/elements/front/functions/types/format/currency.js +16 -0
  21. package/addons/render/elements/front/functions/types/format/date.js +16 -0
  22. package/addons/render/elements/front/functions/types/format/number.js +24 -0
  23. package/addons/render/elements/front/functions/types/format/timeago.js +43 -0
  24. package/addons/render/elements/front/functions/types/render/avatar.js +10 -0
  25. package/addons/render/elements/front/functions/types/render/badge.js +17 -0
  26. package/addons/render/elements/front/functions/types/render/boolean.js +8 -0
  27. package/addons/render/elements/front/functions/types/render/chip.js +6 -0
  28. package/addons/render/elements/front/functions/types/render/currency.js +7 -0
  29. package/addons/render/elements/front/functions/types/render/date.js +6 -0
  30. package/addons/render/elements/front/functions/types/render/description.js +12 -0
  31. package/addons/render/elements/front/functions/types/render/group.js +11 -0
  32. package/addons/render/elements/front/functions/types/render/icon.js +8 -0
  33. package/addons/render/elements/front/functions/types/render/image.js +9 -0
  34. package/addons/render/elements/front/functions/types/render/link.js +8 -0
  35. package/addons/render/elements/front/functions/types/render/media.js +16 -0
  36. package/addons/render/elements/front/functions/types/render/metric.js +13 -0
  37. package/addons/render/elements/front/functions/types/render/number.js +6 -0
  38. package/addons/render/elements/front/functions/types/render/progress.js +13 -0
  39. package/addons/render/elements/front/functions/types/render/status.js +12 -0
  40. package/addons/render/elements/front/functions/types/render/tag.js +6 -0
  41. package/addons/render/elements/front/functions/types/render/tags.js +11 -0
  42. package/addons/render/elements/front/functions/types/render/text.js +8 -0
  43. package/addons/render/elements/front/functions/types/render/timeago.js +6 -0
  44. package/addons/render/elements/front/functions/types/render.js +16 -0
  45. package/addons/render/elements/front/items/directives/element.js +8 -3
  46. package/addons/render/elements/front/items/self/cards/info/info.css +499 -0
  47. package/addons/render/elements/front/items/self/cards/info/info.js +224 -0
  48. package/addons/render/elements/front/items/self/cards/item/item.css +614 -0
  49. package/addons/render/elements/front/items/self/cards/item/item.js +200 -0
  50. package/addons/render/elements/front/items/self/cards/pricing/pricing.css +318 -46
  51. package/addons/render/elements/front/items/self/cards/pricing/pricing.js +81 -30
  52. package/addons/render/elements/front/items/self/cards/profile/profile.css +446 -0
  53. package/addons/render/elements/front/items/self/cards/profile/profile.js +186 -0
  54. package/addons/render/elements/front/items/self/cards/share/share.css +445 -0
  55. package/addons/render/elements/front/items/self/cards/share/share.js +316 -0
  56. package/addons/render/elements/front/items/self/cards/stat/stat.css +356 -0
  57. package/addons/render/elements/front/items/self/cards/stat/stat.js +146 -0
  58. package/addons/render/elements/front/items/self/charts/bar/bar.css +263 -0
  59. package/addons/render/elements/front/items/self/charts/bar/bar.js +156 -0
  60. package/addons/render/elements/front/items/self/charts/donut/donut.css +222 -0
  61. package/addons/render/elements/front/items/self/charts/donut/donut.js +164 -0
  62. package/addons/render/elements/front/items/self/charts/line/line.css +229 -0
  63. package/addons/render/elements/front/items/self/charts/line/line.js +249 -0
  64. package/addons/render/elements/front/items/self/charts/sparkline/sparkline.css +102 -0
  65. package/addons/render/elements/front/items/self/charts/sparkline/sparkline.js +164 -0
  66. package/addons/render/elements/front/items/self/core/builder/builder.css +71 -116
  67. package/addons/render/elements/front/items/self/core/builder/builder.js +212 -127
  68. package/addons/render/elements/front/items/self/core/repeater/repeater.css +338 -71
  69. package/addons/render/elements/front/items/self/core/repeater/repeater.js +191 -44
  70. package/addons/render/elements/front/items/self/data/filters/filters.css +541 -0
  71. package/addons/render/elements/front/items/self/data/filters/filters.js +504 -0
  72. package/addons/render/elements/front/items/self/data/table/table.css +588 -0
  73. package/addons/render/elements/front/items/self/data/table/table.js +589 -0
  74. package/addons/render/elements/front/items/self/form/button/button.css +430 -103
  75. package/addons/render/elements/front/items/self/form/button/button.js +109 -101
  76. package/addons/render/elements/front/items/self/form/checkbox/checkbox.css +356 -39
  77. package/addons/render/elements/front/items/self/form/checkbox/checkbox.js +109 -75
  78. package/addons/render/elements/front/items/self/form/color/color.css +255 -61
  79. package/addons/render/elements/front/items/self/form/color/color.js +135 -41
  80. package/addons/render/elements/front/items/self/form/date/date.css +289 -38
  81. package/addons/render/elements/front/items/self/form/date/date.js +108 -24
  82. package/addons/render/elements/front/items/self/form/editor/editor.css +447 -0
  83. package/addons/render/elements/front/items/self/form/editor/editor.js +794 -0
  84. package/addons/render/elements/front/items/self/form/field/field.css +160 -29
  85. package/addons/render/elements/front/items/self/form/field/field.js +36 -16
  86. package/addons/render/elements/front/items/self/form/input/input.css +272 -32
  87. package/addons/render/elements/front/items/self/form/input/input.js +324 -124
  88. package/addons/render/elements/front/items/self/form/radio/radio.css +310 -45
  89. package/addons/render/elements/front/items/self/form/radio/radio.js +99 -80
  90. package/addons/render/elements/front/items/self/form/rating/rating.css +234 -57
  91. package/addons/render/elements/front/items/self/form/rating/rating.js +216 -86
  92. package/addons/render/elements/front/items/self/form/section/section.css +247 -32
  93. package/addons/render/elements/front/items/self/form/section/section.js +53 -16
  94. package/addons/render/elements/front/items/self/form/select/select.css +362 -64
  95. package/addons/render/elements/front/items/self/form/select/select.js +156 -30
  96. package/addons/render/elements/front/items/self/form/slider/slider.css +331 -123
  97. package/addons/render/elements/front/items/self/form/slider/slider.js +124 -26
  98. package/addons/render/elements/front/items/self/form/tags/tags.css +328 -53
  99. package/addons/render/elements/front/items/self/form/tags/tags.js +155 -28
  100. package/addons/render/elements/front/items/self/form/textarea/textarea.css +128 -27
  101. package/addons/render/elements/front/items/self/form/textarea/textarea.js +172 -113
  102. package/addons/render/elements/front/items/self/form/toggle/toggle.css +239 -39
  103. package/addons/render/elements/front/items/self/form/toggle/toggle.js +32 -17
  104. package/addons/render/elements/front/items/self/form/transfer/transfer.css +377 -0
  105. package/addons/render/elements/front/items/self/form/transfer/transfer.js +453 -0
  106. package/addons/render/elements/front/items/self/form/upload/upload.css +408 -0
  107. package/addons/render/elements/front/items/self/form/upload/upload.js +469 -0
  108. package/addons/render/elements/front/items/self/global/accordion/accordion.css +377 -0
  109. package/addons/render/elements/front/items/self/global/accordion/accordion.js +135 -0
  110. package/addons/render/elements/front/items/self/global/code/code.css +207 -44
  111. package/addons/render/elements/front/items/self/global/code/code.js +327 -19
  112. package/addons/render/elements/front/items/self/global/gallery/gallery.css +521 -0
  113. package/addons/render/elements/front/items/self/global/gallery/gallery.js +291 -0
  114. package/addons/render/elements/front/items/self/global/heading/heading.css +151 -49
  115. package/addons/render/elements/front/items/self/global/heading/heading.js +30 -15
  116. package/addons/render/elements/front/items/self/global/markdown/markdown.css +284 -135
  117. package/addons/render/elements/front/items/self/global/markdown/markdown.js +35 -5
  118. package/addons/render/elements/front/items/self/global/menu/menu.css +311 -56
  119. package/addons/render/elements/front/items/self/global/menu/menu.js +95 -47
  120. package/addons/render/elements/front/items/self/global/notice/notice.css +263 -23
  121. package/addons/render/elements/front/items/self/global/notice/notice.js +51 -11
  122. package/addons/render/elements/front/items/self/global/parameters/parameters.css +276 -33
  123. package/addons/render/elements/front/items/self/global/parameters/parameters.js +86 -16
  124. package/addons/render/elements/front/items/self/global/tags/tags.css +215 -29
  125. package/addons/render/elements/front/items/self/global/tags/tags.js +91 -17
  126. package/addons/render/elements/front/items/self/navigation/dock/dock.css +221 -0
  127. package/addons/render/elements/front/items/self/navigation/dock/dock.js +134 -0
  128. package/addons/render/elements/front/items/self/navigation/footer/footer.css +356 -0
  129. package/addons/render/elements/front/items/self/navigation/footer/footer.js +219 -0
  130. package/addons/render/elements/front/items/self/navigation/navbar/navbar.css +736 -76
  131. package/addons/render/elements/front/items/self/navigation/navbar/navbar.js +437 -29
  132. package/addons/render/elements/front/items/self/navigation/sidebar/sidebar.css +327 -196
  133. package/addons/render/elements/front/items/self/navigation/sidebar/sidebar.js +115 -62
  134. package/addons/render/elements/front/items/self/navigation/steps/steps.css +345 -0
  135. package/addons/render/elements/front/items/self/navigation/steps/steps.js +113 -0
  136. package/addons/render/elements/front/items/self/navigation/tabs/tabs.css +507 -33
  137. package/addons/render/elements/front/items/self/navigation/tabs/tabs.js +62 -19
  138. package/addons/render/elements/front/items/self/status/code/code.css +83 -12
  139. package/addons/render/elements/front/items/self/status/code/code.js +15 -4
  140. package/addons/render/elements/front/items/self/status/empty/empty.css +95 -15
  141. package/addons/render/elements/front/items/self/status/empty/empty.js +17 -12
  142. package/addons/render/elements/front/items/self/status/error/error.css +99 -14
  143. package/addons/render/elements/front/items/self/status/error/error.js +21 -11
  144. package/addons/render/elements/front/items/self/status/loading/loading.css +85 -14
  145. package/addons/render/elements/front/items/self/status/loading/loading.js +5 -6
  146. package/addons/render/elements/front/styles/types.css +363 -0
  147. package/instructions.txt +28 -0
  148. package/lib/load.js +1 -0
  149. package/lib/styles/reset.css +89 -76
  150. package/package.json +1 -1
  151. package/addons/render/elements/front/items/self/form/editor-markdown/editor-markdown.css +0 -410
  152. package/addons/render/elements/front/items/self/form/editor-markdown/editor-markdown.js +0 -191
  153. package/addons/render/elements/front/items/self/global/faq/faq.css +0 -98
  154. package/addons/render/elements/front/items/self/global/faq/faq.js +0 -56
@@ -0,0 +1,249 @@
1
+ onetype.AddonReady('elements', (elements) =>
2
+ {
3
+ elements.ItemAdd({
4
+ id: 'charts-line',
5
+ icon: 'show_chart',
6
+ name: 'Line Chart',
7
+ description: 'Premium line chart with multi-series, smooth/step, area fill, dots, grid and legend.',
8
+ category: 'Charts',
9
+ author: 'OneType',
10
+ config: {
11
+ series: {
12
+ type: 'array',
13
+ value: [],
14
+ each: {
15
+ type: 'object',
16
+ config: {
17
+ label: { type: 'string' },
18
+ color: { type: 'string', value: 'brand' },
19
+ values: { type: 'array', each: { type: 'number' } }
20
+ }
21
+ }
22
+ },
23
+ labels: {
24
+ type: 'array',
25
+ value: [],
26
+ each: { type: 'string' }
27
+ },
28
+ title: {
29
+ type: 'string'
30
+ },
31
+ description: {
32
+ type: 'string'
33
+ },
34
+ smooth: {
35
+ type: 'boolean',
36
+ value: true
37
+ },
38
+ fill: {
39
+ type: 'boolean',
40
+ value: true
41
+ },
42
+ showDots: {
43
+ type: 'boolean',
44
+ value: true
45
+ },
46
+ showGrid: {
47
+ type: 'boolean',
48
+ value: true
49
+ },
50
+ showLegend: {
51
+ type: 'boolean',
52
+ value: true
53
+ },
54
+ showLabels: {
55
+ type: 'boolean',
56
+ value: true
57
+ },
58
+ height: {
59
+ type: 'number',
60
+ value: 240
61
+ },
62
+ variant: {
63
+ type: 'array',
64
+ value: ['bg-1', 'border', 'size-m'],
65
+ options: ['bg-1', 'bg-2', 'bg-3', 'bg-4', 'border', 'clean', 'inline', 'size-s', 'size-m', 'size-l']
66
+ }
67
+ },
68
+ render: function()
69
+ {
70
+ this.hasHead = !!this.title || !!this.description;
71
+ this.hasLegend = this.showLegend && this.series.length > 0;
72
+
73
+ this.formatValue = (value) =>
74
+ {
75
+ if(typeof value !== 'number')
76
+ {
77
+ return String(value || '');
78
+ }
79
+
80
+ if(Math.abs(value) >= 1000000)
81
+ {
82
+ return (value / 1000000).toFixed(1) + 'M';
83
+ }
84
+
85
+ if(Math.abs(value) >= 1000)
86
+ {
87
+ return (value / 1000).toFixed(1) + 'k';
88
+ }
89
+
90
+ return String(Math.round(value));
91
+ };
92
+
93
+ this.width = 640;
94
+ this.padding = { top: 12, right: 12, bottom: this.showLabels ? 28 : 12, left: 40 };
95
+
96
+ const allValues = this.series.flatMap(s => s.values || []);
97
+ const max = allValues.length ? Math.max(...allValues) : 1;
98
+ const min = Math.min(0, ...allValues);
99
+ const range = max - min || 1;
100
+ const niceMax = max > 0 ? max * 1.1 : 1;
101
+
102
+ const pointCount = this.labels.length || Math.max(...this.series.map(s => (s.values || []).length), 0);
103
+ const chartWidth = this.width - this.padding.left - this.padding.right;
104
+ const chartHeight = this.height - this.padding.top - this.padding.bottom;
105
+
106
+ const stepX = pointCount > 1 ? chartWidth / (pointCount - 1) : 0;
107
+
108
+ // Build series paths
109
+
110
+ this.computedSeries = this.series.map(series =>
111
+ {
112
+ const values = series.values || [];
113
+ const points = values.map((value, index) =>
114
+ {
115
+ const x = this.padding.left + index * stepX;
116
+ const y = this.padding.top + chartHeight * (1 - (value - min) / (niceMax - min || 1));
117
+ return { x, y, value, index };
118
+ });
119
+
120
+ let linePath = '';
121
+
122
+ if(points.length)
123
+ {
124
+ if(this.smooth)
125
+ {
126
+ linePath = `M ${points[0].x} ${points[0].y}`;
127
+
128
+ for(let i = 0; i < points.length - 1; i++)
129
+ {
130
+ const current = points[i];
131
+ const next = points[i + 1];
132
+ const controlX = (current.x + next.x) / 2;
133
+ linePath += ` C ${controlX} ${current.y}, ${controlX} ${next.y}, ${next.x} ${next.y}`;
134
+ }
135
+ }
136
+ else
137
+ {
138
+ linePath = 'M ' + points.map(p => `${p.x} ${p.y}`).join(' L ');
139
+ }
140
+ }
141
+
142
+ const areaPath = linePath && points.length > 1
143
+ ? linePath + ` L ${points[points.length - 1].x} ${this.padding.top + chartHeight} L ${points[0].x} ${this.padding.top + chartHeight} Z`
144
+ : '';
145
+
146
+ return {
147
+ label: series.label || '',
148
+ color: series.color || 'brand',
149
+ linePath,
150
+ areaPath,
151
+ points
152
+ };
153
+ });
154
+
155
+ // Grid lines
156
+
157
+ this.gridLines = [0, 0.25, 0.5, 0.75, 1].map(fraction =>
158
+ {
159
+ const y = this.padding.top + chartHeight * (1 - fraction);
160
+ const value = min + (niceMax - min) * fraction;
161
+
162
+ return {
163
+ y,
164
+ x1: this.padding.left,
165
+ x2: this.width - this.padding.right,
166
+ label: this.formatValue(value)
167
+ };
168
+ });
169
+
170
+ // X-axis labels
171
+
172
+ this.xLabels = (this.labels || []).map((label, index) => ({
173
+ x: this.padding.left + index * stepX,
174
+ y: this.height - 4,
175
+ label
176
+ }));
177
+
178
+ // Build SVG string — SVG namespace cannot be templated through ot-for
179
+
180
+ const uid = onetype.GenerateUID();
181
+
182
+ const defs = this.computedSeries.map(series => `
183
+ <linearGradient id="line-fill-${uid}-${series.color}" x1="0" x2="0" y1="0" y2="1">
184
+ <stop offset="0%" class="area-start color-${series.color}" />
185
+ <stop offset="100%" class="area-end color-${series.color}" />
186
+ </linearGradient>
187
+ `).join('');
188
+
189
+ const gridEl = this.showGrid
190
+ ? `<g class="grid">
191
+ ${this.gridLines.map(line => `<line x1="${line.x1}" x2="${line.x2}" y1="${line.y}" y2="${line.y}" class="grid-line"></line>`).join('')}
192
+ ${this.gridLines.map(line => `<text x="${this.padding.left - 6}" y="${line.y + 3}" class="grid-label" text-anchor="end">${line.label}</text>`).join('')}
193
+ </g>`
194
+ : '';
195
+
196
+ const seriesEl = this.computedSeries.map(series =>
197
+ {
198
+ const area = (this.fill && series.areaPath) ? `<path class="area" d="${series.areaPath}" fill="url(#line-fill-${uid}-${series.color})" />` : '';
199
+ const line = series.linePath ? `<path class="line" d="${series.linePath}" />` : '';
200
+ const dots = this.showDots ? series.points.map(point => `<circle class="dot" cx="${point.x}" cy="${point.y}" r="3.5"></circle>`).join('') : '';
201
+
202
+ return `<g class="series color-${series.color}">${area}${line}${dots}</g>`;
203
+ }).join('');
204
+
205
+ const labelsEl = this.showLabels
206
+ ? `<g class="labels">${this.xLabels.map(label => `<text x="${label.x}" y="${label.y}" class="x-label" text-anchor="middle">${label.label}</text>`).join('')}</g>`
207
+ : '';
208
+
209
+ const svgString = `
210
+ <svg viewBox="0 0 ${this.width} ${this.height}" preserveAspectRatio="xMidYMid meet">
211
+ <defs>${defs}</defs>
212
+ ${gridEl}
213
+ ${seriesEl}
214
+ ${labelsEl}
215
+ </svg>
216
+ `;
217
+
218
+ this.OnReady(() =>
219
+ {
220
+ const canvas = this.Element.querySelector('.canvas');
221
+
222
+ if(canvas)
223
+ {
224
+ const existing = canvas.querySelector('svg');
225
+ if(existing) existing.remove();
226
+ canvas.insertAdjacentHTML('afterbegin', svgString);
227
+ }
228
+ });
229
+
230
+ return /* html */ `
231
+ <div :class="'holder ' + variant.join(' ')">
232
+ <header ot-if="hasHead" class="head">
233
+ <div ot-if="title" class="title">{{ title }}</div>
234
+ <div ot-if="description" class="description">{{ description }}</div>
235
+ </header>
236
+
237
+ <div class="canvas"></div>
238
+
239
+ <footer ot-if="hasLegend" class="legend">
240
+ <div ot-for="series in computedSeries" :class="'legend-item color-' + series.color">
241
+ <span class="dot-marker"></span>
242
+ <span>{{ series.label }}</span>
243
+ </div>
244
+ </footer>
245
+ </div>
246
+ `;
247
+ }
248
+ });
249
+ });
@@ -0,0 +1,102 @@
1
+ /* Root */
2
+
3
+ .e-161c4111
4
+ {
5
+ display: flex;
6
+ width: 100%;
7
+ }
8
+
9
+ /* Holder */
10
+
11
+ .e-161c4111 > .holder
12
+ {
13
+ display: block;
14
+ width: 100%;
15
+ height: 100%;
16
+ }
17
+
18
+ .e-161c4111 > .holder > svg
19
+ {
20
+ display: block;
21
+ width: 100%;
22
+ height: 100%;
23
+ overflow: visible;
24
+ }
25
+
26
+ /* Line */
27
+
28
+ .e-161c4111 > .holder > svg > .line
29
+ {
30
+ fill: none;
31
+ stroke-width: 1.5;
32
+ stroke-linecap: round;
33
+ stroke-linejoin: round;
34
+ vector-effect: non-scaling-stroke;
35
+ animation: e-161c4111-draw 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards;
36
+ }
37
+
38
+ @keyframes e-161c4111-draw
39
+ {
40
+ from { opacity: 0; }
41
+ to { opacity: 1; }
42
+ }
43
+
44
+ /* Area fill */
45
+
46
+ .e-161c4111 > .holder > svg > .area
47
+ {
48
+ opacity: 0.35;
49
+ animation: e-161c4111-fade 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards;
50
+ }
51
+
52
+ @keyframes e-161c4111-fade
53
+ {
54
+ from { opacity: 0; }
55
+ to { opacity: 0.35; }
56
+ }
57
+
58
+ /* Bars */
59
+
60
+ .e-161c4111 > .holder > svg > .bar
61
+ {
62
+ animation: e-161c4111-rise 0.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
63
+ transform-origin: bottom;
64
+ }
65
+
66
+ @keyframes e-161c4111-rise
67
+ {
68
+ from { opacity: 0; transform: scaleY(0); }
69
+ to { opacity: 1; transform: scaleY(1); }
70
+ }
71
+
72
+ /* ===== COLORS ===== */
73
+
74
+ .e-161c4111 > .holder.color-brand > svg > .line { stroke: var(--ot-brand); }
75
+ .e-161c4111 > .holder.color-brand > svg > .area { fill: var(--ot-brand); }
76
+ .e-161c4111 > .holder.color-brand > svg > .bar { fill: var(--ot-brand); }
77
+ .e-161c4111 > .holder.color-brand > svg > defs .area-start { stop-color: var(--ot-brand); stop-opacity: 0.6; }
78
+ .e-161c4111 > .holder.color-brand > svg > defs .area-end { stop-color: var(--ot-brand); stop-opacity: 0; }
79
+
80
+ .e-161c4111 > .holder.color-blue > svg > .line { stroke: var(--ot-blue); }
81
+ .e-161c4111 > .holder.color-blue > svg > .area { fill: var(--ot-blue); }
82
+ .e-161c4111 > .holder.color-blue > svg > .bar { fill: var(--ot-blue); }
83
+ .e-161c4111 > .holder.color-blue > svg > defs .area-start { stop-color: var(--ot-blue); stop-opacity: 0.6; }
84
+ .e-161c4111 > .holder.color-blue > svg > defs .area-end { stop-color: var(--ot-blue); stop-opacity: 0; }
85
+
86
+ .e-161c4111 > .holder.color-red > svg > .line { stroke: var(--ot-red); }
87
+ .e-161c4111 > .holder.color-red > svg > .area { fill: var(--ot-red); }
88
+ .e-161c4111 > .holder.color-red > svg > .bar { fill: var(--ot-red); }
89
+ .e-161c4111 > .holder.color-red > svg > defs .area-start { stop-color: var(--ot-red); stop-opacity: 0.6; }
90
+ .e-161c4111 > .holder.color-red > svg > defs .area-end { stop-color: var(--ot-red); stop-opacity: 0; }
91
+
92
+ .e-161c4111 > .holder.color-orange > svg > .line { stroke: var(--ot-orange); }
93
+ .e-161c4111 > .holder.color-orange > svg > .area { fill: var(--ot-orange); }
94
+ .e-161c4111 > .holder.color-orange > svg > .bar { fill: var(--ot-orange); }
95
+ .e-161c4111 > .holder.color-orange > svg > defs .area-start { stop-color: var(--ot-orange); stop-opacity: 0.6; }
96
+ .e-161c4111 > .holder.color-orange > svg > defs .area-end { stop-color: var(--ot-orange); stop-opacity: 0; }
97
+
98
+ .e-161c4111 > .holder.color-green > svg > .line { stroke: var(--ot-green); }
99
+ .e-161c4111 > .holder.color-green > svg > .area { fill: var(--ot-green); }
100
+ .e-161c4111 > .holder.color-green > svg > .bar { fill: var(--ot-green); }
101
+ .e-161c4111 > .holder.color-green > svg > defs .area-start { stop-color: var(--ot-green); stop-opacity: 0.6; }
102
+ .e-161c4111 > .holder.color-green > svg > defs .area-end { stop-color: var(--ot-green); stop-opacity: 0; }
@@ -0,0 +1,164 @@
1
+ onetype.AddonReady('elements', (elements) =>
2
+ {
3
+ elements.ItemAdd({
4
+ id: 'charts-sparkline',
5
+ icon: 'show_chart',
6
+ name: 'Sparkline',
7
+ description: 'Minimal inline chart — line, area or bar — for stat cards and tables. No axes, no grid.',
8
+ category: 'Charts',
9
+ author: 'OneType',
10
+ config: {
11
+ values: {
12
+ type: 'array',
13
+ value: [],
14
+ each: { type: 'number' }
15
+ },
16
+ type: {
17
+ type: 'string',
18
+ value: 'area',
19
+ options: ['line', 'area', 'bar']
20
+ },
21
+ color: {
22
+ type: 'string',
23
+ value: 'brand',
24
+ options: ['brand', 'blue', 'red', 'orange', 'green']
25
+ },
26
+ height: {
27
+ type: 'number',
28
+ value: 40
29
+ },
30
+ smooth: {
31
+ type: 'boolean',
32
+ value: true
33
+ },
34
+ variant: {
35
+ type: 'array',
36
+ value: [],
37
+ options: []
38
+ }
39
+ },
40
+ render: function()
41
+ {
42
+ this.width = 120;
43
+ this.padding = 2;
44
+
45
+ const values = this.values.length ? this.values : [0];
46
+ const min = Math.min(...values);
47
+ const max = Math.max(...values);
48
+ const range = max - min || 1;
49
+
50
+ const count = values.length;
51
+ const stepX = count > 1 ? (this.width - this.padding * 2) / (count - 1) : 0;
52
+
53
+ const points = values.map((value, index) =>
54
+ {
55
+ const x = this.padding + index * stepX;
56
+ const y = this.padding + (this.height - this.padding * 2) * (1 - (value - min) / range);
57
+ return { x, y, value };
58
+ });
59
+
60
+ // Smooth bezier path
61
+
62
+ const smoothPath = (pts) =>
63
+ {
64
+ if(pts.length < 2)
65
+ {
66
+ return '';
67
+ }
68
+
69
+ let d = `M ${pts[0].x} ${pts[0].y}`;
70
+
71
+ for(let i = 0; i < pts.length - 1; i++)
72
+ {
73
+ const current = pts[i];
74
+ const next = pts[i + 1];
75
+ const controlX = (current.x + next.x) / 2;
76
+ d += ` C ${controlX} ${current.y}, ${controlX} ${next.y}, ${next.x} ${next.y}`;
77
+ }
78
+
79
+ return d;
80
+ };
81
+
82
+ const linePath = (pts) =>
83
+ {
84
+ if(!pts.length)
85
+ {
86
+ return '';
87
+ }
88
+
89
+ return 'M ' + pts.map(pt => `${pt.x} ${pt.y}`).join(' L ');
90
+ };
91
+
92
+ this.linePath = this.smooth ? smoothPath(points) : linePath(points);
93
+ this.areaPath = this.linePath ? this.linePath + ` L ${points[points.length - 1].x} ${this.height - this.padding} L ${points[0].x} ${this.height - this.padding} Z` : '';
94
+
95
+ // Bar mode
96
+
97
+ this.bars = [];
98
+
99
+ if(this.type === 'bar' && count)
100
+ {
101
+ const gap = 1;
102
+ const barWidth = Math.max(1, (this.width - this.padding * 2 - gap * (count - 1)) / count);
103
+
104
+ this.bars = values.map((value, index) =>
105
+ {
106
+ const normalized = (value - min) / range;
107
+ const h = Math.max(1, normalized * (this.height - this.padding * 2));
108
+ const x = this.padding + index * (barWidth + gap);
109
+ const y = this.height - this.padding - h;
110
+ return { x, y, width: barWidth, height: h };
111
+ });
112
+ }
113
+
114
+ const isLine = this.type === 'line';
115
+ const isArea = this.type === 'area';
116
+ const isBar = this.type === 'bar';
117
+ const gradientId = 'sparkline-fill-' + onetype.GenerateUID();
118
+
119
+ const defs = isArea
120
+ ? `<defs>
121
+ <linearGradient id="${gradientId}" x1="0" x2="0" y1="0" y2="1">
122
+ <stop offset="0%" class="area-start" />
123
+ <stop offset="100%" class="area-end" />
124
+ </linearGradient>
125
+ </defs>`
126
+ : '';
127
+
128
+ const areaEl = (isArea && this.areaPath)
129
+ ? `<path class="area" d="${this.areaPath}" fill="url(#${gradientId})" />`
130
+ : '';
131
+
132
+ const lineEl = ((isLine || isArea) && this.linePath)
133
+ ? `<path class="line" d="${this.linePath}" />`
134
+ : '';
135
+
136
+ const barsEl = this.bars.map(bar => `<rect class="bar" x="${bar.x}" y="${bar.y}" width="${bar.width}" height="${bar.height}" rx="0.5"></rect>`).join('');
137
+
138
+ const svgString = `
139
+ <svg viewBox="0 0 ${this.width} ${this.height}" preserveAspectRatio="none">
140
+ ${defs}
141
+ ${areaEl}
142
+ ${lineEl}
143
+ ${barsEl}
144
+ </svg>
145
+ `;
146
+
147
+ this.OnReady(() =>
148
+ {
149
+ const wrap = this.Element.querySelector('.holder');
150
+
151
+ if(wrap)
152
+ {
153
+ const existing = wrap.querySelector('svg');
154
+ if(existing) existing.remove();
155
+ wrap.insertAdjacentHTML('afterbegin', svgString);
156
+ }
157
+ });
158
+
159
+ return /* html */ `
160
+ <div :class="'holder color-' + color"></div>
161
+ `;
162
+ }
163
+ });
164
+ });