Skip to content

Commit

Permalink
Moves JS/CSS to Vite buildchain
Browse files Browse the repository at this point in the history
  • Loading branch information
mmikkel committed Aug 7, 2023
1 parent 2c9544e commit e097e4c
Show file tree
Hide file tree
Showing 26 changed files with 2,199 additions and 329 deletions.
2 changes: 2 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_DEVSERVER_HOST=0.0.0.0
VITE_DEVSERVER_PORT=5173
24 changes: 24 additions & 0 deletions build/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
1 change: 1 addition & 0 deletions build/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v16.15.0
25 changes: 25 additions & 0 deletions build/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "RedirectMate",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^0.27.2",
"craftcms-sass": "^3.5.7",
"isbot": "^3.5.0",
"qs": "^6.11.0",
"ua-parser-js": "^1.0.2"
},
"devDependencies": {
"autoprefixer": "^10.4.7",
"postcss": "^8.4.14",
"sass": "^1.54.0",
"tailwindcss": "^3.1.6",
"vite": "^3.0.0"
}
}
6 changes: 6 additions & 0 deletions build/postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
165 changes: 165 additions & 0 deletions build/src/aimate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import CustomPromptModal from "./components/CustomPromptModal.js";

import './aimate.scss';

$(() => {

let customPromptModal;

const onPromptClick = async e => {
e.preventDefault();
const {currentTarget: promptLink} = e;
const menu = promptLink.closest('[data-aimate-menu]');
const menuButton = document.querySelector(`button[aria-controls="${menu.id}"]`);
if (menuButton.classList.contains('loading')) {
return;
}
$(menuButton).trigger('click');
const {field: fieldId, element: elementId, site: siteId} = menuButton.dataset;
const field = menuButton.closest('.field');
if (!field) {
Craft.cp.displayError('Field not found');
return;
}
const {type: fieldType} = field.dataset;
let input;
if (fieldType === 'craft\\fields\\Table') {
input = menuButton.closest('td.textual').querySelector('input,textarea');
} else {
input = menuButton.closest('.input').querySelector('input,textarea');
}
if (!input) {
Craft.cp.displayError('Input not found');
return;
}
let params = {};
if (promptLink.hasAttribute('data-custom')) {
if (!customPromptModal) {
customPromptModal = new CustomPromptModal();
}
customPromptModal.show();
const customPrompt = await customPromptModal.getText();
if (!customPrompt) {
return;
}
params.custom = customPrompt;
} else {
const {prompt} = promptLink.dataset;
if (!prompt) {
Craft.cp.displayError('No prompt');
return;
}
params.prompt = prompt;
}
const elementEditor = $(input.closest('[data-element-editor]')).data('elementEditor');
params = {
...params,
elementId,
siteId
};
if (elementEditor) {
params = {
...params,
draftId: elementEditor.settings.draftId,
isProvisionalDraft: elementEditor.settings.isProvisionalDraft
};
}
menuButton.classList.add('loading');
Craft.sendActionRequest(
'POST',
'_aimate/default/do-prompt',
{
data: {
text: input.value,
...params
}
}
)
.then(res => {
const {data} = res;
if (!data.text) {
return;
}
const field = input.closest('.field');
const {type: fieldType} = field.dataset;
if (fieldType === 'craft\\ckeditor\\Field') {
const editable = field.querySelector('.ck-editor__editable');
const ckEditorInstance = editable ? editable.ckeditorInstance : null;
if (!ckEditorInstance) {
throw new Error('Unable to find CKEditor instance in DOM');
}
ckEditorInstance.focus();
ckEditorInstance.execute('selectAll');
const viewFragment = ckEditorInstance.data.processor.toView(data.text);
const modelFragment = ckEditorInstance.data.toModel(viewFragment);
ckEditorInstance.model.insertContent(modelFragment);
} else if (fieldType === 'craft\\redactor\\Field') {
const redactorInstance = $R(`#${input.id}`);
redactorInstance.editor.focus();
redactorInstance.insertion.set(data.text);
redactorInstance.source.getElement().val(data.text);
} else {
input.focus();
input.select();
if (!document.execCommand('insertText', false, data.text)) {
input.setRangeText(data.text);
}
}
if (elementEditor) {
elementEditor.checkForm();
}
})
.catch(({response}) => {
Craft.cp.displayError(response.message || response.data.message);
})
.catch(error => {
console.error(error);
})
.then(() => {
menuButton.classList.remove('loading');
input.focus();
});
};

const initTableRow = $tr => {
const field = $tr.get(0).closest('.field');
if (!field) {
return;
}
const aimateButton = field.querySelector('button[data-aimate]');
if (!aimateButton) {
return;
}
const tds = $tr.children().get().filter(td => td.classList.contains('singleline-cell', 'multiline-cell'));
const trId = $tr.index();
tds.forEach(td => {
const $aimateButton = $(aimateButton).clone(false, true);
const menuId = $aimateButton.attr('aria-controls');
const $menu = $($(`#${menuId}`)).clone(false, true);
const textarea = td.querySelector('textarea');
const tdId = `${trId}-${$(td).index()}`;
$menu.attr('id', `${$menu.attr('id')}-${trId}-${tdId}`);
$('body').append($menu);
$aimateButton.attr('aria-controls', $menu.attr('id'));
$(textarea.parentNode).prepend($aimateButton.disclosureMenu());
});
};

$('.field table.editable tbody tr').each(function () {
initTableRow($(this));
});

const rowInitFn = Craft.EditableTable.Row.prototype.init;
Craft.EditableTable.Row.prototype.init = function () {
rowInitFn.apply(this, arguments);
initTableRow(this.$tr);
};

$('body').on('click', '[data-aimate-menu] a', onPromptClick);

});

// Accept HMR as per: https://vitejs.dev/guide/api-hmr.html
if (import.meta.hot) {
import.meta.hot.accept();
}
38 changes: 38 additions & 0 deletions build/src/aimate.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@import "../node_modules/craftcms-sass/mixins";

button[data-aimate] {
display: inline-block !important;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}

button[data-aimate] + input.nicetext,
button[data-aimate] + textarea.nicetext,
td.textual button[data-aimate] + textarea {
padding-right: 60px;
}

.field[data-type="craft\\\\fields\\\\Table"] .input > button[data-aimate] {
display: none !important;
}

.field[data-type="craft\\\\fields\\\\Table"] tbody td.textual button[data-aimate] {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}

.field[data-type="craft\\\\fields\\\\Table"] tbody td.textual {
position: relative;
z-index: 1;
}

.field[data-type="craft\\\\fields\\\\Table"] tbody td.textual.focus {
z-index: 2;
}

//.aimate-customprompt .ProseMirror {
// height: 100%;
// padding: 10px;
//}
85 changes: 85 additions & 0 deletions build/src/components/CustomPromptModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const CustomPromptModal = Garnish.Modal.extend({
init: function () {

this.base();
this.setSettings({
resizable: true,
});

const $modalContainerDiv = $(
'<div class="modal fitted aimate-customprompt"></div>'
)
.addClass()
.appendTo(Garnish.$bod);

const $body = $(`
<div class="body" style="display:flex;flex-direction:column;min-height:100%;">
<div class="header" style="flex: none;">
<h1 style="margin-bottom:10px;">Custom prompt</h1>
<p class="notice has-icon">
<span class="icon" aria-hidden="true"></span>
<span>Add a &lt;text&gt; token to the prompt to include the current value in the field.</span>
</p>
</div>
<form method="post" accept-charset="UTF-8" style="display:flex;height:100%;flex: 1 1 auto;position:relative;">
<textarea style="width:420px;min-width:100%;max-width:100%;resize:none;padding:10px;"></textarea>
</form>
</div>
`).appendTo(
$modalContainerDiv.empty()
);

const $footer = $('<div class="footer"/>').appendTo($body);
const $footerBtnContainer = $('<div class="buttons right"/>').appendTo($footer);
const $cancelBtn = $('<button/>', {
type: 'button',
class: 'btn',
text: Craft.t('app', 'Cancel'),
}).appendTo($footerBtnContainer);

const $applyBtn = Craft.ui
.createSubmitButton({ label: Craft.t('app', 'Apply') })
.appendTo($footerBtnContainer);

this.$textarea = $body.find('textarea');

this.addListener($cancelBtn, 'click', 'onCancel');
this.addListener($applyBtn, 'click', 'onApply');

const _this = this;

this.$textarea = $modalContainerDiv.find('textarea');

this.setContainer($modalContainerDiv);

},

onShow: function () {
const _this = this;
this.promise = new Promise((resolve, reject) => {
_this.reject = reject;
_this.resolve = resolve;
});
},

onHide: function () {
this.$textarea.val('');
},

onCancel: function () {
this.resolve();
this.hide();
},

onApply: function () {
this.resolve(this.$textarea.val());
this.hide();
},

getText: function () {
return this.promise;
}

});

export default CustomPromptModal;
Loading

0 comments on commit e097e4c

Please sign in to comment.