Skip to content

Commit

Permalink
wip: remove jquery
Browse files Browse the repository at this point in the history
removed jquery from components:
- AvatarEditor
- CommentPost
- HeaderList
- LogInButton
- NotificationGrid
- PostMeta
- PostsPage
- PostStream
- ReplyPlaceholder
- SignUpModal
- WelcomeHero
- ComposerState
fix: wrong usage of select
fix: incorrect selector syntax
  • Loading branch information
YUCLing committed Nov 18, 2024
1 parent 297a3d6 commit ef8a333
Show file tree
Hide file tree
Showing 19 changed files with 160 additions and 125 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default abstract class AutocompleteDropdown<
m.redraw();

this.addEventListener('mouseup', (e) => e.preventDefault(), { once: true });
this.dispatchEvent(new Event('select'));
this.select();
});
input.addEventListener('blur', () => {
component.hasFocus = false;
Expand Down
2 changes: 1 addition & 1 deletion framework/core/js/src/common/components/EditUserModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export default class EditUserModal<CustomAttrs extends IEditUserModalAttrs = IEd
const target = e.target as HTMLInputElement;
this.setPassword(target.checked);
m.redraw.sync();
if (target.checked) this.element.querySelector('[name=password]')?.dispatchEvent(new Event('select'));
if (target.checked) (this.element.querySelector('[name=password]') as HTMLInputElement)?.select();
e.redraw = false;
}}
disabled={this.nonAdminEditingAdmin()}
Expand Down
4 changes: 2 additions & 2 deletions framework/core/js/src/common/components/FormModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default abstract class FormModal<ModalAttrs extends IFormModalAttrs = IFo
const input = this.element.querySelector('input, select, textarea') as (HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement);
if (!input) return;
input.focus();
input.dispatchEvent(new Event('select'));
if ('select' in input) input.select();
}

/**
Expand All @@ -46,7 +46,7 @@ export default abstract class FormModal<ModalAttrs extends IFormModalAttrs = IFo
m.redraw();

if (error.status === 422 && error.response?.errors) {
this.element.querySelector(`form [name=${(error.response.errors as any[])[0].source.pointer.replace('/data/attributes/', '')}]`)?.dispatchEvent(new Event('select'));
(this.element.querySelector(`form [name='${(error.response.errors as any[])[0].source.pointer.replace('/data/attributes/', '')}']`) as HTMLInputElement)?.select();
} else {
this.onready();
}
Expand Down
4 changes: 4 additions & 0 deletions framework/core/js/src/common/utils/heightWithMargin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default function heightWithMargin(element: HTMLElement): number {
const style = getComputedStyle(element);
return element.getBoundingClientRect().height + parseInt(style.marginBottom, 10) + parseInt(style.marginTop, 10);
}
24 changes: 24 additions & 0 deletions framework/core/js/src/common/utils/scrollEnd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* A utility function that waits for the scroll to end.
* Due to lack of support from some browsers, this is a workaround for `scrollend` event.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo#behavior
* @see https://caniuse.com/mdn-api_element_scrollend_event
* @param container The container element to wait scroll end on.
* @returns A promise that resolves when the scroll is ended.
*/
export default function scrollEnd(container: HTMLElement): Promise<void> {
let lastTop = container.scrollTop;

return new Promise((resolve) => {
const animFrame = () => {
if (lastTop === container.scrollTop) {
resolve();
} else {
requestAnimationFrame(animFrame);
lastTop = container.scrollTop;
}
};
requestAnimationFrame(animFrame);
});
}
18 changes: 9 additions & 9 deletions framework/core/js/src/forum/components/AvatarEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,15 @@ export default class AvatarEditor extends Component {

// Create a hidden HTML input element and click on it so the user can select
// an avatar file. Once they have, we will upload it via the API.
const $input = $('<input type="file" accept=".jpg, .jpeg, .png, .bmp, .gif">');

$input
.appendTo('body')
.hide()
.click()
.on('input', (e) => {
this.upload($(e.target)[0].files[0]);
});
const input = document.createElement('input');
input.type = 'file';
input.accept = '.jpg, .jpeg, .png, .bmp, .gif';
input.style.display = 'none';
input.click();
input.addEventListener('input', (e) => {
this.upload(e.target.files[0]);
});
document.body.append(input);
}

/**
Expand Down
25 changes: 13 additions & 12 deletions framework/core/js/src/forum/components/CommentPost.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ export default class CommentPost extends Post {
// all of the <script> tags in the content and evaluate them. This is
// necessary because TextFormatter outputs them for e.g. syntax highlighting.
if (this.contentHtml !== contentHtml) {
this.$('.Post-body script').each(function () {
this.element.querySelectorAll('.Post-body script').forEach((el) => {
const script = document.createElement('script');
script.textContent = this.textContent;
Array.from(this.attributes).forEach((attr) => script.setAttribute(attr.name, attr.value));
this.parentNode.replaceChild(script, this);
});
script.textContent = el.textContent;
Array.from(el.attributes).forEach((attr) => script.setAttribute(attr.name, attr.value));
this.parentNode.replaceChild(script, el);
})
}

this.contentHtml = contentHtml;
Expand Down Expand Up @@ -183,18 +183,19 @@ export default class CommentPost extends Post {
this.cardVisible = true;
m.redraw();

setTimeout(() => this.$('.UserCard').addClass('in'));
setTimeout(() => this.element.querySelector('.UserCard')?.classList.add('show'));
}

/**
* Hide the user card.
*/
hideCard() {
this.$('.UserCard')
.removeClass('in')
.one('transitionend webkitTransitionEnd oTransitionEnd', () => {
this.cardVisible = false;
m.redraw();
});
const userCard = this.element.querySelector('.UserCard');
if (!userCard) return;
userCard.classList.remove('show');
userCard.addEventListener('transitionend', () => {
this.cardVisible = false;
m.redraw();
}, { once: true });
}
}
16 changes: 8 additions & 8 deletions framework/core/js/src/forum/components/HeaderList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export interface IHeaderListAttrs extends ComponentAttrs {
}

export default class HeaderList<CustomAttrs extends IHeaderListAttrs = IHeaderListAttrs> extends Component<CustomAttrs> {
$content: JQuery<any> | null = null;
$scrollParent: JQuery<any> | null = null;
content: HTMLDivElement | null = null;
scrollParent: HTMLElement | null = null;
boundScrollHandler: (() => void) | null = null;

view(vnode: Mithril.Vnode<CustomAttrs, this>) {
Expand Down Expand Up @@ -47,28 +47,28 @@ export default class HeaderList<CustomAttrs extends IHeaderListAttrs = IHeaderLi
super.oncreate(vnode);

if (this.attrs.loadMore) {
this.$content = this.$('.HeaderList-content');
this.content = this.element.querySelector(".HeaderList-content");

// If we are on the notifications page, the window will be scrolling and not the $notifications element.
this.$scrollParent = this.inPanel() ? this.$content : $(window);
this.scrollParent = this.inPanel() ? this.content : document.documentElement;

this.boundScrollHandler = this.scrollHandler.bind(this);
this.$scrollParent.on('scroll', this.boundScrollHandler);
this.scrollParent?.addEventListener('scroll', this.boundScrollHandler);
}
}

onremove(vnode: Mithril.VnodeDOM<CustomAttrs, this>) {
super.onremove(vnode);

if (this.attrs.loadMore) {
this.$scrollParent!.off('scroll', this.boundScrollHandler!);
this.scrollParent?.removeEventListener('scroll', this.boundScrollHandler!);
}
}

scrollHandler() {
// Whole-page scroll events are listened to on `window`, but we need to get the actual
// scrollHeight, scrollTop, and clientHeight from the document element.
const scrollParent = this.inPanel() ? this.$scrollParent![0] : document.documentElement;
const scrollParent = this.inPanel() ? this.scrollParent! : document.documentElement;

// On very short screens, the scrollHeight + scrollTop might not reach the clientHeight
// by a fraction of a pixel, so we compensate for that.
Expand All @@ -84,6 +84,6 @@ export default class HeaderList<CustomAttrs extends IHeaderListAttrs = IHeaderLi
* we need to listen to scroll events on the window, and get scroll state from the body.
*/
inPanel() {
return this.$content!.css('overflow') === 'auto';
return getComputedStyle(this.content!).overflow === 'auto';
}
}
5 changes: 2 additions & 3 deletions framework/core/js/src/forum/components/LogInButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,14 @@ export default class LogInButton extends Button {
attrs.onclick = function () {
const width = 580;
const height = 400;
const $window = $(window);

window.open(
app.forum.attribute('baseUrl') + attrs.path,
'logInPopup',
`width=${width},` +
`height=${height},` +
`top=${$window.height() / 2 - height / 2},` +
`left=${$window.width() / 2 - width / 2},` +
`top=${window.innerHeight / 2 - height / 2},` +
`left=${window.innerWidth / 2 - width / 2},` +
'status=no,scrollbars=yes,resizable=no'
);
};
Expand Down
20 changes: 9 additions & 11 deletions framework/core/js/src/forum/components/NotificationGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,17 @@ export default class NotificationGrid extends Component {
oncreate(vnode) {
super.oncreate(vnode);

this.$('thead .NotificationGrid-groupToggle').bind('mouseenter mouseleave', function (e) {
const i = parseInt($(this).index(), 10) + 1;
$(this)
.parents('table')
.find('td:nth-child(' + i + ')')
.toggleClass('highlighted', e.type === 'mouseenter');
this.element.querySelectorAll('thead .NotificationGrid-groupToggle').forEach((toggle) => {
['mouseenter', 'mouseleave'].forEach((ev) => toggle.addEventListener(ev, function(e) {
const i = parseInt(Array.from(this.parentElement.children).indexOf(this), 10) + 1;
this.closest('table').querySelectorAll('td:nth-child(' + i + ')').forEach((td) => td.classList.toggle('highlighted', e.type === 'mouseenter'));
}));
});

this.$('tbody .NotificationGrid-groupToggle').bind('mouseenter mouseleave', function (e) {
$(this)
.parent()
.find('td')
.toggleClass('highlighted', e.type === 'mouseenter');
this.element.querySelectorAll('tbody .NotificationGrid-groupToggle').forEach((toggle) => {
['mouseenter', 'mouseleave'].forEach((ev) => toggle.addEventListener(ev, function(e) {
this.parentElement.querySelectorAll('td').forEach((td) => td.classList.toggle('highlighted', e.type === 'mouseenter'));
}));
});
}

Expand Down
2 changes: 1 addition & 1 deletion framework/core/js/src/forum/components/PostMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default class PostMeta<CustomAttrs extends IPostMetaAttrs = IPostMetaAttr
// When the dropdown menu is shown, select the contents of the permalink
// input so that the user can quickly copy the URL.
const selectPermalink = function (this: Element, e: MouseEvent) {
setTimeout(() => $(this).parent().find('.PostMeta-permalink').select());
setTimeout(() => (this.parentElement?.querySelector('.PostMeta-permalink') as HTMLInputElement)?.select());

e.redraw = false;
};
Expand Down
Loading

0 comments on commit ef8a333

Please sign in to comment.