@blinkk/root-cms 1.0.0-beta.57 → 1.0.0-beta.58

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/ui/ui.css CHANGED
@@ -834,6 +834,13 @@
834
834
  flex-direction: column;
835
835
  gap: 24px;
836
836
  }
837
+ .DocEditor__ImageField {
838
+ padding: 10px;
839
+ margin: -10px;
840
+ }
841
+ .DocEditor__ImageField.dragging {
842
+ outline: 2px dashed black;
843
+ }
837
844
  .DocEditor__ImageField__imagePreview {
838
845
  margin-bottom: 10px;
839
846
  }
@@ -873,7 +880,7 @@
873
880
  }
874
881
  .DocEditor__ImageField__noImage {
875
882
  font-family: var(--font-family-mono);
876
- margin: 10px 0;
883
+ margin-bottom: 10px;
877
884
  }
878
885
  .DocEditor__ImageField__uploadButton {
879
886
  display: inline-flex;
@@ -896,12 +903,24 @@
896
903
  .DocEditor__ImageField__uploadButton__icon {
897
904
  margin-right: 10px;
898
905
  }
906
+ .DocEditor__FileField {
907
+ padding: 10px;
908
+ margin: -10px;
909
+ }
910
+ .DocEditor__FileField.dragging {
911
+ outline: 2px dashed black;
912
+ }
913
+ .DocEditor__FileField__controls {
914
+ display: flex;
915
+ justify-content: flex-end;
916
+ gap: 4px;
917
+ }
899
918
  .DocEditor__FileField__file {
900
919
  margin-bottom: 10px;
901
920
  }
902
921
  .DocEditor__FileField__noFile {
903
922
  font-family: var(--font-family-mono);
904
- margin: 10px 0;
923
+ margin-bottom: 10px;
905
924
  }
906
925
  .DocEditor__FileField__uploadButton {
907
926
  display: inline-flex;
package/dist/ui/ui.js CHANGED
@@ -39859,7 +39859,7 @@ ${this.customData.serverResponse}`;
39859
39859
  // package.json
39860
39860
  var package_default = {
39861
39861
  name: "@blinkk/root-cms",
39862
- version: "1.0.0-beta.57",
39862
+ version: "1.0.0-beta.58",
39863
39863
  author: "s@blinkk.com",
39864
39864
  license: "MIT",
39865
39865
  engines: {
@@ -39962,7 +39962,7 @@ ${this.customData.serverResponse}`;
39962
39962
  vitest: "^0.18.1"
39963
39963
  },
39964
39964
  peerDependencies: {
39965
- "@blinkk/root": "1.0.0-beta.57",
39965
+ "@blinkk/root": "1.0.0-beta.58",
39966
39966
  "firebase-admin": ">=11",
39967
39967
  "firebase-functions": ">=4",
39968
39968
  preact: "10.x",
@@ -43859,28 +43859,24 @@ ${content}</tr>
43859
43859
  reader.readAsDataURL(file);
43860
43860
  });
43861
43861
  }
43862
+ var IMAGE_MIMETYPES = [
43863
+ "image/png",
43864
+ "image/jpeg",
43865
+ "image/svg+xml",
43866
+ "image/webp"
43867
+ ];
43862
43868
  DocEditor.ImageField = (props) => {
43863
43869
  const field = props.field;
43864
43870
  const [img, setImg] = y2({});
43865
- const inputRef = A2(null);
43866
43871
  const [loading, setLoading] = y2(false);
43867
- s2(() => {
43868
- const unsubscribe = props.draft.subscribe(
43869
- props.deepKey,
43870
- (newValue) => {
43871
- setImg(newValue);
43872
- }
43873
- );
43874
- return unsubscribe;
43875
- }, []);
43876
- const exts = field.exts ?? [".png", ".jpg", ".jpeg", ".svg"];
43872
+ const [isDragging, setIsDragging] = y2(false);
43873
+ const inputRef = A2(null);
43874
+ const ref2 = A2(null);
43875
+ const exts = field.exts ?? IMAGE_MIMETYPES;
43877
43876
  const accept = exts.join(", ");
43878
- async function onFileChange(e3) {
43877
+ async function uploadFile(file) {
43878
+ setLoading(true);
43879
43879
  try {
43880
- setLoading(true);
43881
- const inputEl = e3.target;
43882
- const files = inputEl.files || [];
43883
- const file = files[0];
43884
43880
  const uploadedImage = await uploadFileToGCS(file);
43885
43881
  setImg((currentImg) => {
43886
43882
  const newImage = Object.assign({}, uploadedImage, {
@@ -43906,6 +43902,14 @@ ${content}</tr>
43906
43902
  inputEl.value = "";
43907
43903
  }
43908
43904
  }
43905
+ function onFileChange(e3) {
43906
+ const inputEl = e3.target;
43907
+ const files = inputEl.files || [];
43908
+ const file = files[0];
43909
+ if (file) {
43910
+ uploadFile(file);
43911
+ }
43912
+ }
43909
43913
  async function setAltText(newValue) {
43910
43914
  setImg((currentImg) => {
43911
43915
  return Object.assign({}, currentImg, { alt: newValue });
@@ -43916,97 +43920,149 @@ ${content}</tr>
43916
43920
  setImg({});
43917
43921
  props.draft.removeKey(props.deepKey);
43918
43922
  }
43919
- return /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField", children: [
43920
- img && img.src ? /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__imagePreview", children: [
43921
- /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__imagePreview__controls", children: /* @__PURE__ */ o4(Tooltip, { label: "Remove image", children: /* @__PURE__ */ o4(
43922
- ActionIcon,
43923
- {
43924
- className: "DocEditor__ImageField__imagePreview__controls__trash",
43925
- onClick: () => removeImage(),
43926
- children: /* @__PURE__ */ o4(IconTrash, { size: 16 })
43927
- }
43928
- ) }) }),
43929
- /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__imagePreview__image", children: [
43930
- /* @__PURE__ */ o4(
43931
- "img",
43932
- {
43933
- src: img.gciUrl || img.src,
43934
- width: img.width,
43935
- height: img.height
43936
- }
43937
- ),
43938
- /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__imagePreview__dimens", children: `${img.width}x${img.height}` })
43939
- ] }),
43940
- /* @__PURE__ */ o4(
43941
- TextInput,
43942
- {
43943
- className: "DocEditor__ImageField__imagePreview__image__url",
43944
- size: "xs",
43945
- radius: 0,
43946
- value: img.gciUrl || img.src,
43947
- disabled: true
43948
- }
43923
+ const handleDragEnter = (e3) => {
43924
+ e3.preventDefault();
43925
+ setIsDragging(true);
43926
+ };
43927
+ const handleDragLeave = (e3) => {
43928
+ e3.preventDefault();
43929
+ setIsDragging(false);
43930
+ };
43931
+ const handleDrop = (e3) => {
43932
+ e3.preventDefault();
43933
+ setIsDragging(false);
43934
+ const files = e3.dataTransfer?.files || [];
43935
+ const file = files[0];
43936
+ if (file) {
43937
+ console.log(`file dropped ("${props.deepKey}"):`, file);
43938
+ uploadFile(file);
43939
+ }
43940
+ };
43941
+ s2(() => {
43942
+ const unsubscribe = props.draft.subscribe(
43943
+ props.deepKey,
43944
+ (newValue) => {
43945
+ setImg(newValue);
43946
+ }
43947
+ );
43948
+ const dropzone = ref2.current;
43949
+ document.addEventListener("dragenter", handleDragEnter);
43950
+ document.addEventListener("dragover", handleDragEnter);
43951
+ document.addEventListener("dragleave", handleDragLeave);
43952
+ document.addEventListener("drop", handleDragLeave);
43953
+ if (dropzone) {
43954
+ dropzone.addEventListener("drop", handleDrop);
43955
+ }
43956
+ return () => {
43957
+ unsubscribe();
43958
+ document.removeEventListener("dragenter", handleDragEnter);
43959
+ document.removeEventListener("dragover", handleDragEnter);
43960
+ document.removeEventListener("dragleave", handleDragLeave);
43961
+ document.removeEventListener("drop", handleDragLeave);
43962
+ if (dropzone) {
43963
+ dropzone.removeEventListener("drop", handleDrop);
43964
+ }
43965
+ };
43966
+ }, []);
43967
+ return /* @__PURE__ */ o4(
43968
+ "div",
43969
+ {
43970
+ className: joinClassNames(
43971
+ "DocEditor__ImageField",
43972
+ isDragging && "dragging"
43949
43973
  ),
43950
- /* @__PURE__ */ o4(
43951
- TextInput,
43952
- {
43953
- className: "DocEditor__ImageField__imagePreview__image__alt",
43954
- size: "xs",
43955
- radius: 0,
43956
- value: img.alt,
43957
- label: "Alt text",
43958
- onChange: (e3) => {
43959
- setAltText(e3.currentTarget.value);
43960
- }
43961
- }
43962
- )
43963
- ] }) : /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__noImage", children: "No image" }),
43964
- /* @__PURE__ */ o4(
43965
- "label",
43966
- {
43967
- className: "DocEditor__ImageField__uploadButton",
43968
- role: "button",
43969
- "aria-disabled": loading,
43970
- children: [
43974
+ ref: ref2,
43975
+ children: [
43976
+ img && img.src ? /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__imagePreview", children: [
43977
+ /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__imagePreview__controls", children: /* @__PURE__ */ o4(Tooltip, { label: "Remove image", children: /* @__PURE__ */ o4(
43978
+ ActionIcon,
43979
+ {
43980
+ className: "DocEditor__ImageField__imagePreview__controls__trash",
43981
+ onClick: () => removeImage(),
43982
+ children: /* @__PURE__ */ o4(IconTrash, { size: 16 })
43983
+ }
43984
+ ) }) }),
43985
+ /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__imagePreview__image", children: [
43986
+ /* @__PURE__ */ o4(
43987
+ "img",
43988
+ {
43989
+ src: img.gciUrl || img.src,
43990
+ width: img.width,
43991
+ height: img.height
43992
+ }
43993
+ ),
43994
+ /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__imagePreview__dimens", children: `${img.width}x${img.height}` })
43995
+ ] }),
43971
43996
  /* @__PURE__ */ o4(
43972
- "input",
43997
+ TextInput,
43973
43998
  {
43974
- type: "file",
43975
- accept,
43976
- onChange: onFileChange,
43977
- ref: inputRef
43999
+ className: "DocEditor__ImageField__imagePreview__image__url",
44000
+ size: "xs",
44001
+ radius: 0,
44002
+ value: img.gciUrl || img.src,
44003
+ disabled: true
43978
44004
  }
43979
44005
  ),
43980
- /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__uploadButton__icon", children: /* @__PURE__ */ o4(IconPhotoUp, { size: 16 }) }),
43981
- /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__uploadButton__label", children: "Upload image" })
43982
- ]
43983
- }
43984
- )
43985
- ] });
44006
+ /* @__PURE__ */ o4(
44007
+ TextInput,
44008
+ {
44009
+ className: "DocEditor__ImageField__imagePreview__image__alt",
44010
+ size: "xs",
44011
+ radius: 0,
44012
+ value: img.alt,
44013
+ label: "Alt text",
44014
+ onChange: (e3) => {
44015
+ setAltText(e3.currentTarget.value);
44016
+ }
44017
+ }
44018
+ )
44019
+ ] }) : /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__noImage", children: "No image" }),
44020
+ /* @__PURE__ */ o4(
44021
+ "label",
44022
+ {
44023
+ className: "DocEditor__ImageField__uploadButton",
44024
+ role: "button",
44025
+ "aria-disabled": loading,
44026
+ children: [
44027
+ /* @__PURE__ */ o4(
44028
+ "input",
44029
+ {
44030
+ type: "file",
44031
+ accept,
44032
+ onChange: onFileChange,
44033
+ ref: inputRef
44034
+ }
44035
+ ),
44036
+ /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__uploadButton__icon", children: /* @__PURE__ */ o4(IconPhotoUp, { size: 16 }) }),
44037
+ /* @__PURE__ */ o4("div", { className: "DocEditor__ImageField__uploadButton__label", children: loading ? "Uploading..." : "Upload image" })
44038
+ ]
44039
+ }
44040
+ )
44041
+ ]
44042
+ }
44043
+ );
43986
44044
  };
43987
44045
  DocEditor.FileField = (props) => {
43988
44046
  const field = props.field;
43989
44047
  const [file, setFile] = y2({});
43990
- const inputRef = A2(null);
43991
44048
  const [loading, setLoading] = y2(false);
43992
- s2(() => {
43993
- const unsubscribe = props.draft.subscribe(
43994
- props.deepKey,
43995
- (newValue) => {
43996
- setFile(newValue);
43997
- }
43998
- );
43999
- return unsubscribe;
44000
- }, []);
44001
- async function onFileChange(e3) {
44049
+ const [isDragging, setIsDragging] = y2(false);
44050
+ const inputRef = A2(null);
44051
+ const ref2 = A2(null);
44052
+ let accept = void 0;
44053
+ if (field.exts) {
44054
+ accept = field.exts.join(",");
44055
+ }
44056
+ async function removeFile() {
44057
+ setFile({});
44058
+ props.draft.removeKey(props.deepKey);
44059
+ }
44060
+ async function uploadFile(file2) {
44061
+ setLoading(true);
44002
44062
  try {
44003
- setLoading(true);
44004
- const inputEl = e3.target;
44005
- const files = inputEl.files || [];
44006
- const uploadedFile = files[0];
44007
- const file2 = await uploadFileToGCS(uploadedFile);
44008
- props.draft.updateKey(props.deepKey, file2);
44009
- setFile(file2);
44063
+ const uploadedFile = await uploadFileToGCS(file2);
44064
+ props.draft.updateKey(props.deepKey, uploadedFile);
44065
+ setFile(uploadedFile);
44010
44066
  setLoading(false);
44011
44067
  } catch (err) {
44012
44068
  console.error("file upload failed");
@@ -44024,43 +44080,109 @@ ${content}</tr>
44024
44080
  inputEl.value = "";
44025
44081
  }
44026
44082
  }
44027
- let accept = void 0;
44028
- if (field.exts) {
44029
- accept = field.exts.join(",");
44083
+ function onFileChange(e3) {
44084
+ const inputEl = e3.target;
44085
+ const files = inputEl.files || [];
44086
+ const file2 = files[0];
44087
+ if (file2) {
44088
+ uploadFile(file2);
44089
+ }
44030
44090
  }
44031
- return /* @__PURE__ */ o4("div", { className: "DocEditor__FileField", children: [
44032
- file && file.src ? /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__file", children: /* @__PURE__ */ o4(
44033
- TextInput,
44034
- {
44035
- className: "DocEditor__FileField__file__url",
44036
- size: "xs",
44037
- radius: 0,
44038
- value: file.src,
44039
- disabled: true
44091
+ const handleDragEnter = (e3) => {
44092
+ e3.preventDefault();
44093
+ setIsDragging(true);
44094
+ };
44095
+ const handleDragLeave = (e3) => {
44096
+ e3.preventDefault();
44097
+ setIsDragging(false);
44098
+ };
44099
+ const handleDrop = (e3) => {
44100
+ e3.preventDefault();
44101
+ setIsDragging(false);
44102
+ const files = e3.dataTransfer?.files || [];
44103
+ const file2 = files[0];
44104
+ if (file2) {
44105
+ console.log(`file dropped ("${props.deepKey}"):`, file2);
44106
+ uploadFile(file2);
44107
+ }
44108
+ };
44109
+ s2(() => {
44110
+ const unsubscribe = props.draft.subscribe(
44111
+ props.deepKey,
44112
+ (newValue) => {
44113
+ setFile(newValue);
44040
44114
  }
44041
- ) }) : /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__noFile", children: "No file" }),
44042
- /* @__PURE__ */ o4(
44043
- "label",
44044
- {
44045
- className: "DocEditor__FileField__uploadButton",
44046
- role: "button",
44047
- "aria-disabled": loading,
44048
- children: [
44049
- /* @__PURE__ */ o4(
44050
- "input",
44051
- {
44052
- type: "file",
44053
- accept,
44054
- onChange: onFileChange,
44055
- ref: inputRef
44056
- }
44057
- ),
44058
- /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__uploadButton__icon", children: /* @__PURE__ */ o4(IconFileUpload, { size: 16 }) }),
44059
- /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__uploadButton__label", children: "Upload file" })
44060
- ]
44115
+ );
44116
+ const dropzone = ref2.current;
44117
+ document.addEventListener("dragenter", handleDragEnter);
44118
+ document.addEventListener("dragover", handleDragEnter);
44119
+ document.addEventListener("dragleave", handleDragLeave);
44120
+ document.addEventListener("drop", handleDragLeave);
44121
+ if (dropzone) {
44122
+ dropzone.addEventListener("drop", handleDrop);
44123
+ }
44124
+ return () => {
44125
+ unsubscribe();
44126
+ document.removeEventListener("dragenter", handleDragEnter);
44127
+ document.removeEventListener("dragover", handleDragEnter);
44128
+ document.removeEventListener("dragleave", handleDragLeave);
44129
+ document.removeEventListener("drop", handleDragLeave);
44130
+ if (dropzone) {
44131
+ dropzone.removeEventListener("drop", handleDrop);
44061
44132
  }
44062
- )
44063
- ] });
44133
+ };
44134
+ }, []);
44135
+ return /* @__PURE__ */ o4(
44136
+ "div",
44137
+ {
44138
+ className: joinClassNames(
44139
+ "DocEditor__FileField",
44140
+ isDragging && "dragging"
44141
+ ),
44142
+ ref: ref2,
44143
+ children: [
44144
+ file && file.src && /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__controls", children: /* @__PURE__ */ o4(Tooltip, { label: "Remove file", children: /* @__PURE__ */ o4(
44145
+ ActionIcon,
44146
+ {
44147
+ className: "DocEditor__FileField__controls__trash",
44148
+ onClick: () => removeFile(),
44149
+ children: /* @__PURE__ */ o4(IconTrash, { size: 16 })
44150
+ }
44151
+ ) }) }),
44152
+ file && file.src ? /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__file", children: /* @__PURE__ */ o4(
44153
+ TextInput,
44154
+ {
44155
+ className: "DocEditor__FileField__file__url",
44156
+ size: "xs",
44157
+ radius: 0,
44158
+ value: file.src,
44159
+ disabled: true
44160
+ }
44161
+ ) }) : /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__noFile", children: "No file" }),
44162
+ /* @__PURE__ */ o4(
44163
+ "label",
44164
+ {
44165
+ className: "DocEditor__FileField__uploadButton",
44166
+ role: "button",
44167
+ "aria-disabled": loading,
44168
+ children: [
44169
+ /* @__PURE__ */ o4(
44170
+ "input",
44171
+ {
44172
+ type: "file",
44173
+ accept,
44174
+ onChange: onFileChange,
44175
+ ref: inputRef
44176
+ }
44177
+ ),
44178
+ /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__uploadButton__icon", children: /* @__PURE__ */ o4(IconFileUpload, { size: 16 }) }),
44179
+ /* @__PURE__ */ o4("div", { className: "DocEditor__FileField__uploadButton__label", children: loading ? "Uploading..." : "Upload file" })
44180
+ ]
44181
+ }
44182
+ )
44183
+ ]
44184
+ }
44185
+ );
44064
44186
  };
44065
44187
  DocEditor.ObjectField = (props) => {
44066
44188
  const field = props.field;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinkk/root-cms",
3
- "version": "1.0.0-beta.57",
3
+ "version": "1.0.0-beta.58",
4
4
  "author": "s@blinkk.com",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -56,7 +56,7 @@
56
56
  "//": "NOTE(stevenle): due to compat issues with mantine and preact, mantine is pinned to v4.2.12",
57
57
  "devDependencies": {
58
58
  "@babel/core": "^7.17.9",
59
- "@blinkk/root": "1.0.0-beta.57",
59
+ "@blinkk/root": "1.0.0-beta.58",
60
60
  "@emotion/react": "^11.10.5",
61
61
  "@firebase/app-compat": "^0.1.33",
62
62
  "@firebase/app-types": "^0.7.0",
@@ -90,7 +90,7 @@
90
90
  "vitest": "^0.18.1"
91
91
  },
92
92
  "peerDependencies": {
93
- "@blinkk/root": "1.0.0-beta.57",
93
+ "@blinkk/root": "1.0.0-beta.58",
94
94
  "firebase-admin": ">=11",
95
95
  "firebase-functions": ">=4",
96
96
  "preact": "10.x",