@eodash/eodash 5.1.0 → 5.3.0

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 (133) hide show
  1. package/core/client/components/DashboardLayout.vue +1 -2
  2. package/core/client/components/EodashOverlay.vue +4 -5
  3. package/core/client/components/MobileLayout.vue +42 -21
  4. package/core/client/composables/index.js +54 -60
  5. package/core/client/eodashSTAC/EodashCollection.js +199 -108
  6. package/core/client/eodashSTAC/auth.js +86 -0
  7. package/core/client/eodashSTAC/createLayers.js +234 -4
  8. package/core/client/eodashSTAC/helpers.js +281 -59
  9. package/core/client/eodashSTAC/parquet.js +0 -13
  10. package/core/client/eodashSTAC/triggers.js +1 -1
  11. package/core/client/store/actions.js +14 -0
  12. package/core/client/store/stac.js +46 -8
  13. package/core/client/store/states.js +6 -0
  14. package/core/client/types.ts +206 -3
  15. package/core/client/utils/bands-editor/arithmetic.js +144 -0
  16. package/core/client/utils/bands-editor/colors.js +36 -0
  17. package/core/client/utils/bands-editor/dom.js +196 -0
  18. package/core/client/utils/bands-editor/exampleSchema.json +1320 -0
  19. package/core/client/utils/bands-editor/index.js +68 -0
  20. package/core/client/utils/bands-editor/rgb.js +102 -0
  21. package/core/client/utils/index.js +5 -2
  22. package/core/client/views/Dashboard.vue +1 -1
  23. package/core/client/vite-env.d.ts +122 -0
  24. package/dist/client/{DashboardLayout-ByVs1DrY.js → DashboardLayout-Cq15p4TH.js} +5 -6
  25. package/dist/client/{DynamicWebComponent-C3W7HSQm.js → DynamicWebComponent-Cv-fPRG1.js} +1 -1
  26. package/dist/client/{EodashDatePicker-BIAf1sMT.js → EodashDatePicker-CPlJwEIO.js} +20 -22
  27. package/dist/client/{EodashItemFilter-DPznh8UB.js → EodashItemFilter-Ydebgbjj.js} +46 -31
  28. package/dist/client/EodashLayerControl-COhrkNEs.js +1517 -0
  29. package/dist/client/{EodashLayoutSwitcher-C5qTEffW.js → EodashLayoutSwitcher-pnKhTRZV.js} +4 -4
  30. package/dist/client/EodashMapBtns-Cj0Fx119.js +301 -0
  31. package/dist/client/{EodashStacInfo-CSvvF2jI.js → EodashStacInfo-Dadkg_Nj.js} +1 -1
  32. package/dist/client/EodashTimeSlider-CpoHX0S7.js +53 -0
  33. package/dist/client/{EodashTools-Cv1SXQ5y.js → EodashTools-UGBG7KC9.js} +10 -7
  34. package/dist/client/{ExportState-D-iuwaad.js → ExportState-GtJkAqeZ.js} +145 -121
  35. package/dist/client/{Footer-CyF0zRAk.js → Footer-D3ZPG5c4.js} +1 -1
  36. package/dist/client/{Header-CgD8jDKU.js → Header-z6AK-wpN.js} +2 -3
  37. package/dist/client/MobileLayout-BXNsNftb.js +118 -0
  38. package/dist/client/{PopUp-BsYLvWch.js → PopUp-BbQdjENV.js} +79 -44
  39. package/dist/client/{ProcessList-C2xsLU2_.js → ProcessList-C6VsdsYI.js} +18 -12
  40. package/dist/client/{VImg-OHe8YTs2.js → VImg-CxaMSB99.js} +203 -5
  41. package/dist/client/{VMain-PryTLU4a.js → VMain-Ds7yw0wj.js} +1 -1
  42. package/dist/client/{VTooltip-DZ0fjpB3.js → VTooltip-Cze6CEVh.js} +2 -3
  43. package/dist/client/{WidgetsContainer-B9LBadcC.js → WidgetsContainer-D66bj-JJ.js} +1 -1
  44. package/dist/client/asWebComponent-CWbNRdf9.js +8895 -0
  45. package/dist/client/{async-DkSu_u2K.js → async-BA7oWCMX.js} +69 -5
  46. package/dist/client/easing-CH0-9wR8.js +35 -0
  47. package/dist/client/eo-dash.js +1 -1
  48. package/dist/client/{VOverlay-yUn7p-Uf.js → forwardRefs-BUfxOIo-.js} +308 -28
  49. package/dist/client/{handling-CgmFXkW6.js → handling-DlNTtKB-.js} +27 -6
  50. package/dist/client/{helpers-Dy0Q13tP.js → helpers-CtE0W7iu.js} +595 -278
  51. package/dist/client/{index-skjhlH8u.js → index-CeEZIjO6.js} +26 -13
  52. package/dist/client/{index-Ch_HchK3.js → index-CsKbRDeN.js} +238 -77
  53. package/dist/client/{index-Dqj4tbx2.js → index-D4_NRKrf.js} +2 -2
  54. package/dist/client/index-DeECc3lV.js +571 -0
  55. package/dist/client/material-symbols-outlined.woff2 +0 -0
  56. package/dist/client/material-symbols-rounded.woff2 +0 -0
  57. package/dist/client/material-symbols-sharp.woff2 +0 -0
  58. package/dist/client/material-symbols-subset.woff2 +0 -0
  59. package/dist/client/templates.js +106 -49
  60. package/dist/client/{transition-C98Yn4Vo.js → transition-Byvp3L6Y.js} +1 -1
  61. package/dist/node/cli.js +6 -6
  62. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +24 -10
  63. package/dist/types/core/client/eodashSTAC/auth.d.ts +7 -0
  64. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +15 -3
  65. package/dist/types/core/client/eodashSTAC/helpers.d.ts +51 -15
  66. package/dist/types/core/client/plugins/vuetify.d.ts +14 -14
  67. package/dist/types/core/client/store/actions.d.ts +2 -0
  68. package/dist/types/core/client/store/stac.d.ts +16 -7
  69. package/dist/types/core/client/store/states.d.ts +4 -0
  70. package/dist/types/core/client/types.d.ts +171 -3
  71. package/dist/types/core/client/utils/bands-editor/arithmetic.d.ts +8 -0
  72. package/dist/types/core/client/utils/bands-editor/colors.d.ts +15 -0
  73. package/dist/types/core/client/utils/bands-editor/dom.d.ts +42 -0
  74. package/dist/types/core/client/utils/bands-editor/index.d.ts +20 -0
  75. package/dist/types/core/client/utils/bands-editor/rgb.d.ts +15 -0
  76. package/dist/types/core/client/utils/index.d.ts +1 -1
  77. package/dist/types/templates/baseConfig.d.ts +87 -1
  78. package/dist/types/templates/compare.d.ts +0 -25
  79. package/dist/types/templates/expert.d.ts +17 -21
  80. package/dist/types/templates/explore.d.ts +67 -0
  81. package/dist/types/templates/index.d.ts +1 -1
  82. package/dist/types/templates/{light.d.ts → lite.d.ts} +9 -0
  83. package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +21 -0
  84. package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +49 -0
  85. package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +4 -0
  86. package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +12 -0
  87. package/dist/types/widgets/EodashItemCatalog/types.d.ts +14 -0
  88. package/dist/types/widgets/{EodashMapBtns.vue.d.ts → EodashMap/EodashMapBtns.vue.d.ts} +6 -0
  89. package/dist/types/widgets/EodashMap/index.vue.d.ts +114 -0
  90. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
  91. package/dist/types/widgets/EodashMap/methods/index.d.ts +1 -1
  92. package/dist/types/widgets/EodashProcess/methods/async.d.ts +1 -0
  93. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
  94. package/dist/types/widgets/EodashTimeSlider.vue.d.ts +7 -0
  95. package/dist/types/widgets/EodashTools.vue.d.ts +10 -10
  96. package/dist/types/widgets/ExportState.vue.d.ts +2 -0
  97. package/package.json +31 -28
  98. package/templates/baseConfig.js +10 -5
  99. package/templates/compare.js +2 -22
  100. package/templates/expert.js +19 -18
  101. package/templates/explore.js +62 -0
  102. package/templates/index.js +1 -1
  103. package/templates/{light.js → lite.js} +11 -2
  104. package/widgets/EodashDatePicker.vue +15 -18
  105. package/widgets/EodashItemCatalog/index.vue +161 -0
  106. package/widgets/EodashItemCatalog/methods/filters.js +216 -0
  107. package/widgets/EodashItemCatalog/methods/handlers.js +50 -0
  108. package/widgets/EodashItemCatalog/methods/map.js +144 -0
  109. package/widgets/EodashItemCatalog/types.ts +15 -0
  110. package/widgets/EodashItemFilter.vue +35 -28
  111. package/widgets/EodashLayerControl.vue +10 -6
  112. package/widgets/EodashLayoutSwitcher.vue +1 -1
  113. package/widgets/EodashMap/EodashMapBtns.vue +278 -0
  114. package/widgets/EodashMap/index.vue +263 -38
  115. package/widgets/EodashMap/methods/create-layers-config.js +9 -6
  116. package/widgets/EodashMap/methods/index.js +27 -13
  117. package/widgets/EodashProcess/ProcessList.vue +13 -1
  118. package/widgets/EodashProcess/index.vue +17 -1
  119. package/widgets/EodashProcess/methods/async.js +22 -1
  120. package/widgets/EodashProcess/methods/custom-endpoints/chart/veda-endpoint.js +25 -3
  121. package/widgets/EodashProcess/methods/handling.js +2 -0
  122. package/widgets/EodashProcess/methods/outputs.js +1 -0
  123. package/widgets/EodashProcess/methods/utils.js +45 -1
  124. package/widgets/EodashTimeSlider.vue +40 -0
  125. package/widgets/EodashTools.vue +7 -3
  126. package/widgets/ExportState.vue +53 -22
  127. package/dist/client/EodashLayerControl-Bhxjw4V2.js +0 -154
  128. package/dist/client/EodashMapBtns-WoGq8MuV.js +0 -173
  129. package/dist/client/MobileLayout-EKQ_kpSh.js +0 -1226
  130. package/dist/client/asWebComponent-By_7_JjS.js +0 -19193
  131. package/dist/client/forwardRefs-BXxrv98s.js +0 -272
  132. package/dist/client/index-BuhOHXKv.js +0 -199
  133. package/widgets/EodashMapBtns.vue +0 -155
@@ -109,6 +109,14 @@ export interface TEodashDatePicker {
109
109
  >["$props"];
110
110
  }
111
111
 
112
+ /** @group Widgets */
113
+ export interface TEodashTimeSlider {
114
+ name: "EodashTimeSlider";
115
+ properties?: InstanceType<
116
+ typeof import("^/EodashTimeSlider.vue").default
117
+ >["$props"];
118
+ }
119
+
112
120
  /** @group Widgets */
113
121
  export interface TEodashItemFilter {
114
122
  name: "EodashItemFilter";
@@ -164,6 +172,12 @@ export interface TEodashLayoutSwitcher {
164
172
  typeof import("^/EodashLayoutSwitcher.vue").default
165
173
  >["$props"];
166
174
  }
175
+ export interface TEodashItemCatalog {
176
+ name: "EodashItemCatalog";
177
+ properties?: InstanceType<
178
+ typeof import("^/EodashItemCatalog/index.vue").default
179
+ >["$props"];
180
+ }
167
181
 
168
182
  /** @group Widgets */
169
183
  export interface TExportState {
@@ -201,9 +215,11 @@ export type ComponentWidget =
201
215
  | TEodashMapBtns
202
216
  | TEodashTools
203
217
  | TEodashLayoutSwitcher
218
+ | TEodashItemCatalog
204
219
  | TExportState
205
220
  | TPopUp
206
- | TWidgetsContainer;
221
+ | TWidgetsContainer
222
+ | TEodashTimeSlider;
207
223
  /**
208
224
  * Widget type: `internal` API. Internal widgets are Vue components provided by
209
225
  * eodash.
@@ -299,7 +315,14 @@ export interface Template {
299
315
  export type MultiTemplates = Record<string, Template>;
300
316
 
301
317
  /** @ignore */
302
- export type StacEndpoint = `${string}/catalog.json`;
318
+ export type StacEndpoint =
319
+ | string
320
+ | {
321
+ endpoint: string;
322
+ api?: boolean;
323
+ rasterEndpoint?: string;
324
+ vectorEndpoint?: string;
325
+ };
303
326
 
304
327
  /** @group Eodash */
305
328
  export interface EodashFont {
@@ -444,7 +467,11 @@ export type EodashStyleJson = import("ol/style/flat").FlatStyleLike & {
444
467
  jsonform?: import("json-schema").JSONSchema7;
445
468
  tooltip?: { id: string; title?: string; appendix?: string }[];
446
469
  };
447
-
470
+ export type EodashRasterJSONForm = {
471
+ jsonform: Record<string, any>;
472
+ legend?: import("@eox/layercontrol/src/components/layer-config.js").EOxLayerControlLayerConfig["layerConfig"]["legend"];
473
+ };
474
+ /** @ignore */
448
475
  export type LayersEventBusKeys =
449
476
  | "layers:updated"
450
477
  | "time:updated"
@@ -452,3 +479,179 @@ export type LayersEventBusKeys =
452
479
  | "compareLayers:updated"
453
480
  | "compareTime:updated"
454
481
  | "compareProcess:updated";
482
+
483
+ //// STAC API types
484
+ /** @ignore */
485
+ export interface SearchParams {
486
+ /** Collection IDs to search within */
487
+ collections: string[];
488
+ /** Query parameters */
489
+ query: {
490
+ datetime?: {
491
+ eq?: string;
492
+ in?: string[];
493
+ };
494
+ geometry?: Record<string, any>;
495
+ };
496
+ /** Maximum number of results to return */
497
+ limit?: number;
498
+ }
499
+ export interface StacItemsAPIResponse {
500
+ type: "FeatureCollection";
501
+ features: import("stac-ts").StacItem[];
502
+ }
503
+ /** @ignore */
504
+ export interface Render {
505
+ /** REQUIRED. Array of asset keys referencing the assets that are used to make the rendering */
506
+ assets: string[];
507
+ /** Optional title of the rendering */
508
+ title?: string;
509
+ /** 2 dimensions array of delimited Min,Max range per band. If not provided, the data will not be rescaled. */
510
+ rescale?: number[][];
511
+ /** Nodata value to use for the referenced assets. */
512
+ nodata?: number | string;
513
+ /** Color map identifier that must be applied for a raster band */
514
+ colormap_name?: string;
515
+ /** Color map JSON definition that must be applied for a raster band */
516
+ colormap?: Record<string, unknown>;
517
+ /** Color formula that must be applied for a raster band */
518
+ color_formula?: string;
519
+ /** Resampling algorithm to apply to the referenced assets. See GDAL resampling algorithm for some examples. */
520
+ resampling?: string;
521
+ /** Band arithmetic formula to apply to the referenced assets. */
522
+ expression?: string;
523
+ /** Zoom levels range applicable for the visualization */
524
+ minmax_zoom?: number[];
525
+ }
526
+ /** @ignore */
527
+ export interface TitilerSTACParameters {
528
+ /** STAC Item URL. Required */
529
+ url: string;
530
+ /** asset names. */
531
+ assets?: string[];
532
+ /** rio-tiler's math expression with asset names (e.g Asset1_b1/Asset2_b1). */
533
+ expression?: string;
534
+ /** tell rio-tiler that each asset is a 1 band dataset, so expression Asset1/Asset2 can be passed. */
535
+ asset_as_band?: boolean;
536
+ /** Per asset band math expression (e.g Asset1|1,2,3). */
537
+ asset_bidx?: string[];
538
+ /** Overwrite internal Nodata value. */
539
+ nodata?: string | number;
540
+ /** Apply dataset internal Scale/Offset. */
541
+ unscale?: boolean;
542
+ /** RasterIO resampling algorithm. Defaults to nearest. */
543
+ resampling?: string;
544
+ /** WarpKernel resampling algorithm (only used when doing re-projection). Defaults to nearest. */
545
+ reproject?: string;
546
+ /** Comma (',') delimited Min,Max range (e.g rescale=0,1000, rescale=0,1000&rescale=0,3000&rescale=0,2000). */
547
+ rescale?: string[];
548
+ /** rio-color formula. */
549
+ color_formula?: string;
550
+ /** JSON encoded custom Colormap. */
551
+ colormap?: string;
552
+ /** rio-tiler color map name. */
553
+ colormap_name?: string;
554
+ /** Add mask to the output data. Default is True. */
555
+ return_mask?: boolean;
556
+ /** Buffer on each side of the given tile. It must be a multiple of 0.5. Output tilesize will be expanded to tilesize + 2 * buffer (e.g 0.5 = 257x257, 1.0 = 258x258). */
557
+ buffer?: number;
558
+ /** Padding to apply to each tile edge. Helps reduce resampling artefacts along edges. Defaults to 0. */
559
+ padding?: number;
560
+ /** Custom algorithm name (e.g hillshade). */
561
+ algorithm?: string;
562
+ /** JSON encoded algorithm parameters. */
563
+ algorithm_params?: string;
564
+ }
565
+ /**
566
+ * Generic GeoJSON Feature interface that can hold additional properties.
567
+ * @ignore
568
+ */
569
+ export interface GeoJsonFeature<T = Record<string, any>, G = GeoJSON.Geometry> {
570
+ type: "Feature";
571
+ geometry: G;
572
+ properties: T & Record<string, any>;
573
+ id?: string | number;
574
+ }
575
+
576
+ /**
577
+ * Generic GeoJSON FeatureCollection interface that can hold additional properties.
578
+ * @ignore
579
+ */
580
+ export interface GeoJsonFeatureCollection<
581
+ T = Record<string, any>,
582
+ G = GeoJSON.Geometry,
583
+ > {
584
+ type: "FeatureCollection";
585
+ features: Array<GeoJsonFeature<T, G>>;
586
+ properties?: T & Record<string, any>;
587
+ }
588
+ /**
589
+ * Partial STAC Authentication Extension v1.1.0
590
+ * Generated from https://stac-extensions.github.io/authentication/v1.1.0/schema.json
591
+ */
592
+
593
+ export interface AuthScheme {
594
+ /** Scheme keyword, e.g. http, s3, signedUrl, oauth2, apiKey, openIdConnect */
595
+ type:
596
+ | "http"
597
+ | "s3"
598
+ | "signedUrl"
599
+ | "oauth2"
600
+ | "apiKey"
601
+ | "openIdConnect"
602
+ | string;
603
+
604
+ description?: string;
605
+
606
+ name?: string;
607
+ in?: string;
608
+
609
+ scheme?: string;
610
+
611
+ flows?: Record<string, OAuth2Flow | SignedUrlFlow>;
612
+
613
+ openIdConnectUrl?: string;
614
+ }
615
+
616
+ export interface OAuth2Flow {
617
+ authorizationUrl?: string;
618
+ tokenUrl?: string;
619
+ refreshUrl?: string;
620
+ scopes: Record<string, string>;
621
+ }
622
+
623
+
624
+ /** Signed URL flow configuration */
625
+ export interface SignedUrlFlow {
626
+ authorizationApi: string;
627
+ method: string;
628
+ responseField?: string;
629
+ parameters?: Record<
630
+ string,
631
+ {
632
+ in: "query" | "header" | "body" | string;
633
+ required: boolean;
634
+ description?: string;
635
+ schema?: object;
636
+ }
637
+ >;
638
+ }
639
+
640
+ export interface ApiKeyAuthScheme extends AuthScheme {
641
+ type: "apiKey",
642
+ name: string;
643
+ in: string;
644
+ }
645
+
646
+ import { StacItem, StacLink, StacAsset } from "stac-ts";
647
+ export interface StacAuthItem extends StacItem {
648
+ "auth:schemes": {
649
+ [key: string]: AuthScheme;
650
+ },
651
+ }
652
+ export interface StacAuthLink extends StacLink {
653
+ "auth:refs": [string],
654
+ }
655
+ export interface StacAuthAsset extends StacAsset {
656
+ "auth:refs": [string],
657
+ }
@@ -0,0 +1,144 @@
1
+ import { addDraggableBands } from "./dom.js";
2
+ import { createSlotStyles, createSlot, fillSlotWithBand } from "./dom";
3
+ const MUSTACHE_REGEX = /\{\{([^}]+)\}\}/g;
4
+ /**
5
+ * Build the arithmetic expression interface
6
+ * @param {import("./index.js").BandsEditor} editor - The editor instance
7
+ * @param {Array<string>} colors - Array of color strings
8
+ * @param {Array<string>} bands - Array of band identifiers
9
+ * @param {Array<string>} bandTitles - Array of band titles
10
+ */
11
+ export function buildArithmeticInterface(editor, colors, bands, bandTitles) {
12
+ const formulaTemplate = editor.schema.formulaTemplate || "{{A}}";
13
+
14
+ const style = createSlotStyles(bands, colors);
15
+ editor.control?.appendChild(style);
16
+
17
+ addDraggableBands(editor, bands, bandTitles);
18
+
19
+ editor.control?.appendChild(document.createElement("hr"));
20
+
21
+ // Add formula display with embedded slots after the bands
22
+ addFormulaSlots(editor, formulaTemplate, bands, bandTitles);
23
+ }
24
+
25
+ /**
26
+ * Generate the final formula string with band values substituted
27
+ * @param {import("./index.js").BandsEditor} editor - The editor instance
28
+ * @returns {string} Formula string with substituted values
29
+ */
30
+ function generateFormulaString(editor) {
31
+ /** @type {string} */
32
+ const formulaTemplate = editor.schema.formulaTemplate || "{{A}}";
33
+
34
+ const variableValues = editor.variableValues || {};
35
+
36
+ return formulaTemplate.replace(MUSTACHE_REGEX, (match, variable) => {
37
+ const bandValue = variableValues[variable.trim()];
38
+ return bandValue || match; // Keep placeholder if no value assigned
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Add formula display with embedded circular slots
44
+ * @param {import("./index.js").BandsEditor} editor - The editor instance
45
+ * @param {string} template - Formula template string
46
+ * @param {Array<string>} bands - Array of band identifiers
47
+ * @param {Array<string>} bandTitles - Array of band titles
48
+ */
49
+ function addFormulaSlots(editor, template, bands, bandTitles) {
50
+ const formulaContainer = document.createElement("div");
51
+ formulaContainer.classList.add("slots-container");
52
+
53
+ // Initialize slots tracking
54
+ editor.variableSlots = {};
55
+
56
+ // Split the template into parts and create elements
57
+ const parts = template.split(/(\{\{[^}]+\}\})/);
58
+
59
+ parts.forEach((part) => {
60
+ if (!part) {
61
+ return;
62
+ }
63
+
64
+ if (!part.match(MUSTACHE_REGEX)) {
65
+ // This is formula text
66
+ part = part.trim();
67
+ if (part) {
68
+ const textElement = document.createElement("span");
69
+ textElement.classList.add("formula-text");
70
+ textElement.textContent = part;
71
+ formulaContainer.appendChild(textElement);
72
+ }
73
+ return;
74
+ }
75
+
76
+ // This is a variable placeholder
77
+ const variable = part.replace(/[{}]/g, "").trim();
78
+ const slotElement = createSlot(variable, (e) => {
79
+ e.preventDefault();
80
+ const enumValue = e.dataTransfer?.getData("band");
81
+ if (!enumValue) return;
82
+
83
+ const enumIndex = bands.indexOf(enumValue);
84
+ const title = bandTitles[enumIndex] || enumValue;
85
+
86
+ editor.variableValues[variable] = enumValue;
87
+
88
+ // Update ALL slots for this variable using unified system
89
+ updateAllSlotsForVariable(editor, variable, enumValue, title);
90
+
91
+ // final formula string as the value
92
+ //@ts-expect-error todo
93
+ editor.value = generateFormulaString(editor);
94
+ editor.onChange(true);
95
+ });
96
+ formulaContainer.appendChild(slotElement);
97
+
98
+ // Track all slots for this variable
99
+ if (!editor.variableSlots[variable]) {
100
+ editor.variableSlots[variable] = [];
101
+ }
102
+ editor.variableSlots[variable].push(slotElement);
103
+ });
104
+
105
+ editor.control?.appendChild(formulaContainer);
106
+
107
+ // Initialize slots with existing values after all slots are created
108
+ setTimeout(() => {
109
+ initializeSlots(editor);
110
+ });
111
+ }
112
+
113
+ /**
114
+ * Initialize all slots with existing values
115
+ * @param {import("./index.js").BandsEditor} editor - The editor instance
116
+ */
117
+ function initializeSlots(editor) {
118
+ if (editor.variableValues && editor.variableSlots) {
119
+ Object.keys(editor.variableValues).forEach((variable) => {
120
+ const enumValue = editor.variableValues[variable];
121
+ const bands = editor.bands || editor.schema.enum || [];
122
+ const bandTitles =
123
+ editor.bandTitles || editor.schema.options?.enum_titles || bands;
124
+ const enumIndex = bands.indexOf(enumValue);
125
+ const title = bandTitles[enumIndex] || enumValue;
126
+ updateAllSlotsForVariable(editor, variable, enumValue, title);
127
+ });
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Update all slots for a specific variable with band circle
133
+ * @param {import("./index.js").BandsEditor} editor - The editor instance
134
+ * @param {string} variable - Variable name
135
+ * @param {string} enumValue - Band value
136
+ * @param {string} title - Band title
137
+ */
138
+ function updateAllSlotsForVariable(editor, variable, enumValue, title) {
139
+ if (editor.variableSlots && editor.variableSlots[variable]) {
140
+ editor.variableSlots[variable].forEach((slot) => {
141
+ fillSlotWithBand(slot, enumValue, title);
142
+ });
143
+ }
144
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Generate band colors for the editor
3
+ * @param {Record<string,any>} schema - JSON schema
4
+ * @param {string} format - Format type ("bands" or "bands-arithmetic")
5
+ * @returns {string[]} Array of color strings
6
+ */
7
+ export function generateBandColors(schema, format) {
8
+ const bands = format === "bands" ? schema.items?.enum : schema.enum || [];
9
+ const colors =
10
+ format === "bands"
11
+ ? schema.items?.options?.colors
12
+ : schema.options?.colors || [];
13
+ if (colors && colors.length === bands.length) {
14
+ return colors;
15
+ }
16
+
17
+ return bands.map(
18
+ () =>
19
+ "#" +
20
+ Math.floor(Math.random() * 16777215)
21
+ .toString(16)
22
+ .padStart(6, "0"),
23
+ );
24
+ }
25
+
26
+ /**
27
+ * Get color for a specific band
28
+ * @param {string} band - Band identifier
29
+ * @param {string[]} bands - Array of band identifiers
30
+ * @param {string[]} colors - Array of color strings
31
+ * @returns {string} Color string
32
+ */
33
+ export function getBandColor(band, bands, colors) {
34
+ const index = bands.indexOf(band);
35
+ return index !== -1 ? colors[index] : "#000000";
36
+ }
@@ -0,0 +1,196 @@
1
+ import { getBandColor } from "./colors.js";
2
+
3
+ /**
4
+ * Create band styles
5
+ * @param {string[]} bands - Array of band identifiers
6
+ * @param {string[]} colors - Array of color strings
7
+ * @param {string} additionalStyles - Additional CSS styles
8
+ * @returns {HTMLStyleElement} Style element
9
+ */
10
+ export function createBandStyles(bands, colors, additionalStyles = "") {
11
+ const style = document.createElement("style");
12
+ style.innerHTML = `
13
+ [data-band], [data-slot] {
14
+ display: inline-flex;
15
+ border: 1px solid darkgrey;
16
+ border-radius: 50%;
17
+ height: 40px;
18
+ aspect-ratio: 1/1;
19
+ padding: 4px;
20
+ margin: 2px;
21
+ align-items: center;
22
+ justify-content: center;
23
+ cursor: move;
24
+ font-size: 10px;
25
+ }
26
+ ${bands
27
+ .map(
28
+ (band) =>
29
+ `[data-band="${band}"] { background: ${getBandColor(
30
+ band,
31
+ bands,
32
+ colors,
33
+ )}; color: black; }`,
34
+ )
35
+ .join("\n")}
36
+ ${additionalStyles}
37
+ `;
38
+ return style;
39
+ }
40
+
41
+ /**
42
+ * Create a draggable band element.
43
+ * @param {string} enumValue
44
+ * @param {string} title
45
+ */
46
+ export function createBandDiv(enumValue, title) {
47
+ const div = document.createElement("div");
48
+ div.dataset.band = enumValue;
49
+ div.textContent = title;
50
+ div.draggable = true;
51
+ div.ondragstart = (e) => {
52
+ e.dataTransfer?.setData("band", enumValue);
53
+ };
54
+ return div;
55
+ }
56
+
57
+ /**
58
+ * Add draggable band elements
59
+ * @param {import("./index.js").BandsEditor} editor - The editor instance
60
+ * @param {Array<string>} bands - Array of band identifiers
61
+ * @param {Array<string>} bandTitles - Array of band titles
62
+ */
63
+ export function addDraggableBands(editor, bands, bandTitles) {
64
+ bands.forEach((band, index) => {
65
+ const title = bandTitles[index];
66
+ const bandDiv = createBandDiv(band, title);
67
+
68
+ // createBandDiv already sets up drag functionality
69
+ editor.control?.appendChild(bandDiv);
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Create unified styles for all slot types
75
+ * @param {string[]} bands - Array of band identifiers
76
+ * @param {string[]} colors - Array of color strings
77
+ * @returns {HTMLStyleElement} Style element with unified slot styles
78
+ */
79
+ export function createSlotStyles(bands, colors) {
80
+ const style = document.createElement("style");
81
+ style.innerHTML = `
82
+ /* Base styles for all band elements */
83
+ [data-band] {
84
+ display: inline-flex;
85
+ border: 1px solid darkgrey;
86
+ border-radius: 50%;
87
+ height: 40px;
88
+ aspect-ratio: 1/1;
89
+ padding: 4px;
90
+ margin: 2px;
91
+ align-items: center;
92
+ justify-content: center;
93
+ cursor: move;
94
+ font-size: 10px;
95
+ }
96
+
97
+ /* Band color styles */
98
+ ${bands
99
+ .map(
100
+ (band) =>
101
+ `[data-band="${band}"] { background: ${getBandColor(
102
+ band,
103
+ bands,
104
+ colors,
105
+ )}; color: black; }`,
106
+ )
107
+ .join("\n")}
108
+
109
+ /* RGB slot styles */
110
+ [data-slot] {
111
+ display: inline-flex;
112
+ width: 50px;
113
+ height: 50px;
114
+ aspect-ratio: 1/1;
115
+ padding: 1px;
116
+ border: 2px solid #666;
117
+ background: #f0f0f0;
118
+ border-radius: 50%;
119
+ align-items: center;
120
+ justify-content: center;
121
+ cursor: pointer;
122
+ margin: 2px;
123
+ position: relative;
124
+ box-sizing: border-box;
125
+ }
126
+ [data-slot]:hover {
127
+ border-color: #333;
128
+ background: #f9f9f9;
129
+ }
130
+ [data-slot]::before {
131
+ content: attr(data-slot);
132
+ position: absolute;
133
+ font-size: 12px;
134
+ font-weight: bold;
135
+ color: #666;
136
+ z-index: 0;
137
+ }
138
+
139
+ /* container */
140
+ .slots-container {
141
+ font-family: monospace;
142
+ font-size: 18px;
143
+ padding: 16px;
144
+ background: #f0f0f0;
145
+ border: 1px solid #ccc;
146
+ border-radius: 4px;
147
+ margin: 8px 0;
148
+ display: flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ flex-wrap: wrap;
152
+ gap: 4px;
153
+ }
154
+
155
+ .formula-text {
156
+ font-size: 18px;
157
+ margin: 0 2px;
158
+ }
159
+ `;
160
+ return style;
161
+ }
162
+
163
+ /**
164
+ * Create a unified slot element for RGB or arithmetic use
165
+ * @param {string} identifier - RGB letter ("R", "G", "B") or variable name for arithmetic
166
+ * @param {(e: DragEvent) => void} onDrop - Drop handler function
167
+ * @returns {HTMLDivElement} Slot element
168
+ */
169
+ export function createSlot(identifier, onDrop) {
170
+ const slotDiv = document.createElement("div");
171
+ // Use data-slot
172
+ slotDiv.dataset.slot = identifier;
173
+
174
+ // Add drag & drop functionality
175
+ slotDiv.ondrop = onDrop;
176
+ slotDiv.ondragover = (e) => e.preventDefault();
177
+
178
+ return slotDiv;
179
+ }
180
+
181
+ /**
182
+ * Fill a slot with a band
183
+ * @param {HTMLElement} slot - Slot element
184
+ * @param {string} enumValue - Band value
185
+ * @param {string} title - Band title
186
+ */
187
+ export function fillSlotWithBand(slot, enumValue, title) {
188
+ // clear existing band and add new one
189
+ const existingBand = slot.querySelector("[data-band]");
190
+ if (existingBand) {
191
+ existingBand.remove();
192
+ }
193
+
194
+ const bandDiv = createBandDiv(enumValue, title);
195
+ slot.appendChild(bandDiv);
196
+ }