@synergy-design-system/components
Version:
This package provides the base of the Synergy Design System as native web components. It uses [lit](https://www.lit.dev) and parts of [shoelace](https://shoelace.style/). Synergy officially supports the latest two versions of all major browsers (as define
538 lines (532 loc) • 15.7 kB
JavaScript
import {
file_styles_default
} from "./chunk.CW2Q7KG6.js";
import {
defaultValue
} from "./chunk.3NXKLKWH.js";
import {
form_control_custom_styles_default,
form_control_styles_default
} from "./chunk.G4URZQCL.js";
import {
SynButton
} from "./chunk.YYEPGTYW.js";
import {
FormControlController
} from "./chunk.HP2LEQRU.js";
import {
animateTo
} from "./chunk.G6ITZTTW.js";
import {
getAnimation,
setDefaultAnimation
} from "./chunk.7JGKUB4A.js";
import {
SynIcon
} from "./chunk.WFJVDRQR.js";
import {
LocalizeController
} from "./chunk.OAQRCZOO.js";
import {
HasSlotController
} from "./chunk.WVVQK5TE.js";
import {
enableDefaultSettings
} from "./chunk.E5UUNP6E.js";
import {
watch
} from "./chunk.BVZQ6QSY.js";
import {
component_styles_default
} from "./chunk.NLYVOJGK.js";
import {
SynergyElement
} from "./chunk.3AZFEB6D.js";
import {
__decorateClass
} from "./chunk.Z4XV3SMG.js";
// src/components/file/file.component.ts
import { classMap } from "lit/directives/class-map.js";
import { property, query, state } from "lit/decorators.js";
import { html } from "lit";
import { ifDefined } from "lit/directives/if-defined.js";
var SynFile = class extends SynergyElement {
constructor() {
super(...arguments);
this.formControlController = new FormControlController(this, {
assumeInteractionOn: ["syn-change"],
// This makes sure the value sent in formdata events is always the files object
// @see internals/form.ts #handleFormData for more information
value: (el) => el.files
});
this.hasSlotController = new HasSlotController(this, "help-text", "label");
this.localize = new LocalizeController(this);
this.userIsDragging = false;
this.name = "";
this.defaultValue = "";
this.size = "medium";
this.label = "";
this.helpText = "";
this.disabled = false;
this.droparea = false;
this.accept = "";
this.multiple = false;
this.webkitdirectory = false;
this.form = "";
this.required = false;
this.hideValue = false;
}
set files(v) {
if (this.input) {
this.input.files = v;
}
}
get files() {
var _a;
return (_a = this.input) == null ? void 0 : _a.files;
}
set value(v) {
if (this.input) {
this.input.value = v;
}
}
get value() {
var _a;
return (_a = this.input) == null ? void 0 : _a.value;
}
/** Gets the validity state object */
get validity() {
return this.input.validity;
}
/** Gets the validation message */
get validationMessage() {
return this.input.validationMessage;
}
/**
* Checks for validity but does not show a validation message.
* Returns `true` when valid and `false` when invalid.
*/
checkValidity() {
return this.input.checkValidity();
}
/** Gets the associated form, if one exists. */
getForm() {
return this.formControlController.getForm();
}
/** Checks for validity and shows the browser's validation message if the control is invalid. */
reportValidity() {
return this.input.reportValidity();
}
/** Sets a custom validation message. Pass an empty string to restore validity. */
setCustomValidity(message) {
this.input.setCustomValidity(message);
this.formControlController.updateValidity();
}
handleDisabledChange() {
this.formControlController.setValidity(this.disabled);
}
async handleValueChange() {
await this.updateComplete;
this.formControlController.updateValidity();
}
/** Sets focus on the button or droparea. */
focus(options) {
var _a, _b;
if (this.droparea) {
(_a = this.dropareaWrapper) == null ? void 0 : _a.focus(options);
return;
}
(_b = this.button) == null ? void 0 : _b.focus(options);
}
/** Removes focus from the button or droparea. */
blur() {
var _a, _b;
if (this.droparea) {
(_a = this.dropareaWrapper) == null ? void 0 : _a.blur();
return;
}
(_b = this.button) == null ? void 0 : _b.blur();
}
handleInvalid(event) {
this.formControlController.setValidity(false);
this.formControlController.emitInvalidEvent(event);
}
handleFiles(files) {
if (!files) {
this.value = "";
return;
}
this.files = files;
}
async handleTransferItems(items) {
if (!items) {
this.value = "";
return new Promise((_resolve, reject) => {
reject(new Error("No proper items found"));
});
}
const entries = Array.from(items).map((item) => item.webkitGetAsEntry());
const filesPromises = entries.map((entry) => this.getFilesFromEntry(entry));
const filesArray = await Promise.all(filesPromises);
const files = filesArray.flat();
const dataTransfer = new DataTransfer();
Array.from(files).forEach((f) => dataTransfer.items.add(f));
return dataTransfer.files;
}
async getFilesFromEntry(entry) {
if (!entry) {
return [];
}
if (entry.isFile) {
return new Promise((resolve, reject) => {
entry.file((file) => resolve([file]), reject);
});
}
if (entry.isDirectory) {
return new Promise((resolve, reject) => {
const dirReader = entry.createReader();
dirReader.readEntries((entries) => {
Promise.all(entries.map((e) => this.getFilesFromEntry(e))).then((files) => {
resolve(files.flat());
}).catch(reject);
});
});
}
return [];
}
handleClick(e) {
e.preventDefault();
this.input.click();
}
/** Handles the change event of the native input */
handleChange(e) {
e.preventDefault();
e.stopPropagation();
this.emit("syn-input");
this.emit("syn-change");
}
handleDragOver(e) {
e.preventDefault();
e.stopPropagation();
this.userIsDragging = true;
}
handleDragLeave(e) {
e.preventDefault();
e.stopPropagation();
this.userIsDragging = false;
}
// eslint-disable-next-line complexity
async handleDrop(e) {
var _a;
e.preventDefault();
e.stopPropagation();
if (!e.dataTransfer) {
return;
}
const files = await this.handleTransferItems((_a = e.dataTransfer) == null ? void 0 : _a.items);
this.userIsDragging = false;
if (!files) {
return;
}
if (!this.multiple && !this.webkitdirectory && files.length > 1) {
this.emit("syn-error");
return;
}
const hasTrigger = this.hasSlotController.test("trigger");
if (!hasTrigger) {
const disappearAnimation = getAnimation(this.inputChosen, "file.text.disappear", { dir: this.localize.dir() });
const appearAnimation = getAnimation(this.inputChosen, "file.text.appear", { dir: this.localize.dir() });
if (this.droparea) {
const dropIconAnimation = getAnimation(this.dropareaIcon, "file.iconDrop", { dir: this.localize.dir() });
animateTo(this.dropareaIcon, dropIconAnimation.keyframes, dropIconAnimation.options);
}
await animateTo(this.inputChosen, disappearAnimation.keyframes, disappearAnimation.options);
this.handleFiles(files);
await animateTo(this.inputChosen, appearAnimation.keyframes, appearAnimation.options);
} else {
this.handleFiles(files);
}
this.input.dispatchEvent(new Event("change"));
}
/**
* Handle the focus of the droparea and emit focus event
*/
handleFocus() {
this.emit("syn-focus");
}
/**
* Handle the blur of the droparea and emit blur event
*/
handleBlur() {
this.emit("syn-blur");
}
renderValue() {
var _a;
let hasFiles = false;
let fileChosenLabel = this.localize.term("numFilesSelected", 0, this.webkitdirectory);
if (this.files && ((_a = this.files) == null ? void 0 : _a.length) > 0) {
hasFiles = true;
fileChosenLabel = this.files.length === 1 ? this.files[0].name : this.localize.term("numFilesSelected", this.files.length, this.webkitdirectory);
}
return html`
<span
class=${classMap({
input__value: true,
"input__value--hidden": this.hideValue,
"input__value--placeholder": !hasFiles
})}
part="value"
>
${fileChosenLabel}
</span>
`;
}
/* eslint-disable @typescript-eslint/unbound-method */
renderDroparea() {
return html`
<div
class="droparea"
=${this.handleClick}
=${this.handleClick}
=${this.handleFocus}
=${this.handleBlur}
tabindex=${this.disabled ? -1 : 0}
part="droparea"
>
<div
class="droparea__background"
part="droparea-background"
>
<span part="droparea-icon" class="droparea__icon">
<slot name="droparea-icon">
<syn-icon name="upload-file" library="system" ></syn-icon>
</slot>
</span>
<p
class="droparea__text"
part="droparea-value"
>
<strong>${this.localize.term(this.webkitdirectory ? "folderDragDrop" : "fileDragDrop")}</strong>
${this.renderValue()}
</p>
</div>
</div>
`;
}
/* eslint-enable @typescript-eslint/unbound-method */
/* eslint-disable @typescript-eslint/unbound-method */
renderButton() {
let buttonText = this.localize.term("fileButtonText");
if (this.multiple) {
buttonText = this.localize.term("fileButtonTextMultiple");
}
if (this.webkitdirectory) {
buttonText = this.localize.term("folderButtonText");
}
return html`
<div
class="button__wrapper"
part="button-wrapper"
>
<syn-button
class="button"
=${this.handleClick}
?disabled=${this.disabled}
exportparts="base:button__base"
part="button"
size=${this.size}
variant="outline"
>
${buttonText}
</syn-button>
${this.renderValue()}
</div>
`;
}
/* eslint-enable @typescript-eslint/unbound-method */
/* eslint-disable @typescript-eslint/unbound-method */
// eslint-disable-next-line complexity
render() {
const hasLabel = this.label || !!this.hasSlotController.test("label");
const hasHelpText = this.helpText ? true : !!this.hasSlotController.test("help-text");
const hasTrigger = !!this.hasSlotController.test("trigger");
return html`
<div
class=${classMap({
"form-control": true,
"form-control--droparea": this.droparea,
"form-control--has-help-text": hasHelpText,
"form-control--has-label": hasLabel,
"form-control--large": this.size === "large",
"form-control--medium": this.size === "medium",
"form-control--small": this.size === "small",
"form-control--user-dragging": this.userIsDragging
})}
=${this.handleDragOver}
=${this.handleDragLeave}
=${this.handleDragOver}
=${this.handleDrop}
part="form-control"
>
${hasTrigger ? html`
<slot
=${this.handleClick}
=${this.handleClick}
name="trigger"
part="trigger"
></slot>
` : html`
<label
aria-hidden=${hasLabel ? "false" : "true"}
class="form-control__label"
for="input"
part="form-control-label"
>
<slot name="label">${this.label}</slot>
</label>
<div
class="form-control-input"
part="form-control-input"
>
${this.droparea ? this.renderDroparea() : this.renderButton()}
</div>
<div
aria-hidden=${hasHelpText ? "false" : "true"}
class="form-control__help-text"
id="help-text"
part="form-control-help-text"
>
<slot name="help-text">${this.helpText}</slot>
</div>
`}
<input
accept=${this.accept}
aria-describedby="help-text"
=${this.handleChange}
class="input__control"
?disabled=${this.disabled}
id="input"
=${this.handleInvalid}
?multiple=${this.multiple}
name=${ifDefined(this.name)}
?required=${this.required}
type="file"
tabindex="-1"
?webkitdirectory=${this.webkitdirectory}
>
</div>
`;
}
/* eslint-enable @typescript-eslint/unbound-method */
};
SynFile.styles = [
component_styles_default,
form_control_styles_default,
form_control_custom_styles_default,
file_styles_default
];
SynFile.dependencies = {
"syn-button": SynButton,
"syn-icon": SynIcon
};
__decorateClass([
state()
], SynFile.prototype, "userIsDragging", 2);
__decorateClass([
property({ type: Object })
], SynFile.prototype, "files", 1);
__decorateClass([
property({ type: String })
], SynFile.prototype, "name", 2);
__decorateClass([
property({ type: String })
], SynFile.prototype, "value", 1);
__decorateClass([
defaultValue()
], SynFile.prototype, "defaultValue", 2);
__decorateClass([
property({ reflect: true })
], SynFile.prototype, "size", 2);
__decorateClass([
property()
], SynFile.prototype, "label", 2);
__decorateClass([
property({ attribute: "help-text" })
], SynFile.prototype, "helpText", 2);
__decorateClass([
property({ reflect: true, type: Boolean })
], SynFile.prototype, "disabled", 2);
__decorateClass([
property({ type: Boolean })
], SynFile.prototype, "droparea", 2);
__decorateClass([
property({ type: String })
], SynFile.prototype, "accept", 2);
__decorateClass([
property({ type: String })
], SynFile.prototype, "capture", 2);
__decorateClass([
property({ reflect: true, type: Boolean })
], SynFile.prototype, "multiple", 2);
__decorateClass([
property({ reflect: true, type: Boolean })
], SynFile.prototype, "webkitdirectory", 2);
__decorateClass([
property({ reflect: true })
], SynFile.prototype, "form", 2);
__decorateClass([
property({ reflect: true, type: Boolean })
], SynFile.prototype, "required", 2);
__decorateClass([
property({ attribute: "hide-value", type: Boolean })
], SynFile.prototype, "hideValue", 2);
__decorateClass([
query(".input__control")
], SynFile.prototype, "input", 2);
__decorateClass([
query(".button")
], SynFile.prototype, "button", 2);
__decorateClass([
query(".droparea")
], SynFile.prototype, "dropareaWrapper", 2);
__decorateClass([
query(".droparea__icon")
], SynFile.prototype, "dropareaIcon", 2);
__decorateClass([
query(".input__value")
], SynFile.prototype, "inputChosen", 2);
__decorateClass([
watch("disabled", { waitUntilFirstUpdate: true })
], SynFile.prototype, "handleDisabledChange", 1);
__decorateClass([
watch("value", { waitUntilFirstUpdate: true })
], SynFile.prototype, "handleValueChange", 1);
SynFile = __decorateClass([
enableDefaultSettings("SynFile")
], SynFile);
setDefaultAnimation("file.iconDrop", {
keyframes: [
{ scale: 1 },
{ scale: 0.7 },
{ scale: 1 }
],
options: { duration: 600, easing: "ease-out" }
});
setDefaultAnimation("file.text.disappear", {
keyframes: [
{ opacity: 1 },
{ opacity: 0, transform: "translateY(-40%)" }
],
options: { duration: 300, easing: "cubic-bezier(0.45, 1.45, 0.8, 1)" }
});
setDefaultAnimation("file.text.appear", {
keyframes: [
{ opacity: 0, transform: "translateY(40%)" },
{ opacity: 1 }
],
options: { duration: 300, easing: "cubic-bezier(0.45, 1.45, 0.8, 1)" }
});
export {
SynFile
};
//# sourceMappingURL=chunk.Z2DUHMF5.js.map