mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-25 21:16:47 +00:00
Update, rebuild, and add a new API endpoint.
This commit is contained in:
42
resources/assets/v2/libraries/dark-editable/Modes/BaseMode.js
Executable file
42
resources/assets/v2/libraries/dark-editable/Modes/BaseMode.js
Executable file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* BaseMode.js
|
||||
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
|
||||
*
|
||||
* License: MIT
|
||||
*
|
||||
* Copied and slightly edited by James Cole <james@firefly-iii.org>
|
||||
*/
|
||||
export default class BaseMode{
|
||||
constructor(context) {
|
||||
if(this.constructor === BaseMode){
|
||||
throw new Error(`It's abstract class`);
|
||||
}
|
||||
this.context = context;
|
||||
}
|
||||
event_show(){
|
||||
this.context.typeElement.hideError();
|
||||
this.context.typeElement.element.value = this.context.value;
|
||||
this.context.element.dispatchEvent(new CustomEvent("show"));
|
||||
}
|
||||
event_shown(){
|
||||
this.context.element.dispatchEvent(new CustomEvent("shown"));
|
||||
}
|
||||
event_hide(){
|
||||
this.context.element.dispatchEvent(new CustomEvent("hide"));
|
||||
}
|
||||
event_hidden(){
|
||||
this.context.element.dispatchEvent(new CustomEvent("hidden"));
|
||||
}
|
||||
init(){
|
||||
throw new Error('Method `init` not define!');
|
||||
}
|
||||
enable(){
|
||||
throw new Error('Method `enable` not define!');
|
||||
}
|
||||
disable(){
|
||||
throw new Error('Method `disable` not define!');
|
||||
}
|
||||
hide(){
|
||||
throw new Error('Method `hide` not define!');
|
||||
}
|
||||
}
|
||||
40
resources/assets/v2/libraries/dark-editable/Modes/InlineMode.js
Executable file
40
resources/assets/v2/libraries/dark-editable/Modes/InlineMode.js
Executable file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* InlineMode.js
|
||||
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
|
||||
*
|
||||
* License: MIT
|
||||
*
|
||||
* Copied and slightly edited by James Cole <james@firefly-iii.org>
|
||||
*/
|
||||
|
||||
import BaseMode from "./BaseMode.js";
|
||||
|
||||
export default class InlineMode extends BaseMode{
|
||||
init(){
|
||||
const open = () => {
|
||||
if(!this.context.disabled){
|
||||
const item = this.context.typeElement.create();
|
||||
this.event_show();
|
||||
this.context.element.removeEventListener('click', open);
|
||||
this.context.element.innerHTML = '';
|
||||
this.context.element.append(item);
|
||||
this.event_shown();
|
||||
}
|
||||
}
|
||||
this.context.element.addEventListener('click', open);
|
||||
}
|
||||
enable(){
|
||||
|
||||
}
|
||||
disable(){
|
||||
|
||||
}
|
||||
hide(){
|
||||
this.event_hide();
|
||||
this.context.element.innerHTML = this.context.value;
|
||||
setTimeout(() => {
|
||||
this.init();
|
||||
this.event_hidden();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
53
resources/assets/v2/libraries/dark-editable/Modes/PopupMode.js
Executable file
53
resources/assets/v2/libraries/dark-editable/Modes/PopupMode.js
Executable file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* PopupMode.js
|
||||
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
|
||||
*
|
||||
* License: MIT
|
||||
*
|
||||
* Copied and slightly edited by James Cole <james@firefly-iii.org>
|
||||
*/
|
||||
|
||||
import BaseMode from "./BaseMode.js";
|
||||
|
||||
export default class PopupMode extends BaseMode{
|
||||
init(){
|
||||
this.popover = new bootstrap.Popover(this.context.element, {
|
||||
container: "body",
|
||||
content: this.context.typeElement.create(),
|
||||
html: true,
|
||||
customClass: "dark-editable",
|
||||
title: this.context.title,
|
||||
});
|
||||
this.context.element.addEventListener('show.bs.popover', () => {
|
||||
this.event_show();
|
||||
});
|
||||
this.context.element.addEventListener('shown.bs.popover', () => {
|
||||
this.event_shown();
|
||||
});
|
||||
this.context.element.addEventListener('hide.bs.popover', () => {
|
||||
this.event_hide();
|
||||
});
|
||||
this.context.element.addEventListener('hidden.bs.popover', () => {
|
||||
this.event_hidden();
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
const target = e.target;
|
||||
if(target === this.popover.tip || target === this.context.element) return;
|
||||
let current = target;
|
||||
while(current = current.parentNode){
|
||||
if(current === this.popover.tip) return;
|
||||
}
|
||||
this.hide();
|
||||
})
|
||||
}
|
||||
enable(){
|
||||
this.popover.enable();
|
||||
}
|
||||
disable(){
|
||||
this.popover.disable();
|
||||
}
|
||||
hide(){
|
||||
this.popover.hide();
|
||||
}
|
||||
}
|
||||
255
resources/assets/v2/libraries/dark-editable/Types/BaseType.js
Executable file
255
resources/assets/v2/libraries/dark-editable/Types/BaseType.js
Executable file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* BaseMode.js
|
||||
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
|
||||
*
|
||||
* License: MIT
|
||||
*
|
||||
* Copied and slightly edited by James Cole <james@firefly-iii.org>
|
||||
*/
|
||||
|
||||
export default class BaseType {
|
||||
context = null;
|
||||
element = null;
|
||||
error = null;
|
||||
form = null;
|
||||
load = null;
|
||||
buttonGroup = null;
|
||||
buttons = {success: null, cancel: null};
|
||||
|
||||
constructor(context) {
|
||||
if (this.constructor === BaseType) {
|
||||
throw new Error(`It's abstract class`);
|
||||
}
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
create() {
|
||||
throw new Error('Method `create` not define!');
|
||||
}
|
||||
|
||||
createContainer(element) {
|
||||
const div = document.createElement(`div`);
|
||||
|
||||
// original list of elements:
|
||||
this.element = element;
|
||||
this.error = this.createContainerError();
|
||||
this.form = this.createContainerForm();
|
||||
this.load = this.createContainerLoad();
|
||||
this.buttons.success = this.createButtonSuccess();
|
||||
this.buttons.cancel = this.createButtonCancel();
|
||||
|
||||
// create first div, with label and input:
|
||||
const topDiv = document.createElement(`div`);
|
||||
topDiv.classList.add("col-12");
|
||||
|
||||
// create label:
|
||||
const label = document.createElement(`label`);
|
||||
label.classList.add("visually-hidden");
|
||||
label.for = element.id;
|
||||
|
||||
// add label + input to top div:
|
||||
topDiv.append(label, element);
|
||||
|
||||
// create second div, with button group:
|
||||
const bottomDiv = document.createElement(`div`);
|
||||
bottomDiv.classList.add("col-12");
|
||||
|
||||
// create button group:
|
||||
this.buttonGroup = this.createButtonGroup();
|
||||
|
||||
// append buttons to button group:
|
||||
this.buttonGroup.append(this.buttons.success, this.buttons.cancel);
|
||||
bottomDiv.append(this.buttonGroup);
|
||||
|
||||
// append bottom and top div to form:
|
||||
this.form.append(topDiv, bottomDiv);
|
||||
|
||||
//this.form.append(element, this.load, this.buttons.success, this.buttons.cancel);
|
||||
//this.form.append(element, this.load, this.buttonGroup);
|
||||
div.append(this.error, this.form);
|
||||
return div;
|
||||
}
|
||||
|
||||
createButtonGroup() {
|
||||
const div = document.createElement(`div`);
|
||||
div.classList.add("btn-group", "btn-group-sm");
|
||||
return div;
|
||||
}
|
||||
|
||||
createContainerError() {
|
||||
const div = document.createElement(`div`);
|
||||
div.classList.add("text-danger", "fst-italic", "mb-2", "fw-bold");
|
||||
div.style.display = "none";
|
||||
return div;
|
||||
}
|
||||
|
||||
createContainerForm() {
|
||||
|
||||
|
||||
const form = document.createElement(`form`);
|
||||
form.classList.add("row", "row-cols-lg-auto", "g-3", "align-items-center");
|
||||
//form.style.gap = "20px";
|
||||
form.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
const newValue = this.getValue();
|
||||
if (this.context.send && this.context.pk && this.context.url && (this.context.value !== newValue)) {
|
||||
this.showLoad();
|
||||
let msg;
|
||||
try {
|
||||
const response = await this.ajax(newValue);
|
||||
if (response.ok) {
|
||||
msg = await this.context.success(response, newValue);
|
||||
} else {
|
||||
msg = await this.context.error(response, newValue) || `${response.status} ${response.statusText}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
msg = error;
|
||||
}
|
||||
|
||||
if (msg) {
|
||||
this.setError(msg);
|
||||
this.showError();
|
||||
} else {
|
||||
this.setError(null);
|
||||
this.hideError();
|
||||
this.context.value = this.getValue();
|
||||
this.context.modeElement.hide();
|
||||
this.initText();
|
||||
}
|
||||
this.hideLoad();
|
||||
} else {
|
||||
this.context.value = this.getValue();
|
||||
this.context.modeElement.hide();
|
||||
this.initText();
|
||||
}
|
||||
this.context.element.dispatchEvent(new CustomEvent("save"));
|
||||
})
|
||||
return form;
|
||||
}
|
||||
|
||||
createContainerLoad() {
|
||||
const div = document.createElement(`div`);
|
||||
div.style.display = "none";
|
||||
div.style.position = "absolute";
|
||||
div.style.background = "white";
|
||||
div.style.width = "100%";
|
||||
div.style.height = "100%";
|
||||
div.style.top = 0;
|
||||
div.style.left = 0;
|
||||
const loader = document.createElement(`div`);
|
||||
loader.classList.add("dark-editable-loader");
|
||||
div.append(loader);
|
||||
return div;
|
||||
}
|
||||
|
||||
createButton() {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.classList.add("btn", "btn-sm");
|
||||
button.style.color = "transparent";
|
||||
button.style.textShadow = "0 0 0 white";
|
||||
return button;
|
||||
}
|
||||
|
||||
createButtonSuccess() {
|
||||
const btn_success = this.createButton();
|
||||
btn_success.type = "submit";
|
||||
btn_success.classList.add("btn-success");
|
||||
btn_success.innerHTML = "✔";
|
||||
return btn_success;
|
||||
}
|
||||
|
||||
createButtonCancel() {
|
||||
const btn_cancel = this.createButton();
|
||||
btn_cancel.classList.add("btn-danger");
|
||||
const div = document.createElement("div");
|
||||
div.innerHTML = "✖";
|
||||
btn_cancel.append(div);
|
||||
btn_cancel.addEventListener("click", () => {
|
||||
this.context.modeElement.hide();
|
||||
});
|
||||
return btn_cancel;
|
||||
}
|
||||
|
||||
hideLoad() {
|
||||
this.load.style.display = "none";
|
||||
}
|
||||
|
||||
showLoad() {
|
||||
this.load.style.display = "block";
|
||||
}
|
||||
|
||||
ajax(new_value) {
|
||||
let url = this.context.url;
|
||||
const form = new FormData;
|
||||
form.append("pk", this.context.pk);
|
||||
form.append("name", this.context.name);
|
||||
form.append("value", new_value);
|
||||
const option = {};
|
||||
option.method = this.context.ajaxOptions.method;
|
||||
if (option.method === "POST") {
|
||||
option.body = form;
|
||||
} else {
|
||||
url += "?" + new URLSearchParams(form).toString();
|
||||
}
|
||||
return fetch(url, option);
|
||||
}
|
||||
|
||||
async successResponse(response, newValue) {
|
||||
|
||||
}
|
||||
|
||||
async errorResponse(response, newValue) {
|
||||
|
||||
}
|
||||
|
||||
setError(errorMsg) {
|
||||
this.error.innerHTML = errorMsg;
|
||||
}
|
||||
|
||||
showError() {
|
||||
this.error.style.display = "block";
|
||||
}
|
||||
|
||||
hideError() {
|
||||
if (this.error) {
|
||||
this.error.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
createElement(name) {
|
||||
const element = document.createElement(name);
|
||||
console.log(element);
|
||||
element.classList.add("form-control");
|
||||
if (this.context.required) {
|
||||
element.required = this.context.required;
|
||||
}
|
||||
this.add_focus(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
add_focus(element) {
|
||||
this.context.element.addEventListener('shown', function () {
|
||||
element.focus();
|
||||
});
|
||||
}
|
||||
|
||||
initText() {
|
||||
if (this.context.value === "") {
|
||||
this.context.element.innerHTML = this.context.emptytext;
|
||||
return true;
|
||||
} else {
|
||||
this.context.element.innerHTML = this.context.value;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
initOptions() {
|
||||
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.element.value;
|
||||
}
|
||||
}
|
||||
16
resources/assets/v2/libraries/dark-editable/Types/DateTimeType.js
Executable file
16
resources/assets/v2/libraries/dark-editable/Types/DateTimeType.js
Executable file
@@ -0,0 +1,16 @@
|
||||
import DateType from "./DateType.js";
|
||||
|
||||
export default class DateTimeType extends DateType{
|
||||
create(){
|
||||
const input = this.createElement(`input`);
|
||||
input.type = "datetime-local";
|
||||
|
||||
return this.createContainer(input);
|
||||
}
|
||||
|
||||
initOptions(){
|
||||
this.context.get_opt("format", "YYYY-MM-DD HH:mm");
|
||||
this.context.get_opt("viewformat", "YYYY-MM-DD HH:mm");
|
||||
this.context.value = moment(this.context.value).format("YYYY-MM-DDTHH:mm");
|
||||
}
|
||||
}
|
||||
25
resources/assets/v2/libraries/dark-editable/Types/DateType.js
Executable file
25
resources/assets/v2/libraries/dark-editable/Types/DateType.js
Executable file
@@ -0,0 +1,25 @@
|
||||
import BaseType from "./BaseType.js";
|
||||
|
||||
export default class DateType extends BaseType{
|
||||
create(){
|
||||
const input = this.createElement(`input`);
|
||||
input.type = "date";
|
||||
|
||||
return this.createContainer(input);
|
||||
}
|
||||
|
||||
initText(){
|
||||
if(this.value === ""){
|
||||
this.context.element.innerHTML = this.context.emptytext;
|
||||
return true;
|
||||
} else {
|
||||
this.context.element.innerHTML = moment(this.context.value).format(this.context.viewformat);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
initOptions(){
|
||||
this.context.get_opt("format", "YYYY-MM-DD");
|
||||
this.context.get_opt("viewformat", "YYYY-MM-DD");
|
||||
}
|
||||
}
|
||||
19
resources/assets/v2/libraries/dark-editable/Types/InputType.js
Executable file
19
resources/assets/v2/libraries/dark-editable/Types/InputType.js
Executable file
@@ -0,0 +1,19 @@
|
||||
import BaseType from "./BaseType.js";
|
||||
|
||||
export default class InputType extends BaseType{
|
||||
create(){
|
||||
// expand input element with necessary classes and things.
|
||||
// <input type="text" class="form-control form-control-md" id="inlineFormInputGroupUsername" placeholder="Username">
|
||||
|
||||
|
||||
const input = this.createElement(`input`);
|
||||
const id = this.context.element.id + '_input';
|
||||
input.type = this.context.type;
|
||||
input.id = id;
|
||||
input.autocomplete = 'off';
|
||||
input.placeholder = this.context.element.innerText;
|
||||
input.classList.add("form-control", "form-control-md");
|
||||
|
||||
return this.createContainer(input);
|
||||
}
|
||||
}
|
||||
36
resources/assets/v2/libraries/dark-editable/Types/SelectType.js
Executable file
36
resources/assets/v2/libraries/dark-editable/Types/SelectType.js
Executable file
@@ -0,0 +1,36 @@
|
||||
import BaseType from "./BaseType.js";
|
||||
|
||||
export default class SelectType extends BaseType{
|
||||
create(){
|
||||
const select = this.createElement(`select`);
|
||||
this.context.source.forEach(item => {
|
||||
const opt = document.createElement(`option`);
|
||||
opt.value = item.value;
|
||||
opt.innerHTML = item.text;
|
||||
select.append(opt);
|
||||
});
|
||||
|
||||
return this.createContainer(select);
|
||||
}
|
||||
|
||||
initText(){
|
||||
this.context.element.innerHTML = this.context.emptytext;
|
||||
if(this.context.value !== "" && this.context.source.length > 0){
|
||||
for(const key in this.context.source){
|
||||
const item = this.context.source[ key ];
|
||||
if(item.value == this.context.value){
|
||||
this.context.element.innerHTML = item.text;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
initOptions(){
|
||||
this.context.get_opt("source", []);
|
||||
if(typeof this.context.source === "string" && this.context.source !== ""){
|
||||
this.context.source = JSON.parse(this.context.source);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
resources/assets/v2/libraries/dark-editable/Types/TextAreaType.js
Executable file
9
resources/assets/v2/libraries/dark-editable/Types/TextAreaType.js
Executable file
@@ -0,0 +1,9 @@
|
||||
import BaseType from "./BaseType.js";
|
||||
|
||||
export default class TextAreaType extends BaseType{
|
||||
create(){
|
||||
const textarea = this.createElement(`textarea`);
|
||||
|
||||
return this.createContainer(textarea);
|
||||
}
|
||||
}
|
||||
87
resources/assets/v2/libraries/dark-editable/dark-editable.css
Executable file
87
resources/assets/v2/libraries/dark-editable/dark-editable.css
Executable file
@@ -0,0 +1,87 @@
|
||||
.dark-editable-element{
|
||||
border-bottom: dashed 1px #0088cc;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dark-editable-element-disabled{
|
||||
border-bottom: none;
|
||||
cursor: default;
|
||||
}
|
||||
.dark-editable-element-empty{
|
||||
font-style: italic;
|
||||
color: #DD1144;
|
||||
}
|
||||
.dark-editable{
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.dark-editable-loader {
|
||||
font-size: 5px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
text-indent: -9999em;
|
||||
-webkit-animation: load5 1.1s infinite ease;
|
||||
animation: load5 1.1s infinite ease;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
}
|
||||
@-webkit-keyframes load5 {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0em -2.6em 0em 0em #000000, 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.5), -1.8em -1.8em 0 0em rgba(0,0,0, 0.7);
|
||||
}
|
||||
12.5% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.7), 1.8em -1.8em 0 0em #000000, 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.5);
|
||||
}
|
||||
25% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.5), 1.8em -1.8em 0 0em rgba(0,0,0, 0.7), 2.5em 0em 0 0em #000000, 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
37.5% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.5), 2.5em 0em 0 0em rgba(0,0,0, 0.7), 1.75em 1.75em 0 0em #000000, 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.5), 1.75em 1.75em 0 0em rgba(0,0,0, 0.7), 0em 2.5em 0 0em #000000, -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
62.5% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.5), 0em 2.5em 0 0em rgba(0,0,0, 0.7), -1.8em 1.8em 0 0em #000000, -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
75% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.5), -1.8em 1.8em 0 0em rgba(0,0,0, 0.7), -2.6em 0em 0 0em #000000, -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
87.5% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.5), -2.6em 0em 0 0em rgba(0,0,0, 0.7), -1.8em -1.8em 0 0em #000000;
|
||||
}
|
||||
}
|
||||
@keyframes load5 {
|
||||
0%,
|
||||
100% {
|
||||
box-shadow: 0em -2.6em 0em 0em #000000, 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.5), -1.8em -1.8em 0 0em rgba(0,0,0, 0.7);
|
||||
}
|
||||
12.5% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.7), 1.8em -1.8em 0 0em #000000, 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.5);
|
||||
}
|
||||
25% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.5), 1.8em -1.8em 0 0em rgba(0,0,0, 0.7), 2.5em 0em 0 0em #000000, 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
37.5% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.5), 2.5em 0em 0 0em rgba(0,0,0, 0.7), 1.75em 1.75em 0 0em #000000, 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.5), 1.75em 1.75em 0 0em rgba(0,0,0, 0.7), 0em 2.5em 0 0em #000000, -1.8em 1.8em 0 0em rgba(0,0,0, 0.2), -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
62.5% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.5), 0em 2.5em 0 0em rgba(0,0,0, 0.7), -1.8em 1.8em 0 0em #000000, -2.6em 0em 0 0em rgba(0,0,0, 0.2), -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
75% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.5), -1.8em 1.8em 0 0em rgba(0,0,0, 0.7), -2.6em 0em 0 0em #000000, -1.8em -1.8em 0 0em rgba(0,0,0, 0.2);
|
||||
}
|
||||
87.5% {
|
||||
box-shadow: 0em -2.6em 0em 0em rgba(0,0,0, 0.2), 1.8em -1.8em 0 0em rgba(0,0,0, 0.2), 2.5em 0em 0 0em rgba(0,0,0, 0.2), 1.75em 1.75em 0 0em rgba(0,0,0, 0.2), 0em 2.5em 0 0em rgba(0,0,0, 0.2), -1.8em 1.8em 0 0em rgba(0,0,0, 0.5), -2.6em 0em 0 0em rgba(0,0,0, 0.7), -1.8em -1.8em 0 0em #000000;
|
||||
}
|
||||
}
|
||||
|
||||
181
resources/assets/v2/libraries/dark-editable/dark-editable.js
Normal file
181
resources/assets/v2/libraries/dark-editable/dark-editable.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* dark-editable.js
|
||||
* Copyright (c) 2024 https://github.com/DarKsandr/dark-editable
|
||||
*
|
||||
* License: MIT
|
||||
*
|
||||
* Copied and slightly edited by James Cole <james@firefly-iii.org>
|
||||
*/
|
||||
|
||||
import "./dark-editable.css";
|
||||
import PopupMode from "./Modes/PopupMode.js";
|
||||
import InlineMode from "./Modes/InlineMode.js";
|
||||
import BaseType from "./Types/BaseType.js";
|
||||
import InputType from "./Types/InputType.js";
|
||||
import TextAreaType from "./Types/TextAreaType.js";
|
||||
import SelectType from "./Types/SelectType.js";
|
||||
import DateType from "./Types/DateType.js";
|
||||
import DateTimeType from "./Types/DateTimeType.js";
|
||||
|
||||
export default class DarkEditable{
|
||||
modeElement = null;
|
||||
typeElement = null;
|
||||
mode = null;
|
||||
type = null;
|
||||
emptytext = null;
|
||||
viewformat = null;
|
||||
pk = null;
|
||||
name = null;
|
||||
|
||||
constructor(element, options = {}){
|
||||
this.element = element;
|
||||
this.options = options;
|
||||
|
||||
this.init_options();
|
||||
this.typeElement = this.route_type();
|
||||
this.typeElement.initOptions();
|
||||
this.modeElement = this.route_mode();
|
||||
this.modeElement.init();
|
||||
this.init_text();
|
||||
this.init_style();
|
||||
if(this.disabled){
|
||||
this.disable();
|
||||
}
|
||||
this.element.dispatchEvent(new CustomEvent("init"));
|
||||
}
|
||||
|
||||
/* INIT METHODS */
|
||||
|
||||
get_opt(name, default_value){
|
||||
return this[ name ] = this.element.dataset?.[ name ] ?? this.options?.[ name ] ?? default_value;
|
||||
}
|
||||
get_opt_bool(name, default_value){
|
||||
this.get_opt(name, default_value);
|
||||
if(typeof this[ name ] !== "boolean"){
|
||||
if(this[ name ] === "true") {
|
||||
this[ name ] = true;
|
||||
} else if(this[ name ] === "false") {
|
||||
this[ name ] = false;
|
||||
} else {
|
||||
this[ name ] = default_value;
|
||||
}
|
||||
}
|
||||
return this[ name ];
|
||||
}
|
||||
|
||||
init_options(){
|
||||
//priority date elements
|
||||
this.get_opt("value", this.element.innerHTML);
|
||||
this.get_opt("name", this.element.id);
|
||||
this.get_opt("pk", null);
|
||||
this.get_opt("title", "");
|
||||
this.get_opt("type", "text");
|
||||
this.get_opt("emptytext", "Empty");
|
||||
this.get_opt("mode", "popup");
|
||||
this.get_opt("url", null);
|
||||
this.get_opt("ajaxOptions", {});
|
||||
this.ajaxOptions = Object.assign({
|
||||
method: "POST",
|
||||
dataType: "text",
|
||||
}, this.ajaxOptions);
|
||||
this.get_opt_bool("send", true);
|
||||
this.get_opt_bool("disabled", false);
|
||||
this.get_opt_bool("required", false);
|
||||
if(this.options?.success && typeof this.options?.success == "function"){
|
||||
this.success = this.options.success;
|
||||
}
|
||||
if(this.options?.error && typeof this.options?.error == "function"){
|
||||
this.error = this.options.error;
|
||||
}
|
||||
}
|
||||
|
||||
init_text(){
|
||||
const empty_class = "dark-editable-element-empty";
|
||||
this.element.classList.remove(empty_class);
|
||||
if(this.typeElement.initText()){
|
||||
this.element.classList.add(empty_class);
|
||||
}
|
||||
}
|
||||
|
||||
init_style(){
|
||||
this.element.classList.add("dark-editable-element");
|
||||
}
|
||||
|
||||
/* INIT METHODS END */
|
||||
route_mode(){
|
||||
switch (this.mode){
|
||||
default:
|
||||
throw new Error(`Mode ${this.mode} not found!`)
|
||||
case 'popup':
|
||||
return new PopupMode(this);
|
||||
case 'inline':
|
||||
return new InlineMode(this);
|
||||
}
|
||||
}
|
||||
|
||||
route_type(){
|
||||
if(this.type.prototype instanceof BaseType){
|
||||
return new this.type(this);
|
||||
}
|
||||
if(typeof this.type === 'string'){
|
||||
switch(this.type){
|
||||
case "text":
|
||||
case "password":
|
||||
case "email":
|
||||
case "url":
|
||||
case "tel":
|
||||
case "number":
|
||||
case "range":
|
||||
case "time":
|
||||
return new InputType(this);
|
||||
case "textarea":
|
||||
return new TextAreaType(this);
|
||||
case "select":
|
||||
return new SelectType(this);
|
||||
case "date":
|
||||
return new DateType(this);
|
||||
case "datetime":
|
||||
return new DateTimeType(this);
|
||||
}
|
||||
}
|
||||
throw new Error(`Undefined type`);
|
||||
}
|
||||
|
||||
/* AJAX */
|
||||
|
||||
async success(response, newValue){
|
||||
return await this.typeElement.successResponse(response, newValue);
|
||||
}
|
||||
|
||||
async error(response, newValue){
|
||||
return await this.typeElement.errorResponse(response, newValue);
|
||||
}
|
||||
|
||||
/* AJAX END */
|
||||
|
||||
/* METHODS */
|
||||
|
||||
enable(){
|
||||
this.disabled = false;
|
||||
this.element.classList.remove("dark-editable-element-disabled");
|
||||
this.modeElement.enable();
|
||||
|
||||
}
|
||||
|
||||
disable(){
|
||||
this.disabled = true;
|
||||
this.element.classList.add("dark-editable-element-disabled");
|
||||
this.modeElement.enable();
|
||||
}
|
||||
|
||||
setValue(value){
|
||||
this.value = value;
|
||||
this.init_text();
|
||||
}
|
||||
|
||||
getValue(){
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/* METHODS END */
|
||||
}
|
||||
@@ -349,7 +349,7 @@ let transactions = function () {
|
||||
this.detectTransactionType();
|
||||
|
||||
// parse transaction:
|
||||
let transactions = parseFromEntries(this.entries, this.groupProperties.transactionType);
|
||||
let transactions = parseFromEntries(this.entries, null, this.groupProperties.transactionType);
|
||||
let submission = {
|
||||
group_title: this.groupProperties.title,
|
||||
fire_webhooks: this.formStates.webhooksButton,
|
||||
@@ -466,7 +466,7 @@ let transactions = function () {
|
||||
// onRenderItem: renderAccount,
|
||||
onChange: changeSourceAccount,
|
||||
onSelectItem: selectSourceAccount,
|
||||
hiddenValue: this.items[count].source_account.alpine_name,
|
||||
hiddenValue: this.entries[count].source_account.alpine_name
|
||||
});
|
||||
addAutocomplete({
|
||||
selector: 'input.ac-dest',
|
||||
|
||||
@@ -58,6 +58,7 @@ let transactions = function () {
|
||||
return {
|
||||
// transactions are stored in "entries":
|
||||
entries: [],
|
||||
originals: [],
|
||||
|
||||
// state of the form is stored in formState:
|
||||
formStates: {
|
||||
@@ -126,7 +127,7 @@ let transactions = function () {
|
||||
this.formStates.isSubmitting = true;
|
||||
|
||||
// parse transaction:
|
||||
let transactions = parseFromEntries(this.entries, this.groupProperties.transactionType);
|
||||
let transactions = parseFromEntries(this.entries, this.originals, this.groupProperties.transactionType);
|
||||
let submission = {
|
||||
group_title: this.groupProperties.title,
|
||||
fire_webhooks: this.formStates.webhooksButton,
|
||||
@@ -263,7 +264,7 @@ let transactions = function () {
|
||||
getter.show(groupId, {}).then((response) => {
|
||||
const data = response.data.data;
|
||||
this.groupProperties.id = parseInt(data.id);
|
||||
this.groupProperties.transactionType = data.attributes.transactions[0].type;
|
||||
this.groupProperties.transactionType = data.attributes.transactions[0].type.toLowerCase();
|
||||
this.groupProperties.title = data.attributes.title ?? data.attributes.transactions[0].description;
|
||||
this.entries = parseDownloadedSplits(data.attributes.transactions);
|
||||
|
||||
|
||||
@@ -43,12 +43,14 @@ function addPointToMap(e) {
|
||||
markers[index].on('dragend', dragEnd);
|
||||
markers[index].addTo(maps[index]);
|
||||
|
||||
const setEvent = new CustomEvent('location-set', {detail: {
|
||||
const setEvent = new CustomEvent('location-set', {
|
||||
detail: {
|
||||
latitude: e.latlng.lat,
|
||||
longitude: e.latlng.lng,
|
||||
index: index,
|
||||
zoomLevel: maps[index].getZoom()
|
||||
}});
|
||||
}
|
||||
});
|
||||
document.dispatchEvent(setEvent);
|
||||
}
|
||||
}
|
||||
@@ -56,10 +58,12 @@ function addPointToMap(e) {
|
||||
function saveZoomOfMap(e) {
|
||||
//let index = parseInt(e.sourceTarget._container.attributes['data-index'].value);
|
||||
let index = 0;
|
||||
const zoomEvent = new CustomEvent('location-zoom', {detail: {
|
||||
const zoomEvent = new CustomEvent('location-zoom', {
|
||||
detail: {
|
||||
index: index,
|
||||
zoomLevel: maps[index].getZoom()
|
||||
}});
|
||||
}
|
||||
});
|
||||
document.dispatchEvent(zoomEvent);
|
||||
}
|
||||
|
||||
@@ -87,13 +91,15 @@ export function addLocation(index) {
|
||||
|
||||
//let holder = document.getElementById('location_map_' + index);
|
||||
let holder = document.getElementById('location_map');
|
||||
maps[index] = L.map(holder).setView([holder.dataset.latitude, holder.dataset.longitude], holder.dataset.zoomLevel);
|
||||
if (holder) {
|
||||
maps[index] = L.map(holder).setView([holder.dataset.latitude, holder.dataset.longitude], holder.dataset.zoomLevel);
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
}).addTo(maps[index]);
|
||||
maps[index].on('click', addPointToMap);
|
||||
maps[index].on('zoomend', saveZoomOfMap);
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
}).addTo(maps[index]);
|
||||
maps[index].on('click', addPointToMap);
|
||||
maps[index].on('zoomend', saveZoomOfMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,23 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export function parseFromEntries(entries, transactionType) {
|
||||
export function parseFromEntries(entries, originals, transactionType) {
|
||||
let returnArray = [];
|
||||
for (let i in entries) {
|
||||
if (entries.hasOwnProperty(i)) {
|
||||
const entry = entries[i];
|
||||
let compare = false;
|
||||
let original = {};
|
||||
if (originals !== null && originals.hasOwnProperty(i)) {
|
||||
compare = true;
|
||||
let original = originals[i];
|
||||
}
|
||||
let current = {};
|
||||
|
||||
// fields for transaction
|
||||
current.description = entry.description;
|
||||
if ((compare && original.description !== entry.description) || !compare) {
|
||||
current.description = entry.description;
|
||||
}
|
||||
|
||||
// source and destination
|
||||
current.source_name = entry.source_account.name;
|
||||
|
||||
@@ -25,6 +25,8 @@ import Get from "../../api/v2/model/transaction/get.js";
|
||||
import {parseDownloadedSplits} from "./shared/parse-downloaded-splits.js";
|
||||
import {format} from "date-fns";
|
||||
import formatMoney from "../../util/format-money.js";
|
||||
import DarkEditable from "../../libraries/dark-editable/dark-editable.js";
|
||||
|
||||
|
||||
let show = function () {
|
||||
return {
|
||||
@@ -56,6 +58,10 @@ let show = function () {
|
||||
|
||||
pageProperties: {},
|
||||
formatMoney(amount, currencyCode) {
|
||||
console.log('formatting', amount, currencyCode);
|
||||
if('' === currencyCode) {
|
||||
currencyCode = 'EUR';
|
||||
}
|
||||
return formatMoney(amount, currencyCode);
|
||||
},
|
||||
format(date) {
|
||||
@@ -95,6 +101,12 @@ let show = function () {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// at this point do the inline change fields
|
||||
//inlineEdit('journal_description')
|
||||
const usernameEl = document.getElementById('journal_description');
|
||||
const popover = new DarkEditable(usernameEl, {mode: 'inline', url: '/something-else'});
|
||||
|
||||
}).catch((error) => {
|
||||
// todo auto generated.
|
||||
this.notifications.error.show = true;
|
||||
|
||||
Reference in New Issue
Block a user