bard-attachment_field 0.3.1 → 0.4.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b550a7ccc4937f60e42b9a19129cae22ba0e357705e9c239d4962abbb6dc005
4
- data.tar.gz: 2d471d6085ac11fd2013006ae3397efc163c6ca0d91ca996d23697ce231eb50b
3
+ metadata.gz: 455080b5539151ab0d19ff817c8a8ca1c81af69abc7de283f61ee6753f94cdc2
4
+ data.tar.gz: 3bc698d1aca4b7cdbe50dab75758bdf65b2a818fa8d3a3ce17651d7ed97a7ced
5
5
  SHA512:
6
- metadata.gz: 7aba1939902c829690034fc9f2db0f0b6068fca76f297c54baaba98f6f227c35e4f27b397eaeefd13175d52af565c924d2ee3b7e8d27ea0f30d8126a524c47be
7
- data.tar.gz: d51ffb195e0637448f30100051d5cc0e6a99073d7bc1e9858c5edb18a04b88b26e2b52ed3b462580024a352a2f6ec8a101ca2a6096f828dd236dcc1359509668
6
+ metadata.gz: a5625056035d5e5aa4e1d51de78a21081f3b5e233db5c1582167537f936d904c5cc70070d55a234b308958cc25da90588c0a1543e779613b70bd091d10dbf8b7
7
+ data.tar.gz: e860eb4c5f28cd7b1c4519cbc06dc3d227c5dcb0b2cf4a3bc6d74aae77d9709dc5f0d418a04c3965a411ec5edb17d1c03df24aef0e491c346a40bd83be05d641
@@ -5280,7 +5280,7 @@ var request = (verb, url, payload, headers) => {
5280
5280
  });
5281
5281
  };
5282
5282
  var get = (url, payload = {}, headers = {}) => request("get", url, payload, headers);
5283
- var attachmentFileCss = `:host{display:block;width:100%;max-width:100%;font-size:13px}figure{margin:0}.progress-details{position:relative;display:flex;align-items:center}progress-bar{flex:1 0;padding:0 10px}progress-bar.pending{opacity:0.5}progress-bar.complete{opacity:0.8}progress-bar:not(.complete)+.progress-icon{display:none}progress-bar.complete+.progress-icon{content:url('data:image/svg+xml;utf8,<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve"><g><path d="M6.3,9.1c0.2,0,0.5,0.1,0.7,0.4c0.5,0.5,1,1,1.4,1.4c0.3,0.3,0.3,0.3,0.6,0c1.4-1.3,2.7-2.6,4-3.9c0.3-0.3,0.6-0.4,1-0.4 c0.5,0.1,0.9,0.6,0.7,1.1c-0.1,0.2-0.2,0.4-0.3,0.6c-1.6,1.6-3.2,3.2-4.8,4.8c-0.5,0.5-1,0.5-1.6,0c-0.8-0.7-1.5-1.5-2.3-2.3 c-0.3-0.3-0.5-0.6-0.3-1.1C5.5,9.3,5.8,9.1,6.3,9.1z"/></g></svg>');filter:invert(100%)}.progress-icon{display:inline-block;flex:0 0 20px;width:28px;height:28px;background-size:contain;position:absolute;right:30px;z-index:1}progress-bar.error{background:#f8b3b1;background:rgba(74, 70, 70, 0.25);opacity:1}.progress-bar a{color:#fff}.download-link{padding-right:20px;color:#fff}.remove-media{display:inline-block;content:url('data:image/svg+xml;utf8,<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve"><g><path d="M0,19.9C0.2,8.5,9.2-0.1,20.1,0C31.8,0.1,40.2,9.5,40,20.4c-0.2,11-8.9,19.7-20.1,19.6C8,39.9,0,30.5,0,19.9z M20,3.7 c-9,0-16.3,7-16.3,16.2C3.7,29,10.9,36.3,20,36.3c9,0,16.3-7.1,16.4-16.3C36.3,11,29.2,3.8,20,3.7z"/><path d="M17.3,20c-0.2-0.2-0.3-0.4-0.5-0.6c-1-1-2-1.9-2.9-2.9c-0.5-0.5-0.8-1.1-0.7-1.9c0.1-0.7,0.5-1.2,1.2-1.4 c0.8-0.2,1.5,0,2.1,0.6c1,1,2,2,3,3.1c0.3,0.4,0.6,0.3,0.9,0c1-1,2-2,3-3c0.3-0.3,0.7-0.5,1.1-0.6c0.8-0.2,1.6,0.1,2,0.8 c0.4,0.8,0.3,1.7-0.4,2.4c-1,1-2,2-3,3c-0.2,0.2-0.3,0.4-0.5,0.6c1.2,1.2,2.3,2.3,3.4,3.4c0.6,0.6,0.9,1.3,0.6,2.2 c-0.4,1.1-1.7,1.6-2.6,1c-0.3-0.2-0.5-0.4-0.8-0.6c-1-1-1.9-1.9-2.9-2.9c-0.3-0.3-0.5-0.3-0.9,0c-1,1-2,2.1-3,3 c-0.4,0.4-1,0.6-1.5,0.8c-0.6,0.1-1.2-0.2-1.5-0.8c-0.4-0.6-0.5-1.3-0.1-1.9c0.2-0.3,0.4-0.5,0.6-0.7C15.1,22.3,16.2,21.2,17.3,20z "/></g></svg>');flex:0 0 25px;width:25px;height:20px;align-items:center;opacity:0.25}.remove-media:hover{opacity:1;filter:invert(50%)sepia(100%)saturate(10000%)}.remove-media span{display:inline-block;text-indent:-9999px;color:transparent}.retry-media{color:#c00;font-size:12px;text-decoration:underline;cursor:pointer;padding-left:8px}.retry-media:hover{color:#900}.validation-error{color:#c00;font-size:12px;margin:4px 0 0 10px}`;
5283
+ var attachmentFileCss = `:host{display:block;width:100%;max-width:100%;font-size:13px}figure{margin:0}.progress-details{position:relative;display:flex;align-items:center}progress-bar{flex:1 0;padding:0 10px}progress-bar.pending{opacity:0.5}progress-bar.complete{opacity:0.8}progress-bar:not(.complete)+.progress-icon{display:none}progress-bar.complete+.progress-icon{content:url('data:image/svg+xml;utf8,<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve"><g><path d="M6.3,9.1c0.2,0,0.5,0.1,0.7,0.4c0.5,0.5,1,1,1.4,1.4c0.3,0.3,0.3,0.3,0.6,0c1.4-1.3,2.7-2.6,4-3.9c0.3-0.3,0.6-0.4,1-0.4 c0.5,0.1,0.9,0.6,0.7,1.1c-0.1,0.2-0.2,0.4-0.3,0.6c-1.6,1.6-3.2,3.2-4.8,4.8c-0.5,0.5-1,0.5-1.6,0c-0.8-0.7-1.5-1.5-2.3-2.3 c-0.3-0.3-0.5-0.6-0.3-1.1C5.5,9.3,5.8,9.1,6.3,9.1z"/></g></svg>');filter:invert(100%)}.progress-icon{display:inline-block;flex:0 0 20px;width:28px;height:28px;background-size:contain;position:absolute;right:30px;z-index:1}progress-bar.error{background:var(--input-attachment-error-bg, rgba(74, 70, 70, 0.25));opacity:1}.progress-bar a{color:#fff}.download-link{padding-right:20px;color:#fff}.remove-media{display:inline-block;content:url('data:image/svg+xml;utf8,<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve"><g><path d="M0,19.9C0.2,8.5,9.2-0.1,20.1,0C31.8,0.1,40.2,9.5,40,20.4c-0.2,11-8.9,19.7-20.1,19.6C8,39.9,0,30.5,0,19.9z M20,3.7 c-9,0-16.3,7-16.3,16.2C3.7,29,10.9,36.3,20,36.3c9,0,16.3-7.1,16.4-16.3C36.3,11,29.2,3.8,20,3.7z"/><path d="M17.3,20c-0.2-0.2-0.3-0.4-0.5-0.6c-1-1-2-1.9-2.9-2.9c-0.5-0.5-0.8-1.1-0.7-1.9c0.1-0.7,0.5-1.2,1.2-1.4 c0.8-0.2,1.5,0,2.1,0.6c1,1,2,2,3,3.1c0.3,0.4,0.6,0.3,0.9,0c1-1,2-2,3-3c0.3-0.3,0.7-0.5,1.1-0.6c0.8-0.2,1.6,0.1,2,0.8 c0.4,0.8,0.3,1.7-0.4,2.4c-1,1-2,2-3,3c-0.2,0.2-0.3,0.4-0.5,0.6c1.2,1.2,2.3,2.3,3.4,3.4c0.6,0.6,0.9,1.3,0.6,2.2 c-0.4,1.1-1.7,1.6-2.6,1c-0.3-0.2-0.5-0.4-0.8-0.6c-1-1-1.9-1.9-2.9-2.9c-0.3-0.3-0.5-0.3-0.9,0c-1,1-2,2.1-3,3 c-0.4,0.4-1,0.6-1.5,0.8c-0.6,0.1-1.2-0.2-1.5-0.8c-0.4-0.6-0.5-1.3-0.1-1.9c0.2-0.3,0.4-0.5,0.6-0.7C15.1,22.3,16.2,21.2,17.3,20z "/></g></svg>');flex:0 0 25px;width:25px;height:20px;align-items:center;opacity:0.25}.remove-media:hover{opacity:1;filter:invert(50%)sepia(100%)saturate(10000%)}.remove-media span{display:inline-block;text-indent:-9999px;color:transparent}.retry-media{color:var(--input-attachment-error-color, #c00);font-size:12px;text-decoration:underline;cursor:pointer;padding-left:8px}.retry-media:hover{color:var(--input-attachment-error-color-hover, #900)}.validation-error{color:var(--input-attachment-error-color, #c00);font-size:12px;margin:4px 0 0 10px}@media (prefers-color-scheme: dark){:host{--input-attachment-error-color:#f66;--input-attachment-error-color-hover:#f99;--input-attachment-error-bg:rgba(200, 100, 100, 0.2)}.remove-media{filter:invert(1)}}`;
5284
5284
  var AttachmentFile = /* @__PURE__ */ proxyCustomElement(class AttachmentFile2 extends H {
5285
5285
  constructor(registerHost2) {
5286
5286
  super();
@@ -5715,11 +5715,12 @@ var ProgressBar = class extends HTMLElement {
5715
5715
  --bar-height: 32px;
5716
5716
  --bar-radius: 4px;
5717
5717
  --bar-padding: 8px;
5718
+ --bar-border-color: #999;
5718
5719
 
5719
5720
  display: block;
5720
5721
  position: relative;
5721
5722
  padding: var(--bar-padding);
5722
- border: 1px solid #999;
5723
+ border: 1px solid var(--bar-border-color);
5723
5724
  border-radius: var(--bar-radius);
5724
5725
  }
5725
5726
 
@@ -5753,7 +5754,7 @@ var ProgressBar = class extends HTMLElement {
5753
5754
  if (!customElements.get("progress-bar")) {
5754
5755
  customElements.define("progress-bar", ProgressBar);
5755
5756
  }
5756
- var inputAttachmentCss = ":host{display:block;padding:25px;color:var(--input-attachment-text-color, #000);font-size:13px}:host *{box-sizing:border-box;position:relative}file-drop{cursor:pointer;display:block;outline-offset:-10px;background:rgba(255,255,255, 0.25);padding:20px;text-align:center;transition:all 0.15s;outline:2px dashed rgba(0,0,0,0.25);color:#444;font-size:14px}file-drop.-full{width:100%}p{padding:10px 20px;margin:0}.-dragover{background:rgba(255,255,255,0.5);outline:2px dashed rgba(0,0,0,0.25)}.media-preview{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:center}// UPLOADER .direct-upload-wrapper{position:fixed;z-index:9999;top:0;left:0;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:rgba(#333, 0.9)}.direct-upload-content{display:block;background:#fcfcfc;padding:40px 60px 60px;border-radius:3px;width:60vw}.direct-upload-content h3{border-bottom:2px solid #1f1f1f;margin-bottom:20px}.separate-upload{padding:0 10px;margin-top:10px;font-size:0.9em}.direct-upload--pending{opacity:0.6}.direct-upload--complete{opacity:0.4}.direct-upload--error{border-color:red}input[type=file][data-direct-upload-url][disabled]{display:none}:host.separate-upload{padding:0 10px;margin-top:10px;font-size:0.9em}";
5757
+ var inputAttachmentCss = ":host{display:block;padding:25px;color:var(--input-attachment-text-color, #000);font-size:13px}:host *{box-sizing:border-box;position:relative}file-drop{cursor:pointer;display:block;outline-offset:-10px;background:var(--input-attachment-drop-bg, rgba(255,255,255, 0.25));padding:20px;text-align:center;transition:all 0.15s;outline:2px dashed var(--input-attachment-drop-border, rgba(0,0,0,0.25));color:var(--input-attachment-drop-color, #444);font-size:14px}file-drop.-full{width:100%}p{padding:10px 20px;margin:0}.-dragover{background:var(--input-attachment-drop-bg-active, rgba(255,255,255,0.5));outline:2px dashed var(--input-attachment-drop-border, rgba(0,0,0,0.25))}.media-preview{display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:center}// UPLOADER .direct-upload-wrapper{position:fixed;z-index:9999;top:0;left:0;width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:var(--input-attachment-overlay-bg, rgba(#333, 0.9))}.direct-upload-content{display:block;background:var(--input-attachment-dialog-bg, #fcfcfc);color:var(--input-attachment-text-color, #000);padding:40px 60px 60px;border-radius:3px;width:60vw}.direct-upload-content h3{border-bottom:2px solid var(--input-attachment-dialog-border, #1f1f1f);margin-bottom:20px}.separate-upload{padding:0 10px;margin-top:10px;font-size:0.9em}.direct-upload--pending{opacity:0.6}.direct-upload--complete{opacity:0.4}.direct-upload--error{border-color:var(--input-attachment-error-color, red)}input[type=file][data-direct-upload-url][disabled]{display:none}:host.separate-upload{padding:0 10px;margin-top:10px;font-size:0.9em}@media (prefers-color-scheme: dark){:host{--input-attachment-text-color:#e0e0e0;--input-attachment-drop-bg:rgba(255,255,255, 0.08);--input-attachment-drop-border:rgba(255,255,255, 0.25);--input-attachment-drop-color:#bbb;--input-attachment-drop-bg-active:rgba(255,255,255, 0.15);--input-attachment-dialog-bg:#2a2a2a;--input-attachment-dialog-border:#555;--input-attachment-error-color:#f66}}";
5757
5758
  var InputAttachment$1 = /* @__PURE__ */ proxyCustomElement(class InputAttachment extends H {
5758
5759
  get el() {
5759
5760
  return this;
@@ -5788,54 +5789,12 @@ var InputAttachment$1 = /* @__PURE__ */ proxyCustomElement(class InputAttachment
5788
5789
  const existingFiles = Array.from(this.el.children).filter((e) => e.tagName == "ATTACHMENT-FILE");
5789
5790
  if (existingFiles.length > 0)
5790
5791
  this.files = existingFiles;
5791
- this.restoreFromLocalStorage();
5792
5792
  if (this.files.length > 0)
5793
5793
  this.updateFormValue();
5794
5794
  }
5795
5795
  componentDidLoad() {
5796
- if (this.form) {
5797
- this.form.addEventListener("submit", () => this.clearLocalStorage());
5798
- }
5799
5796
  this.fileInput?.addEventListener("change", this.handleFileInputChange);
5800
5797
  }
5801
- get localStorageKey() {
5802
- if (!this.form || !this.name)
5803
- return null;
5804
- const formId = this.form.id || this.form.action || "form";
5805
- return `input-attachment:${formId}:${this.name}`;
5806
- }
5807
- saveToLocalStorage() {
5808
- const key = this.localStorageKey;
5809
- if (!key)
5810
- return;
5811
- const data = this.persistenceData;
5812
- if (data.length > 0) {
5813
- localStorage.setItem(key, JSON.stringify(data));
5814
- } else {
5815
- localStorage.removeItem(key);
5816
- }
5817
- }
5818
- restoreFromLocalStorage() {
5819
- const key = this.localStorageKey;
5820
- if (!key || this.files.length > 0)
5821
- return;
5822
- try {
5823
- const stored = localStorage.getItem(key);
5824
- if (stored) {
5825
- const data = JSON.parse(stored);
5826
- if (Array.isArray(data) && data.length > 0) {
5827
- this.restoreFromPersistence(data);
5828
- }
5829
- }
5830
- } catch (e) {
5831
- }
5832
- }
5833
- clearLocalStorage() {
5834
- const key = this.localStorageKey;
5835
- if (key) {
5836
- localStorage.removeItem(key);
5837
- }
5838
- }
5839
5798
  // Methods
5840
5799
  get files() {
5841
5800
  return this._files;
@@ -5848,23 +5807,7 @@ var InputAttachment$1 = /* @__PURE__ */ proxyCustomElement(class InputAttachment
5848
5807
  this.fireChangeEvent();
5849
5808
  }
5850
5809
  get value() {
5851
- return this.files.map((e) => e.value);
5852
- }
5853
- set value(val) {
5854
- const newValue = val || [];
5855
- if (JSON.stringify(this.value) !== JSON.stringify(newValue)) {
5856
- this.files = newValue.map((signedId) => {
5857
- const attachmentFile = document.createElement("attachment-file");
5858
- attachmentFile.name = this.name;
5859
- attachmentFile.preview = this.preview;
5860
- attachmentFile.signedId = signedId;
5861
- return attachmentFile;
5862
- });
5863
- }
5864
- }
5865
- // For form-persistence: store complete attachment data (not just signed_ids)
5866
- get persistenceData() {
5867
- return this.files.map((f) => ({
5810
+ return JSON.stringify(this.files.map((f) => ({
5868
5811
  value: f.value,
5869
5812
  filename: f.filename,
5870
5813
  src: f.src,
@@ -5872,11 +5815,14 @@ var InputAttachment$1 = /* @__PURE__ */ proxyCustomElement(class InputAttachment
5872
5815
  percent: f.percent,
5873
5816
  size: f.size,
5874
5817
  filetype: f.filetype
5875
- }));
5818
+ })));
5876
5819
  }
5877
- restoreFromPersistence(data) {
5878
- if (!Array.isArray(data) || data.length === 0)
5820
+ set value(val) {
5821
+ const data = JSON.parse(val || "[]");
5822
+ if (data.length === 0) {
5823
+ this.files = [];
5879
5824
  return;
5825
+ }
5880
5826
  this.files = data.map((item) => {
5881
5827
  const attachmentFile = document.createElement("attachment-file");
5882
5828
  attachmentFile.name = this.name;
@@ -5896,7 +5842,7 @@ var InputAttachment$1 = /* @__PURE__ */ proxyCustomElement(class InputAttachment
5896
5842
  if (!this.name || !this.internals?.setFormValue)
5897
5843
  return;
5898
5844
  const formData = new FormData();
5899
- const values = this.value.filter((v) => v);
5845
+ const values = this.files.map((f) => f.value).filter((v) => v);
5900
5846
  if (this.multiple) {
5901
5847
  values.forEach((signedId) => formData.append(this.name, signedId));
5902
5848
  if (values.length === 0)
@@ -5905,7 +5851,6 @@ var InputAttachment$1 = /* @__PURE__ */ proxyCustomElement(class InputAttachment
5905
5851
  formData.set(this.name, values[0] || "");
5906
5852
  }
5907
5853
  this.internals.setFormValue(formData);
5908
- this.saveToLocalStorage();
5909
5854
  if (this.required && this.files.length === 0) {
5910
5855
  this.internals.setValidity({ valueMissing: true }, "Please select a file.", this.fileInput);
5911
5856
  } else {
@@ -5918,8 +5863,7 @@ var InputAttachment$1 = /* @__PURE__ */ proxyCustomElement(class InputAttachment
5918
5863
  }
5919
5864
  }
5920
5865
  reset() {
5921
- this.value = [];
5922
- this.clearLocalStorage();
5866
+ this.files = [];
5923
5867
  }
5924
5868
  handleFileInputChange = () => {
5925
5869
  if (!this.fileInput?.files?.length)
@@ -5956,12 +5900,12 @@ var InputAttachment$1 = /* @__PURE__ */ proxyCustomElement(class InputAttachment
5956
5900
  return this.disabled || !!this.el.closest("fieldset[disabled]");
5957
5901
  }
5958
5902
  render() {
5959
- return h(Host, { key: "e63c2624a4d88232adacc7d1610983fac89eb9db" }, h("input", { key: "baea544d4d62e31e1228d92ba8b0356d40e75ce5", ref: (el) => this.fileInput = el, type: "file", multiple: this.multiple, accept: this.accepts, required: this.required && this.files.length === 0, disabled: this.isDisabled, onChange: () => this.handleFileInputChange(), style: {
5903
+ return h(Host, { key: "d6cdfb923931f8232c1a6118dcb38889266cc5d0" }, h("input", { key: "e77f98d380aa090cabc70f712fd0787c0cf1373b", ref: (el) => this.fileInput = el, type: "file", multiple: this.multiple, accept: this.accepts, required: this.required && this.files.length === 0, disabled: this.isDisabled, onChange: () => this.handleFileInputChange(), style: {
5960
5904
  opacity: "0.01",
5961
5905
  width: "1px",
5962
5906
  height: "1px",
5963
5907
  zIndex: "-999"
5964
- } }), h("file-drop", { key: "0b5621010445988462b8056cb360bf18ac6b0b59", onClick: () => this.fileInput?.click(), onDrop: this.handleDrop }, h("p", { key: "718fe6b91ed9cdc5c5a2b54317fad8f1f25b3cd4", part: "title" }, h("strong", { key: "f28b9ffc58bb086bb1941ee01e943fe5068482a4" }, "Choose ", this.multiple ? "files" : "file", " "), h("span", { key: "eeca7b2f619a7a4d7863f98ba37218d6a66fdcfd" }, "or drag ", this.multiple ? "them" : "it", " here.")), h("div", { key: "2a1d0a824139f5c26833611a5523354965be0d9d", class: `media-preview ${this.multiple ? "-stacked" : ""}` }, h("slot", { key: "59455a857d01dfabd7aa5e697616f4be72cbbb20" }))));
5908
+ } }), h("file-drop", { key: "87f1d105a94d63b1ff23061cca0787d6dcf909cf", onClick: () => this.fileInput?.click(), onDrop: this.handleDrop }, h("p", { key: "97fbb415cf9d528f1813a4c4ab512819f63e1838", part: "title" }, h("strong", { key: "f856b14d87bee688d2bec237b0cdef748f9ddc33" }, "Choose ", this.multiple ? "files" : "file", " "), h("span", { key: "c51929dd34c8826a434ee15dc577ce8ae4e65c72" }, "or drag ", this.multiple ? "them" : "it", " here.")), h("div", { key: "9a847df1d30360e515f6dd69652841ff56efb915", class: `media-preview ${this.multiple ? "-stacked" : ""}` }, h("slot", { key: "e2d06462bddde0191f1071eb6f1fd78b04c4b49a" }))));
5965
5909
  }
5966
5910
  componentDidRender() {
5967
5911
  if (this.files.length === 0) {
@@ -159,20 +159,64 @@ checkValidity(): boolean
159
159
 
160
160
  ## Styling
161
161
 
162
- The component uses Shadow DOM with customizable CSS custom properties:
162
+ The components use Shadow DOM, so external CSS cannot reach their internals. Instead, all visual properties are exposed as CSS custom properties that pierce the shadow boundary. Set them on the `input-attachment` element or any ancestor.
163
+
164
+ ### CSS Custom Properties
165
+
166
+ #### Text & Drop Zone
167
+
168
+ | Property | Default | Description |
169
+ | -------- | ------- | ----------- |
170
+ | `--input-attachment-text-color` | `#000` | Main text color |
171
+ | `--input-attachment-drop-bg` | `rgba(255,255,255, 0.25)` | Drop zone background |
172
+ | `--input-attachment-drop-border` | `rgba(0,0,0, 0.25)` | Drop zone dashed border |
173
+ | `--input-attachment-drop-color` | `#444` | Drop zone text color |
174
+ | `--input-attachment-drop-bg-active` | `rgba(255,255,255, 0.5)` | Drop zone background during drag-over |
175
+
176
+ #### Upload Dialog
177
+
178
+ | Property | Default | Description |
179
+ | -------- | ------- | ----------- |
180
+ | `--input-attachment-overlay-bg` | `rgba(51, 51, 51, 0.9)` | Modal overlay background |
181
+ | `--input-attachment-dialog-bg` | `#fcfcfc` | Dialog content background |
182
+ | `--input-attachment-dialog-border` | `#1f1f1f` | Dialog heading border |
183
+
184
+ #### Errors
185
+
186
+ | Property | Default | Description |
187
+ | -------- | ------- | ----------- |
188
+ | `--input-attachment-error-color` | `#c00` | Validation error and retry link text |
189
+ | `--input-attachment-error-color-hover` | `#900` | Retry link hover color |
190
+ | `--input-attachment-error-bg` | `rgba(74, 70, 70, 0.25)` | Error state progress bar background |
191
+
192
+ ### Dark Mode
193
+
194
+ The components automatically adapt to dark color schemes via `prefers-color-scheme: dark`. No configuration is needed — if the user's OS or browser is set to dark mode, the components will use appropriate colors.
195
+
196
+ To manually apply a dark theme (e.g., for a toggle-based dark mode), set the custom properties yourself:
163
197
 
164
198
  ```css
165
- input-attachment {
166
- --input-attachment-text-color: #000;
199
+ .dark-theme input-attachment {
200
+ --input-attachment-text-color: #e0e0e0;
201
+ --input-attachment-drop-bg: rgba(255,255,255, 0.08);
202
+ --input-attachment-drop-border: rgba(255,255,255, 0.25);
203
+ --input-attachment-drop-color: #bbb;
204
+ --input-attachment-drop-bg-active: rgba(255,255,255, 0.15);
205
+ --input-attachment-dialog-bg: #2a2a2a;
206
+ --input-attachment-dialog-border: #555;
207
+ --input-attachment-error-color: #f66;
208
+ --input-attachment-error-color-hover: #f99;
209
+ --input-attachment-error-bg: rgba(200, 100, 100, 0.2);
167
210
  }
168
211
  ```
169
212
 
170
- Style the file-drop area using the `::part()` pseudo-element:
213
+ ### Parts
214
+
215
+ Style the file-drop title using the `::part()` pseudo-element:
171
216
 
172
217
  ```css
173
218
  input-attachment::part(title) {
174
219
  font-size: 16px;
175
- color: #333;
176
220
  }
177
221
  ```
178
222
 
Binary file
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@botandrose/file-drop": "^0.1.0",
36
- "@botandrose/progress-bar": "^0.1.1",
36
+ "@botandrose/progress-bar": "^0.1.2",
37
37
  "@rails/activestorage": "^8.1.0",
38
38
  "@rails/request.js": "0.0.13",
39
39
  "@stencil/core": "^4.38.2",
@@ -49,8 +49,7 @@ progress-bar.complete + .progress-icon{
49
49
  }
50
50
 
51
51
  progress-bar.error{
52
- background: #f8b3b1;
53
- background: rgba(74, 70, 70, 0.25);
52
+ background: var(--input-attachment-error-bg, rgba(74, 70, 70, 0.25));
54
53
  opacity: 1;
55
54
  }
56
55
 
@@ -83,7 +82,7 @@ progress-bar.error{
83
82
  }
84
83
 
85
84
  .retry-media{
86
- color: #c00;
85
+ color: var(--input-attachment-error-color, #c00);
87
86
  font-size: 12px;
88
87
  text-decoration: underline;
89
88
  cursor: pointer;
@@ -91,11 +90,23 @@ progress-bar.error{
91
90
  }
92
91
 
93
92
  .retry-media:hover{
94
- color: #900;
93
+ color: var(--input-attachment-error-color-hover, #900);
95
94
  }
96
95
 
97
96
  .validation-error{
98
- color: #c00;
97
+ color: var(--input-attachment-error-color, #c00);
99
98
  font-size: 12px;
100
99
  margin: 4px 0 0 10px;
101
100
  }
101
+
102
+ @media (prefers-color-scheme: dark) {
103
+ :host {
104
+ --input-attachment-error-color: #f66;
105
+ --input-attachment-error-color-hover: #f99;
106
+ --input-attachment-error-bg: rgba(200, 100, 100, 0.2);
107
+ }
108
+
109
+ .remove-media {
110
+ filter: invert(1);
111
+ }
112
+ }
@@ -14,12 +14,12 @@ file-drop {
14
14
  cursor: pointer;
15
15
  display: block;
16
16
  outline-offset: -10px;
17
- background: rgba(255,255,255, 0.25);
17
+ background: var(--input-attachment-drop-bg, rgba(255,255,255, 0.25));
18
18
  padding: 20px;
19
19
  text-align: center;
20
20
  transition: all 0.15s;
21
- outline: 2px dashed rgba(0,0,0,0.25);
22
- color: #444;
21
+ outline: 2px dashed var(--input-attachment-drop-border, rgba(0,0,0,0.25));
22
+ color: var(--input-attachment-drop-color, #444);
23
23
  font-size: 14px;
24
24
  }
25
25
 
@@ -33,8 +33,8 @@ p{
33
33
  }
34
34
 
35
35
  .-dragover{
36
- background: rgba(255,255,255,0.5);
37
- outline: 2px dashed rgba(0,0,0,0.25);
36
+ background: var(--input-attachment-drop-bg-active, rgba(255,255,255,0.5));
37
+ outline: 2px dashed var(--input-attachment-drop-border, rgba(0,0,0,0.25));
38
38
  }
39
39
 
40
40
  .media-preview {
@@ -56,18 +56,19 @@ p{
56
56
  display: flex;
57
57
  align-items: center;
58
58
  justify-content: center;
59
- background: rgba(#333, 0.9);
59
+ background: var(--input-attachment-overlay-bg, rgba(#333, 0.9));
60
60
  }
61
61
 
62
62
  .direct-upload-content{
63
63
  display: block;
64
- background: #fcfcfc;
64
+ background: var(--input-attachment-dialog-bg, #fcfcfc);
65
+ color: var(--input-attachment-text-color, #000);
65
66
  padding: 40px 60px 60px;
66
67
  border-radius: 3px;
67
68
  width: 60vw;
68
69
  }
69
70
  .direct-upload-content h3{
70
- border-bottom: 2px solid #1f1f1f;
71
+ border-bottom: 2px solid var(--input-attachment-dialog-border, #1f1f1f);
71
72
  margin-bottom: 20px;
72
73
  }
73
74
 
@@ -86,7 +87,7 @@ p{
86
87
  }
87
88
 
88
89
  .direct-upload--error{
89
- border-color: red;
90
+ border-color: var(--input-attachment-error-color, red);
90
91
  }
91
92
 
92
93
  input[type=file][data-direct-upload-url][disabled]{
@@ -98,3 +99,16 @@ input[type=file][data-direct-upload-url][disabled]{
98
99
  margin-top: 10px;
99
100
  font-size: 0.9em;
100
101
  }
102
+
103
+ @media (prefers-color-scheme: dark) {
104
+ :host {
105
+ --input-attachment-text-color: #e0e0e0;
106
+ --input-attachment-drop-bg: rgba(255,255,255, 0.08);
107
+ --input-attachment-drop-border: rgba(255,255,255, 0.25);
108
+ --input-attachment-drop-color: #bbb;
109
+ --input-attachment-drop-bg-active: rgba(255,255,255, 0.15);
110
+ --input-attachment-dialog-bg: #2a2a2a;
111
+ --input-attachment-dialog-border: #555;
112
+ --input-attachment-error-color: #f66;
113
+ }
114
+ }
@@ -46,65 +46,15 @@ export class InputAttachment {
46
46
  const existingFiles = Array.from(this.el.children).filter(e => e.tagName == "ATTACHMENT-FILE");
47
47
  if(existingFiles.length > 0) this.files = existingFiles
48
48
 
49
- // Restore from localStorage BEFORE setting initial validity (which triggers saveToLocalStorage)
50
- this.restoreFromLocalStorage()
51
-
52
49
  // Set initial validity state (only if we have files from above)
53
50
  if(this.files.length > 0) this.updateFormValue()
54
51
  }
55
52
 
56
53
  componentDidLoad() {
57
- // Clear persistence data when form is submitted
58
- if (this.form) {
59
- this.form.addEventListener('submit', () => this.clearLocalStorage())
60
- }
61
-
62
54
  // Listen for file input changes directly (JSX onChange doesn't reliably work in shadow DOM)
63
55
  this.fileInput?.addEventListener('change', this.handleFileInputChange)
64
56
  }
65
57
 
66
- get localStorageKey() {
67
- if (!this.form || !this.name) return null
68
- const formId = this.form.id || this.form.action || 'form'
69
- return `input-attachment:${formId}:${this.name}`
70
- }
71
-
72
- saveToLocalStorage() {
73
- const key = this.localStorageKey
74
- if (!key) return
75
-
76
- const data = this.persistenceData
77
- if (data.length > 0) {
78
- localStorage.setItem(key, JSON.stringify(data))
79
- } else {
80
- localStorage.removeItem(key)
81
- }
82
- }
83
-
84
- restoreFromLocalStorage() {
85
- const key = this.localStorageKey
86
- if (!key || this.files.length > 0) return
87
-
88
- try {
89
- const stored = localStorage.getItem(key)
90
- if (stored) {
91
- const data = JSON.parse(stored)
92
- if (Array.isArray(data) && data.length > 0) {
93
- this.restoreFromPersistence(data)
94
- }
95
- }
96
- } catch (e) {
97
- // Invalid JSON, ignore
98
- }
99
- }
100
-
101
- clearLocalStorage() {
102
- const key = this.localStorageKey
103
- if (key) {
104
- localStorage.removeItem(key)
105
- }
106
- }
107
-
108
58
  // Methods
109
59
 
110
60
  get files() {
@@ -119,25 +69,7 @@ export class InputAttachment {
119
69
  }
120
70
 
121
71
  get value() {
122
- return this.files.map(e => e.value)
123
- }
124
-
125
- set value(val) {
126
- const newValue = val || []
127
- if(JSON.stringify(this.value) !== JSON.stringify(newValue)) { // this is insane. javascript is fucking garbage.
128
- this.files = newValue.map(signedId => {
129
- const attachmentFile = document.createElement('attachment-file') as any
130
- attachmentFile.name = this.name
131
- attachmentFile.preview = this.preview
132
- attachmentFile.signedId = signedId
133
- return attachmentFile
134
- })
135
- }
136
- }
137
-
138
- // For form-persistence: store complete attachment data (not just signed_ids)
139
- get persistenceData() {
140
- return this.files.map(f => ({
72
+ return JSON.stringify(this.files.map(f => ({
141
73
  value: f.value,
142
74
  filename: f.filename,
143
75
  src: f.src,
@@ -145,12 +77,15 @@ export class InputAttachment {
145
77
  percent: f.percent,
146
78
  size: f.size,
147
79
  filetype: f.filetype,
148
- }))
80
+ })))
149
81
  }
150
82
 
151
- restoreFromPersistence(data: any[]) {
152
- if (!Array.isArray(data) || data.length === 0) return
153
-
83
+ set value(val) {
84
+ const data = JSON.parse(val || "[]")
85
+ if (data.length === 0) {
86
+ this.files = []
87
+ return
88
+ }
154
89
  this.files = data.map(item => {
155
90
  const attachmentFile = document.createElement('attachment-file') as any
156
91
  attachmentFile.name = this.name
@@ -170,7 +105,7 @@ export class InputAttachment {
170
105
  updateFormValue() {
171
106
  if (!this.name || !this.internals?.setFormValue) return
172
107
  const formData = new FormData()
173
- const values = this.value.filter(v => v) // filter out empty values
108
+ const values = this.files.map(f => f.value).filter(v => v)
174
109
  if (this.multiple) {
175
110
  // For has_many_attached: append each signed_id separately
176
111
  values.forEach(signedId => formData.append(this.name, signedId))
@@ -182,9 +117,6 @@ export class InputAttachment {
182
117
  }
183
118
  this.internals.setFormValue(formData)
184
119
 
185
- // Save to localStorage for persistence across page reloads
186
- this.saveToLocalStorage()
187
-
188
120
  // Update validity state - check for required and child validation errors
189
121
  if (this.required && this.files.length === 0) {
190
122
  this.internals.setValidity({ valueMissing: true }, "Please select a file.", this.fileInput)
@@ -202,8 +134,7 @@ export class InputAttachment {
202
134
  }
203
135
 
204
136
  reset() {
205
- this.value = []
206
- this.clearLocalStorage()
137
+ this.files = []
207
138
  }
208
139
 
209
140
  handleFileInputChange = () => {
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bard
4
4
  module AttachmentField
5
- VERSION = "0.3.1"
5
+ VERSION = "0.4.1"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bard-attachment_field
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micah Geisel
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-15 00:00:00.000000000 Z
11
+ date: 2026-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activestorage