@open-file-viewer/core 0.1.0 → 0.1.2

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/dist/index.d.cts CHANGED
@@ -4,6 +4,8 @@ type PreviewSource = File | Blob | string | ArrayBuffer;
4
4
  type PreviewFit = "contain" | "cover" | "width" | "height" | "actual" | "scale-down";
5
5
  type PreviewFallback = "inline" | "download" | "custom";
6
6
  type PreviewTheme = "light" | "dark" | "auto";
7
+ type PreviewToolbarBuiltInAction = "previous" | "next" | "queue" | "zoom-out" | "zoom-in" | "zoom-reset" | "rotate-right" | "download" | "fullscreen" | "print" | "search";
8
+ type PreviewToolbarActionId = PreviewToolbarBuiltInAction | (string & {});
7
9
  interface PreviewFile {
8
10
  source: PreviewSource;
9
11
  name: string;
@@ -29,6 +31,40 @@ interface PreviewToolbarOptions {
29
31
  fullscreen?: boolean;
30
32
  print?: boolean;
31
33
  search?: boolean;
34
+ order?: PreviewToolbarActionId[];
35
+ labels?: Partial<Record<PreviewToolbarBuiltInAction, string>>;
36
+ titles?: Partial<Record<PreviewToolbarBuiltInAction, string>>;
37
+ icons?: Partial<Record<PreviewToolbarBuiltInAction, string | HTMLElement | SVGElement>>;
38
+ actions?: PreviewToolbarCustomAction[];
39
+ render?: (ctx: PreviewToolbarRenderContext) => HTMLElement | void;
40
+ }
41
+ interface PreviewToolbarCustomAction {
42
+ id: string;
43
+ label: string;
44
+ title?: string;
45
+ icon?: string | HTMLElement | SVGElement;
46
+ order?: number;
47
+ disabled?: boolean | ((ctx: PreviewToolbarRenderContext) => boolean);
48
+ hidden?: boolean | ((ctx: PreviewToolbarRenderContext) => boolean);
49
+ className?: string;
50
+ onClick: (ctx: PreviewToolbarRenderContext) => void | Promise<void>;
51
+ }
52
+ interface PreviewToolbarRenderContext {
53
+ file?: PreviewFile;
54
+ index: number;
55
+ length: number;
56
+ viewport: HTMLElement;
57
+ canPrevious: boolean;
58
+ canNext: boolean;
59
+ previous: () => Promise<void>;
60
+ next: () => Promise<void>;
61
+ command: (command: PreviewCommand) => void | boolean | undefined;
62
+ canCommand: (command: PreviewCommand) => boolean;
63
+ download: () => void;
64
+ fullscreen: () => void;
65
+ print: () => void;
66
+ search: (query: string) => number;
67
+ clearSearch: () => void;
32
68
  }
33
69
  interface PreviewOptions {
34
70
  container: HTMLElement | string;
@@ -56,6 +92,7 @@ interface PreviewContext {
56
92
  file: PreviewFile;
57
93
  size: PreviewSize;
58
94
  options: Required<Pick<PreviewOptions, "fit" | "fallback">> & PreviewOptions;
95
+ toolbar?: PreviewToolbarRenderContext;
59
96
  setLoading: (loading: boolean) => void;
60
97
  setError: (error: Error | string) => void;
61
98
  }
@@ -122,4 +159,4 @@ declare function assetPlugin(): PreviewPlugin;
122
159
 
123
160
  declare function fallbackPlugin(): PreviewPlugin;
124
161
 
125
- export { type FileViewer, type PreviewCommand, type PreviewContext, type PreviewFallback, type PreviewFile, type PreviewFit, type PreviewInstance, type PreviewItem, type PreviewOptions, type PreviewPlugin, type PreviewSize, type PreviewSource, type PreviewTheme, type PreviewToolbarOptions, archivePlugin, assetPlugin, audioPlugin, cadPlugin, createViewer, drawingPlugin, emailPlugin, epubPlugin, fallbackPlugin, gisPlugin, imagePlugin, model3dPlugin, ofdPlugin, officePlugin, pdfPlugin, textPlugin, videoPlugin, xpsPlugin };
162
+ export { type FileViewer, type PreviewCommand, type PreviewContext, type PreviewFallback, type PreviewFile, type PreviewFit, type PreviewInstance, type PreviewItem, type PreviewOptions, type PreviewPlugin, type PreviewSize, type PreviewSource, type PreviewTheme, type PreviewToolbarActionId, type PreviewToolbarBuiltInAction, type PreviewToolbarCustomAction, type PreviewToolbarOptions, type PreviewToolbarRenderContext, archivePlugin, assetPlugin, audioPlugin, cadPlugin, createViewer, drawingPlugin, emailPlugin, epubPlugin, fallbackPlugin, gisPlugin, imagePlugin, model3dPlugin, ofdPlugin, officePlugin, pdfPlugin, textPlugin, videoPlugin, xpsPlugin };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ type PreviewSource = File | Blob | string | ArrayBuffer;
4
4
  type PreviewFit = "contain" | "cover" | "width" | "height" | "actual" | "scale-down";
5
5
  type PreviewFallback = "inline" | "download" | "custom";
6
6
  type PreviewTheme = "light" | "dark" | "auto";
7
+ type PreviewToolbarBuiltInAction = "previous" | "next" | "queue" | "zoom-out" | "zoom-in" | "zoom-reset" | "rotate-right" | "download" | "fullscreen" | "print" | "search";
8
+ type PreviewToolbarActionId = PreviewToolbarBuiltInAction | (string & {});
7
9
  interface PreviewFile {
8
10
  source: PreviewSource;
9
11
  name: string;
@@ -29,6 +31,40 @@ interface PreviewToolbarOptions {
29
31
  fullscreen?: boolean;
30
32
  print?: boolean;
31
33
  search?: boolean;
34
+ order?: PreviewToolbarActionId[];
35
+ labels?: Partial<Record<PreviewToolbarBuiltInAction, string>>;
36
+ titles?: Partial<Record<PreviewToolbarBuiltInAction, string>>;
37
+ icons?: Partial<Record<PreviewToolbarBuiltInAction, string | HTMLElement | SVGElement>>;
38
+ actions?: PreviewToolbarCustomAction[];
39
+ render?: (ctx: PreviewToolbarRenderContext) => HTMLElement | void;
40
+ }
41
+ interface PreviewToolbarCustomAction {
42
+ id: string;
43
+ label: string;
44
+ title?: string;
45
+ icon?: string | HTMLElement | SVGElement;
46
+ order?: number;
47
+ disabled?: boolean | ((ctx: PreviewToolbarRenderContext) => boolean);
48
+ hidden?: boolean | ((ctx: PreviewToolbarRenderContext) => boolean);
49
+ className?: string;
50
+ onClick: (ctx: PreviewToolbarRenderContext) => void | Promise<void>;
51
+ }
52
+ interface PreviewToolbarRenderContext {
53
+ file?: PreviewFile;
54
+ index: number;
55
+ length: number;
56
+ viewport: HTMLElement;
57
+ canPrevious: boolean;
58
+ canNext: boolean;
59
+ previous: () => Promise<void>;
60
+ next: () => Promise<void>;
61
+ command: (command: PreviewCommand) => void | boolean | undefined;
62
+ canCommand: (command: PreviewCommand) => boolean;
63
+ download: () => void;
64
+ fullscreen: () => void;
65
+ print: () => void;
66
+ search: (query: string) => number;
67
+ clearSearch: () => void;
32
68
  }
33
69
  interface PreviewOptions {
34
70
  container: HTMLElement | string;
@@ -56,6 +92,7 @@ interface PreviewContext {
56
92
  file: PreviewFile;
57
93
  size: PreviewSize;
58
94
  options: Required<Pick<PreviewOptions, "fit" | "fallback">> & PreviewOptions;
95
+ toolbar?: PreviewToolbarRenderContext;
59
96
  setLoading: (loading: boolean) => void;
60
97
  setError: (error: Error | string) => void;
61
98
  }
@@ -122,4 +159,4 @@ declare function assetPlugin(): PreviewPlugin;
122
159
 
123
160
  declare function fallbackPlugin(): PreviewPlugin;
124
161
 
125
- export { type FileViewer, type PreviewCommand, type PreviewContext, type PreviewFallback, type PreviewFile, type PreviewFit, type PreviewInstance, type PreviewItem, type PreviewOptions, type PreviewPlugin, type PreviewSize, type PreviewSource, type PreviewTheme, type PreviewToolbarOptions, archivePlugin, assetPlugin, audioPlugin, cadPlugin, createViewer, drawingPlugin, emailPlugin, epubPlugin, fallbackPlugin, gisPlugin, imagePlugin, model3dPlugin, ofdPlugin, officePlugin, pdfPlugin, textPlugin, videoPlugin, xpsPlugin };
162
+ export { type FileViewer, type PreviewCommand, type PreviewContext, type PreviewFallback, type PreviewFile, type PreviewFit, type PreviewInstance, type PreviewItem, type PreviewOptions, type PreviewPlugin, type PreviewSize, type PreviewSource, type PreviewTheme, type PreviewToolbarActionId, type PreviewToolbarBuiltInAction, type PreviewToolbarCustomAction, type PreviewToolbarOptions, type PreviewToolbarRenderContext, archivePlugin, assetPlugin, audioPlugin, cadPlugin, createViewer, drawingPlugin, emailPlugin, epubPlugin, fallbackPlugin, gisPlugin, imagePlugin, model3dPlugin, ofdPlugin, officePlugin, pdfPlugin, textPlugin, videoPlugin, xpsPlugin };
package/dist/index.js CHANGED
@@ -682,6 +682,7 @@ function createViewer(options) {
682
682
  file,
683
683
  size: getElementSize(viewport),
684
684
  options: normalizedOptions,
685
+ toolbar: toolbar?.getContext(),
685
686
  setLoading,
686
687
  setError
687
688
  });
@@ -855,18 +856,32 @@ function createToolbar(toolbar, viewport, queue) {
855
856
  element.setAttribute("role", "toolbar");
856
857
  element.setAttribute("aria-label", "File preview toolbar");
857
858
  let file;
859
+ let currentIndex = 0;
860
+ let currentLength = queue.getLength();
858
861
  let queueLabel;
859
862
  let previousButton;
860
863
  let nextButton;
861
864
  const commandButtons = [];
865
+ const customButtons = [];
862
866
  const disposers = [];
863
867
  const search = createSearchController(viewport);
864
868
  let searchInput;
865
869
  let searchCount;
866
- const addButton = (label, title, action, className) => {
870
+ let canRunCommand = (_command) => false;
871
+ const getContext = () => createToolbarContext({
872
+ file,
873
+ index: currentIndex,
874
+ length: currentLength,
875
+ viewport,
876
+ queue,
877
+ element,
878
+ search,
879
+ canCommand: canRunCommand
880
+ });
881
+ const addButton = (label, title, action, className, icon) => {
867
882
  const button = document.createElement("button");
868
883
  button.type = "button";
869
- button.textContent = label;
884
+ setToolbarButtonContent(button, label, icon);
870
885
  button.title = title;
871
886
  button.setAttribute("aria-label", title);
872
887
  if (className) {
@@ -877,68 +892,117 @@ function createToolbar(toolbar, viewport, queue) {
877
892
  disposers.push(() => button.removeEventListener("click", action));
878
893
  return button;
879
894
  };
880
- const addQueueButton = (label, title, action) => {
881
- const button = document.createElement("button");
882
- button.type = "button";
883
- button.textContent = label;
884
- button.title = title;
885
- button.setAttribute("aria-label", title);
886
- const listener = () => {
887
- void action();
888
- };
889
- button.addEventListener("click", listener);
890
- element.append(button);
891
- disposers.push(() => button.removeEventListener("click", listener));
892
- return button;
893
- };
894
- if (queue.getLength() > 1) {
895
- previousButton = addQueueButton("Prev", "Previous file", queue.previous);
896
- nextButton = addQueueButton("Next", "Next file", queue.next);
897
- queueLabel = document.createElement("span");
898
- queueLabel.className = "ofv-toolbar-queue";
899
- element.append(queueLabel);
900
- }
901
- const addCommandButton = (label, title, command) => {
895
+ const addCommandButton = (id, label, title, command) => {
902
896
  const button = addButton(label, title, () => {
903
897
  queue.command(command);
904
- });
898
+ }, void 0, options.icons?.[id]);
905
899
  button.disabled = true;
906
900
  commandButtons.push({ button, command });
907
901
  };
908
- if (options.zoom) {
909
- addCommandButton("-", "Zoom out", "zoom-out");
910
- addCommandButton("+", "Zoom in", "zoom-in");
911
- addCommandButton("100%", "Reset zoom", "zoom-reset");
912
- }
913
- if (options.rotate) {
914
- addCommandButton("Rotate", "Rotate right", "rotate-right");
915
- }
916
- if (options.download !== false) {
917
- addButton("Download", "Download file", () => {
918
- if (file) {
919
- downloadFile(file);
902
+ const renderDefaultAction = (id) => {
903
+ if (!isBuiltInToolbarAction(id)) {
904
+ const customAction = options.actions?.find((action) => action.id === id);
905
+ if (customAction) {
906
+ renderCustomAction(customAction);
920
907
  }
921
- });
922
- }
923
- if (options.fullscreen !== false) {
924
- addButton("Fullscreen", "Open preview fullscreen", () => {
925
- const target = element.parentElement;
926
- void target?.requestFullscreen?.();
927
- });
928
- }
929
- if (options.print) {
930
- addButton("Print", "Print preview", () => {
931
- printPreview(viewport);
932
- });
933
- }
934
- if (options.search !== false) {
908
+ return;
909
+ }
910
+ if (id === "previous" && queue.getLength() > 1) {
911
+ previousButton = addButton(
912
+ getToolbarLabel(options, "previous"),
913
+ getToolbarTitle(options, "previous"),
914
+ () => void queue.previous(),
915
+ void 0,
916
+ options.icons?.previous
917
+ );
918
+ return;
919
+ }
920
+ if (id === "next" && queue.getLength() > 1) {
921
+ nextButton = addButton(
922
+ getToolbarLabel(options, "next"),
923
+ getToolbarTitle(options, "next"),
924
+ () => void queue.next(),
925
+ void 0,
926
+ options.icons?.next
927
+ );
928
+ return;
929
+ }
930
+ if (id === "queue" && queue.getLength() > 1) {
931
+ queueLabel = document.createElement("span");
932
+ queueLabel.className = "ofv-toolbar-queue";
933
+ element.append(queueLabel);
934
+ return;
935
+ }
936
+ if (id === "zoom-out" && options.zoom) {
937
+ addCommandButton(id, getToolbarLabel(options, id), getToolbarTitle(options, id), "zoom-out");
938
+ return;
939
+ }
940
+ if (id === "zoom-in" && options.zoom) {
941
+ addCommandButton(id, getToolbarLabel(options, id), getToolbarTitle(options, id), "zoom-in");
942
+ return;
943
+ }
944
+ if (id === "zoom-reset" && options.zoom) {
945
+ addCommandButton(id, getToolbarLabel(options, id), getToolbarTitle(options, id), "zoom-reset");
946
+ return;
947
+ }
948
+ if (id === "rotate-right" && options.rotate) {
949
+ addCommandButton(id, getToolbarLabel(options, id), getToolbarTitle(options, id), "rotate-right");
950
+ return;
951
+ }
952
+ if (id === "download" && options.download !== false) {
953
+ addButton(
954
+ getToolbarLabel(options, id),
955
+ getToolbarTitle(options, id),
956
+ () => getContext().download(),
957
+ void 0,
958
+ options.icons?.download
959
+ );
960
+ return;
961
+ }
962
+ if (id === "fullscreen" && options.fullscreen !== false) {
963
+ addButton(
964
+ getToolbarLabel(options, id),
965
+ getToolbarTitle(options, id),
966
+ () => getContext().fullscreen(),
967
+ void 0,
968
+ options.icons?.fullscreen
969
+ );
970
+ return;
971
+ }
972
+ if (id === "print" && options.print) {
973
+ addButton(
974
+ getToolbarLabel(options, id),
975
+ getToolbarTitle(options, id),
976
+ () => getContext().print(),
977
+ void 0,
978
+ options.icons?.print
979
+ );
980
+ return;
981
+ }
982
+ if (id === "search" && options.search !== false) {
983
+ renderSearchControl();
984
+ return;
985
+ }
986
+ };
987
+ const renderCustomAction = (action) => {
988
+ const button = addButton(
989
+ action.label,
990
+ action.title || action.label,
991
+ () => void action.onClick(getContext()),
992
+ action.className,
993
+ action.icon
994
+ );
995
+ button.dataset.ofvToolbarAction = action.id;
996
+ customButtons.push({ button, action });
997
+ };
998
+ const renderSearchControl = () => {
935
999
  const searchGroup = document.createElement("div");
936
1000
  searchGroup.className = "ofv-toolbar-search";
937
- searchGroup.title = "Search preview text";
1001
+ searchGroup.title = getToolbarTitle(options, "search");
938
1002
  const nextSearchInput = document.createElement("input");
939
1003
  nextSearchInput.type = "search";
940
- nextSearchInput.placeholder = "Search";
941
- nextSearchInput.setAttribute("aria-label", "Search preview text");
1004
+ nextSearchInput.placeholder = getToolbarLabel(options, "search");
1005
+ nextSearchInput.setAttribute("aria-label", getToolbarTitle(options, "search"));
942
1006
  const nextSearchCount = document.createElement("span");
943
1007
  nextSearchCount.className = "ofv-toolbar-search-count";
944
1008
  searchInput = nextSearchInput;
@@ -951,18 +1015,53 @@ function createToolbar(toolbar, viewport, queue) {
951
1015
  searchGroup.append(nextSearchInput, nextSearchCount);
952
1016
  element.append(searchGroup);
953
1017
  disposers.push(() => nextSearchInput.removeEventListener("input", runSearch));
954
- }
1018
+ };
1019
+ const renderToolbar = () => {
1020
+ if (options.render) {
1021
+ element.replaceChildren();
1022
+ const customElement = options.render(getContext());
1023
+ if (customElement) {
1024
+ element.append(customElement);
1025
+ }
1026
+ return;
1027
+ }
1028
+ getToolbarOrder(options, queue.getLength()).forEach(renderDefaultAction);
1029
+ getImplicitCustomActions(options).forEach(renderCustomAction);
1030
+ };
1031
+ renderToolbar();
1032
+ const updateCustomButtons = () => {
1033
+ const context = getContext();
1034
+ for (const { button, action } of customButtons) {
1035
+ button.disabled = evaluateToolbarFlag(action.disabled, context);
1036
+ button.hidden = evaluateToolbarFlag(action.hidden, context);
1037
+ }
1038
+ };
1039
+ const resetSearch = () => {
1040
+ search.clear();
1041
+ if (searchInput) {
1042
+ searchInput.value = "";
1043
+ }
1044
+ if (searchCount) {
1045
+ searchCount.textContent = "";
1046
+ }
1047
+ };
1048
+ const refreshCustomRender = () => {
1049
+ if (!options.render) {
1050
+ return;
1051
+ }
1052
+ element.replaceChildren();
1053
+ const customElement = options.render(getContext());
1054
+ if (customElement) {
1055
+ element.append(customElement);
1056
+ }
1057
+ };
955
1058
  return {
956
1059
  element,
957
1060
  update(nextFile, index, length) {
958
1061
  file = nextFile;
959
- search.clear();
960
- if (searchInput) {
961
- searchInput.value = "";
962
- }
963
- if (searchCount) {
964
- searchCount.textContent = "";
965
- }
1062
+ currentIndex = index;
1063
+ currentLength = length;
1064
+ resetSearch();
966
1065
  commandButtons.forEach(({ button }) => {
967
1066
  button.disabled = true;
968
1067
  });
@@ -975,20 +1074,158 @@ function createToolbar(toolbar, viewport, queue) {
975
1074
  if (nextButton) {
976
1075
  nextButton.disabled = index >= length - 1;
977
1076
  }
1077
+ updateCustomButtons();
1078
+ refreshCustomRender();
978
1079
  },
979
1080
  setCommandSupport(isSupported) {
1081
+ canRunCommand = isSupported;
980
1082
  commandButtons.forEach(({ button, command }) => {
981
1083
  button.disabled = !isSupported(command);
982
1084
  });
1085
+ updateCustomButtons();
1086
+ refreshCustomRender();
983
1087
  },
1088
+ getContext,
984
1089
  destroy() {
985
1090
  search.clear();
986
1091
  for (const dispose of disposers) {
987
1092
  dispose();
988
1093
  }
1094
+ element.replaceChildren();
989
1095
  }
990
1096
  };
991
1097
  }
1098
+ function createToolbarContext({
1099
+ file,
1100
+ index,
1101
+ length,
1102
+ viewport,
1103
+ queue,
1104
+ element,
1105
+ search,
1106
+ canCommand
1107
+ }) {
1108
+ return {
1109
+ file,
1110
+ index,
1111
+ length,
1112
+ viewport,
1113
+ canPrevious: index > 0,
1114
+ canNext: index < length - 1,
1115
+ async previous() {
1116
+ await queue.previous();
1117
+ },
1118
+ async next() {
1119
+ await queue.next();
1120
+ },
1121
+ command: queue.command,
1122
+ canCommand,
1123
+ download() {
1124
+ if (file) {
1125
+ downloadFile(file);
1126
+ }
1127
+ },
1128
+ fullscreen() {
1129
+ void element.parentElement?.requestFullscreen?.();
1130
+ },
1131
+ print() {
1132
+ printPreview(viewport);
1133
+ },
1134
+ search: search.search,
1135
+ clearSearch: search.clear
1136
+ };
1137
+ }
1138
+ var defaultToolbarLabels = {
1139
+ previous: "Prev",
1140
+ next: "Next",
1141
+ queue: "",
1142
+ "zoom-out": "-",
1143
+ "zoom-in": "+",
1144
+ "zoom-reset": "100%",
1145
+ "rotate-right": "Rotate",
1146
+ download: "Download",
1147
+ fullscreen: "Fullscreen",
1148
+ print: "Print",
1149
+ search: "Search"
1150
+ };
1151
+ var defaultToolbarTitles = {
1152
+ previous: "Previous file",
1153
+ next: "Next file",
1154
+ queue: "Current file position",
1155
+ "zoom-out": "Zoom out",
1156
+ "zoom-in": "Zoom in",
1157
+ "zoom-reset": "Reset zoom",
1158
+ "rotate-right": "Rotate right",
1159
+ download: "Download file",
1160
+ fullscreen: "Open preview fullscreen",
1161
+ print: "Print preview",
1162
+ search: "Search preview text"
1163
+ };
1164
+ function getToolbarLabel(options, id) {
1165
+ return options.labels?.[id] ?? defaultToolbarLabels[id];
1166
+ }
1167
+ function getToolbarTitle(options, id) {
1168
+ return options.titles?.[id] ?? options.labels?.[id] ?? defaultToolbarTitles[id];
1169
+ }
1170
+ function getToolbarOrder(options, queueLength) {
1171
+ if (options.order) {
1172
+ return options.order;
1173
+ }
1174
+ const actions = [];
1175
+ if (queueLength > 1) {
1176
+ actions.push("previous", "next", "queue");
1177
+ }
1178
+ if (options.zoom) {
1179
+ actions.push("zoom-out", "zoom-in", "zoom-reset");
1180
+ }
1181
+ if (options.rotate) {
1182
+ actions.push("rotate-right");
1183
+ }
1184
+ if (options.download !== false) {
1185
+ actions.push("download");
1186
+ }
1187
+ if (options.fullscreen !== false) {
1188
+ actions.push("fullscreen");
1189
+ }
1190
+ if (options.print) {
1191
+ actions.push("print");
1192
+ }
1193
+ if (options.search !== false) {
1194
+ actions.push("search");
1195
+ }
1196
+ return actions;
1197
+ }
1198
+ function getImplicitCustomActions(options) {
1199
+ if (options.order || !options.actions) {
1200
+ return [];
1201
+ }
1202
+ return [...options.actions].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
1203
+ }
1204
+ function evaluateToolbarFlag(value, context) {
1205
+ return typeof value === "function" ? value(context) : Boolean(value);
1206
+ }
1207
+ function setToolbarButtonContent(button, label, icon) {
1208
+ button.replaceChildren();
1209
+ if (!icon) {
1210
+ button.textContent = label;
1211
+ return;
1212
+ }
1213
+ const iconElement = document.createElement("span");
1214
+ iconElement.className = "ofv-toolbar-icon";
1215
+ iconElement.setAttribute("aria-hidden", "true");
1216
+ if (typeof icon === "string") {
1217
+ iconElement.innerHTML = icon;
1218
+ } else {
1219
+ iconElement.append(icon.cloneNode(true));
1220
+ }
1221
+ const labelElement = document.createElement("span");
1222
+ labelElement.className = "ofv-toolbar-label";
1223
+ labelElement.textContent = label;
1224
+ button.append(iconElement, labelElement);
1225
+ }
1226
+ function isBuiltInToolbarAction(id) {
1227
+ return id in defaultToolbarLabels;
1228
+ }
992
1229
  function createSearchController(root) {
993
1230
  const markerClass = "ofv-search-match";
994
1231
  const clear = () => {