Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions content/effects/flip.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
The Flip effect lets users flip their images before uploading them. Users can choose to flip vertically or horizontally.

```ts
import { Uppload, Rotate } from "uppload";
import { Uppload, Flip } from "uppload";

const profilePicture = new Uppload();
profilePicture.use(new Rotate());
profilePicture.use(new Flip());
```

![Screenshot of the Flip effect](/assets/screenshots/flip.png)
17 changes: 17 additions & 0 deletions src/helpers/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ export const safeListen = (
listening.push({ element, type });
};

/**
* Safely remove an event listener
* @param element - HTML element to remove event listener to
* @param type - Type of event listener to remove
* @param fn - Callback function to associated listener
*/
export const safeUnlisten = (
element: Element,
type: string,
fn: EventListenerOrEventListenerObject
) => {
const listener = listening.findIndex((a) => a.element === element && a.type === type);
if (listener < 0) return;
element.removeEventListener(type, fn);
listening.splice(listener, 1);
};

const safeRequestAnimationFrame = (callback: FrameRequestCallback) => {
if (window.requestAnimationFrame)
return window.requestAnimationFrame(callback);
Expand Down
5 changes: 5 additions & 0 deletions src/helpers/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export type IElements = string | string[] | Element | Element[];

export type ITranslator = (key: string, params?: string | string[]) => string;
export interface IUppload {
id: string;
wrapper?: string;
services: UpploadService[];
effects: UpploadEffect[];
isOpen: boolean;
Expand All @@ -15,6 +17,8 @@ export interface IUppload {
}

export interface IUpploadSettings {
id?: string;
wrapper?: string;
value?: string;
bind?: IElements;
call?: IElements;
Expand All @@ -33,6 +37,7 @@ export interface IUpploadSettings {
compressor?: (file: Blob) => Promise<Blob>;
transitionDuration?: number;
disableModalClickClose?: boolean;
disableHelp?: boolean;
}

export interface IHandlersParams {
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/microlink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class MicrolinkBaseClass extends UpploadService {
exampleURL = "";
validator: (value: string) => boolean = () => true;

template = ({ translate }: IServiceTemplateParams): string => {
template = ({ translate, uppload }: IServiceTemplateParams): string => {
return `
<div class="microlink-container">
<form class="microlink-search-form">
Expand Down Expand Up @@ -49,9 +49,9 @@ export class MicrolinkBaseClass extends UpploadService {
"services.microlink.button",
translate(`services.${this.name}.title`) || this.name
)
}</button></form><button class="need-help-link"><span>${translate(
}</button></form>${!uppload.settings.disableHelp ? `<button class="need-help-link"><span>${translate(
"needHelp"
)}</span aria-hidden="true"><span>?</span></button></div>
)}</span aria-hidden="true"><span>?</span></button>` : ""}</div>
<div class="uppload-loader microlink-loader">
<div></div>
<p>${
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class SearchBaseClass<ImageResult = any> extends UpploadService {
if (loader) loader.style.display = this.loading ? "flex" : "none";
}

template = ({ translate }: IServiceTemplateParams): string => {
template = ({ translate, uppload }: IServiceTemplateParams): string => {
return `
<div class="search-container"><form class="search-search-form">
<div class="service-icon">${colorSVG(this.icon, this)}</div>
Expand All @@ -131,9 +131,9 @@ export class SearchBaseClass<ImageResult = any> extends UpploadService {
`services.${this.name}.title`
)}</a>`
)}</p></div>
<button class="need-help-link"><span>${translate(
${!uppload.settings.disableHelp ? `<button class="need-help-link"><span>${translate(
"needHelp"
)}</span aria-hidden="true"><span>?</span></button>
)}</span aria-hidden="true"><span>?</span></button>` : ""}
<div class="uppload-loader search-loader">
<div></div>
<p>${translate(
Expand Down
6 changes: 3 additions & 3 deletions src/services/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default class Camera extends UpploadService {
!/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)
);

template = ({ translate }: IServiceTemplateParams) => {
template = ({ translate, uppload }: IServiceTemplateParams) => {
return `
<div class="service-main">
<div class="camera-waiting">${translate(
Expand All @@ -49,9 +49,9 @@ export default class Camera extends UpploadService {
style="background: ${this.color}"
>${translate("services.camera.button")}</button>
</footer>
<button class="need-help-link"><span>${translate(
${!uppload.settings.disableHelp ? `<button class="need-help-link"><span>${translate(
"needHelp"
)}</span aria-hidden="true"><span>?</span></button>
)}</span aria-hidden="true"><span>?</span></button>` : ""}
`;
};

Expand Down
4 changes: 2 additions & 2 deletions src/services/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export default class Local extends UpploadService {
<div class="alternate-input">
<input type="file" accept="${this.mimeTypes.join()}"${
params.uppload.settings.multiple ? " multiple" : ""
}></div><button class="need-help-link"><span>${translate(
}></div>${!params.uppload.settings.disableHelp ? `<button class="need-help-link"><span>${translate(
"needHelp"
)}</span aria-hidden="true"><span>?</span></button>`;
)}</span aria-hidden="true"><span>?</span></button>` : ""}`;
};

handlers = (params: IHandlersParams) => {
Expand Down
66 changes: 41 additions & 25 deletions src/uppload.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { UpploadService } from "./service";
import { UpploadEffect } from "./effect";
import { setI18N, translate } from "./helpers/i18n";
import { getElements, safeListen, compressImage } from "./helpers/elements";
import { getElements, safeListen, safeUnlisten, compressImage } from "./helpers/elements";
import { colorSVG } from "./helpers/assets";
import createFocusTrap, { FocusTrap, Options } from "focus-trap";
import mitt from "mitt";
Expand Down Expand Up @@ -37,6 +37,8 @@ class UploadingService extends UpploadService {
* Uppload image uploading widget
*/
export class Uppload implements IUppload {
id: string = `${+new Date()}`;
wrapper?: string;
services: UpploadService[] = [new DefaultService(), new UploadingService()];
effects: UpploadEffect[] = [];
isOpen = false;
Expand All @@ -62,11 +64,12 @@ export class Uppload implements IUppload {
this.settings = {};
this.updateSettings(settings || {});
this.container = document.createElement("div");
this.container.setAttribute("id", `uppload-${this.id}`);
this.renderContainer();
this.container.classList.add("uppload-container");
const body = document.body;
if (body) {
body.appendChild(this.container);
const wrapper = this.wrapper ? document.querySelector(this.wrapper) : document.body;
if (wrapper) {
wrapper.appendChild(this.container);
}
this.focusTrap = createFocusTrap(this.container, {
initialFocus: () => this.container.querySelector("button"),
Expand Down Expand Up @@ -98,6 +101,8 @@ export class Uppload implements IUppload {
updateSettings(settings: IUpploadSettings) {
this.settings = { ...this.settings, ...settings };
this.emitter.emit("settingsUpdated", settings);
if (settings.id) this.id = settings.id;
if (settings.wrapper) this.wrapper = settings.wrapper;
if (settings.lang) setI18N(settings.lang);
if (settings.defaultService) this.activeService = settings.defaultService;
if (settings.lang) this.lang = settings.lang;
Expand Down Expand Up @@ -221,19 +226,24 @@ export class Uppload implements IUppload {
this.file = { blob: new Blob() };
this.activeService = "default";
this.activeEffect = "";
const serviceRadio = this.container.querySelector(
`input[type=radio][value='${this.activeService}']`
);
if (serviceRadio) serviceRadio.setAttribute("checked", "checked");
this.container.style.transition = `${this.transitionDuration}ms`;
this.container.style.opacity = "0";
this.update();
let firstService = this.settings.defaultService;
if (this.services.length === 3) this.navigate(this.services[2].name);
const firstService = this.settings.defaultService;
if (firstService) this.navigate(firstService);
safeListen(document.body, "keyup", (e) => {
if ((e as KeyboardEvent).key === "Escape" && this.open) this.close();
});
else if (this.services.length === 3) this.navigate(this.services[2].name);
const serviceRadio = this.container.querySelector(
`input[type=radio][value='${this.activeService}']`
) as HTMLInputElement;
if (serviceRadio) {
serviceRadio.setAttribute("checked", "checked");
serviceRadio.checked = true;
}
const escape = (e: KeyboardEvent) => {
if (e.key === "Escape" && this.isOpen) this.close();
};
safeUnlisten(document.body, "keyup", escape);
safeListen(document.body, "keyup", escape);
setTimeout(() => {
this.container.style.opacity = "1";
}, 1);
Expand Down Expand Up @@ -341,12 +351,12 @@ export class Uppload implements IUppload {
}" class="uppload-service-name">
${
sidebar
? `<input type="radio" id="uppload-service-radio-${service.name}" value="${service.name}" name="uppload-radio">`
? `<input type="radio" id="uppload-service-radio-${service.name}-${this.id}" value="${service.name}" name="uppload-radio" ${service.name === this.activeService ? `checked="checked"` : ""}>`
: ""
}
<${
sidebar
? `label for="uppload-service-radio-${service.name}"`
? `label for="uppload-service-radio-${service.name}-${this.id}"`
: "button"
} data-uppload-service="${service.name}">
${
Expand Down Expand Up @@ -378,10 +388,10 @@ export class Uppload implements IUppload {
${this.effects
.map(
(effect) => `
<input type="radio" id="uppload-effect-radio-${effect.name}" value="${
<input type="radio" id="uppload-effect-radio-${effect.name}-${this.id}" value="${
effect.name
}" name="uppload-effect-radio">
<label for="uppload-effect-radio-${effect.name}">
<label for="uppload-effect-radio-${effect.name}-${this.id}">
${
effect.icon.indexOf("http") === 0
? `<img class="effect-icon" alt="" src="${effect.icon}">`
Expand Down Expand Up @@ -419,6 +429,7 @@ export class Uppload implements IUppload {
<div class="uppload-active-container"></div>
<footer style="display: none" class="effects-nav">${this.getEffectsNavbar()}</footer>
</section>
${!this.settings.disableHelp ? `
<div class="uppload-help-loading">
<div class="uppload-loader">
<div></div>
Expand All @@ -430,7 +441,7 @@ export class Uppload implements IUppload {
"help.close"
)}</span><span aria-hidden="true">&times;</span></button></div>
<iframe></iframe>
</div>
</div>` : ""}
</div>
<div class="uppload-modal-bg">
<button class="uppload-close" aria-label="${translate(
Expand Down Expand Up @@ -621,8 +632,11 @@ export class Uppload implements IUppload {
// Set active state to current effect
const activeRadio = this.container.querySelector(
`input[name='uppload-effect-radio'][value='${this.activeEffect}']`
);
if (activeRadio) activeRadio.setAttribute("checked", "checked");
) as HTMLInputElement;
if (activeRadio) {
activeRadio.setAttribute("checked", "checked");
activeRadio.checked = true;
}
}

compress(file: Blob) {
Expand Down Expand Up @@ -713,6 +727,13 @@ export class Uppload implements IUppload {
defaultServiceLinks.forEach((link) => {
const linkFunction = (e: Event) => {
const service = link.getAttribute("data-uppload-service");
const serviceRadio = this.container.querySelector(
`input[type=radio][value='${service}']`
) as HTMLInputElement;
if (serviceRadio) {
serviceRadio.setAttribute("checked", "checked");
serviceRadio.checked = true;
}
if (service) {
this.navigate(service);
const serviceDiv = this.container.querySelector(
Expand All @@ -732,10 +753,6 @@ export class Uppload implements IUppload {
} catch (error) {}
}
}
const serviceRadio = this.container.querySelector(
`input[type=radio][value='${service}']`
);
if (serviceRadio) serviceRadio.setAttribute("checked", "checked");
e.preventDefault();
return false;
};
Expand Down Expand Up @@ -809,7 +826,6 @@ export class Uppload implements IUppload {
if (cancelButton)
safeListen(cancelButton, "click", () => {
this.file = { blob: new Blob() };
this.activeService = "default";
this.activeEffect = "";
this.update();
});
Expand Down