Skip to content

Commit

Permalink
wip: remove jquery & upgrade: bootstrap to v5
Browse files Browse the repository at this point in the history
marked jquery as deprecated
removed jquery from the following components:
- AbstractGlobalSearch
- Dropdown
- Page
- Tooltip
- Drawer
- DiscussionListItem
- DiscussionListPane
- IndexPage
- LogInModal
- Pane
- slidable
- liveHumanTimes
  • Loading branch information
YUCLing committed Nov 13, 2024
1 parent 3b69af2 commit 3871fcf
Show file tree
Hide file tree
Showing 21 changed files with 953 additions and 948 deletions.
6 changes: 3 additions & 3 deletions framework/core/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
"type": "module",
"prettier": "@flarum/prettier-config",
"dependencies": {
"@popperjs/core": "^2.11.8",
"body-scroll-lock": "^4.0.0-beta.0",
"bootstrap": "^3.4.1",
"bootstrap": "^5.3.3",
"clsx": "^1.1.1",
"color-thief-browser": "^2.0.2",
"dayjs": "^1.10.7",
"focus-trap": "^6.7.1",
"format-message": "^6.2.4",
"jquery": "^3.6.0",
"jquery.hotkeys": "^0.1.0",
"mithril": "^2.2",
"nanoid": "^3.1.30",
"punycode": "^2.1.1",
Expand All @@ -24,7 +24,7 @@
"@flarum/jest-config": "^1.0.0",
"@flarum/prettier-config": "^1.0.0",
"@types/body-scroll-lock": "^3.1.0",
"@types/jquery": "^3.5.10",
"@types/bootstrap": "^5.2.10",
"@types/mithril": "^2.0.8",
"@types/punycode": "^2.1.0",
"@types/textarea-caret": "^3.0.1",
Expand Down
1 change: 1 addition & 0 deletions framework/core/js/src/common/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export default abstract class Component<Attrs extends ComponentAttrs = Component
* @param [selector] a jQuery-compatible selector string
* @returns the jQuery object for the DOM node
* @final
* @deprecated We are moving away from jQuery.
*/
$(selector?: string): JQuery {
const $element = $(this.element) as JQuery<HTMLElement>;
Expand Down
1 change: 1 addition & 0 deletions framework/core/js/src/common/Fragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default abstract class Fragment {
* @param [selector] a jQuery-compatible selector string
* @returns the jQuery object for the DOM node
* @final
* @deprecated We are moving away from jQuery.
*/
public $(selector?: string): JQuery {
const $element = $(this.element) as JQuery<HTMLElement>;
Expand Down
11 changes: 8 additions & 3 deletions framework/core/js/src/common/components/AbstractGlobalSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,17 @@ export default abstract class AbstractGlobalSearch<T extends SearchAttrs = Searc
this.searchState = this.attrs.state;
}

blur() {
this.element.querySelector('input')?.blur();
return true;
}

view() {
// Hide the search view if no sources were loaded
if (this.sourceItems().isEmpty()) return <div></div>;

const openSearchModal = () => {
this.$('input').blur() &&
this.blur() &&
app.modal.show(() => import('../../common/components/SearchModal'), { searchState: this.searchState, sources: this.sourceItems().toArray() });
};

Expand All @@ -101,7 +106,7 @@ export default abstract class AbstractGlobalSearch<T extends SearchAttrs = Searc
className="Search"
aria-label={this.attrs.a11yRoleLabel}
onclick={() => {
this.$('input').blur();
this.blur();
setTimeout(() => openSearchModal(), 150);
}}
>
Expand All @@ -124,7 +129,7 @@ export default abstract class AbstractGlobalSearch<T extends SearchAttrs = Searc
onkeydown: (e: KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault();
this.$('input').blur() && openSearchModal();
this.blur() && openSearchModal();
}
},
}}
Expand Down
39 changes: 14 additions & 25 deletions framework/core/js/src/common/components/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export interface IDropdownAttrs extends ComponentAttrs {
export default class Dropdown<CustomAttrs extends IDropdownAttrs = IDropdownAttrs> extends Component<CustomAttrs> {
protected showing = false;

protected backdropElement: HTMLDivElement | null = null;

static initAttrs(attrs: IDropdownAttrs) {
attrs.className ||= '';
attrs.buttonClassName ||= '';
Expand Down Expand Up @@ -67,7 +69,7 @@ export default class Dropdown<CustomAttrs extends IDropdownAttrs = IDropdownAttr
// When opening the dropdown menu, work out if the menu goes beyond the
// bottom of the viewport. If it does, we will apply class to make it show
// above the toggle button instead of below it.
this.$().on('shown.bs.dropdown', () => {
this.element.addEventListener('shown.bs.dropdown', () => {
const { lazyDraw, onshow } = this.attrs;

this.showing = true;
Expand All @@ -88,38 +90,25 @@ export default class Dropdown<CustomAttrs extends IDropdownAttrs = IDropdownAttr
m.redraw();
}

const $menu = this.$('.Dropdown-menu');
const isRight = $menu.hasClass('Dropdown-menu--right');

const top = $menu.offset()?.top ?? 0;
const height = $menu.height() ?? 0;
const windowSrollTop = $(window).scrollTop() ?? 0;
const windowHeight = $(window).height() ?? 0;

$menu.removeClass('Dropdown-menu--top Dropdown-menu--right');

$menu.toggleClass('Dropdown-menu--top', top + height > windowSrollTop + windowHeight);

if (($menu.offset()?.top || 0) < 0) {
$menu.removeClass('Dropdown-menu--top');
}

const left = $menu.offset()?.left ?? 0;
const width = $menu.width() ?? 0;
const windowScrollLeft = $(window).scrollLeft() ?? 0;
const windowWidth = $(window).width() ?? 0;

$menu.toggleClass('Dropdown-menu--right', isRight || left + width > windowScrollLeft + windowWidth);
// Mithril doesn't really redraw this component
// Bootstrap 5 has removed the open class toggle and the backdrop
// these need to be added manually.
this.element.classList.add('open');
this.backdropElement = document.createElement('div');
this.backdropElement.classList.add('dropdown-backdrop');
this.element.append(this.backdropElement);
});

this.$().on('hidden.bs.dropdown', () => {
this.element.addEventListener('hidden.bs.dropdown', () => {
this.showing = false;

if (this.attrs.onhide) {
this.attrs.onhide();
}

m.redraw();
this.element.classList.remove('open');
this.backdropElement?.remove();
});
}

Expand All @@ -132,7 +121,7 @@ export default class Dropdown<CustomAttrs extends IDropdownAttrs = IDropdownAttr
className={'Dropdown-toggle ' + this.attrs.buttonClassName}
aria-haspopup="menu"
aria-label={this.attrs.accessibleToggleLabel}
data-toggle="dropdown"
data-bs-toggle="dropdown"
onclick={this.attrs.onclick}
{...this.attrs.buttonAttrs}
>
Expand Down
6 changes: 3 additions & 3 deletions framework/core/js/src/common/components/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ export default abstract class Page<CustomAttrs extends IPageAttrs = IPageAttrs,
super.oncreate(vnode);

if (this.bodyClass) {
$('#app').addClass(this.bodyClass);
document.getElementById('app')?.classList.add(...this.bodyClass.split(' '));
}

if (this.scrollTopOnCreate) {
$(window).scrollTop(0);
window.scrollTo({ top: 0 });
}

if ('scrollRestoration' in history) {
Expand All @@ -59,7 +59,7 @@ export default abstract class Page<CustomAttrs extends IPageAttrs = IPageAttrs,
super.onremove(vnode);

if (this.bodyClass) {
$('#app').removeClass(this.bodyClass);
document.getElementById('app')?.classList.remove(...this.bodyClass.split(' '));
}
}
}
39 changes: 13 additions & 26 deletions framework/core/js/src/common/components/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Component from '../Component';
import type Mithril from 'mithril';
import classList from '../utils/classList';
import extractText from '../utils/extractText';
import BootstrapTooltip from 'bootstrap/js/dist/tooltip';

export interface TooltipAttrs extends Mithril.CommonAttributes<TooltipAttrs, Tooltip> {
/**
Expand Down Expand Up @@ -97,6 +98,8 @@ export interface TooltipAttrs extends Mithril.CommonAttributes<TooltipAttrs, Too
* </Tooltip>
*/
export default class Tooltip extends Component<TooltipAttrs> {
private tooltip: BootstrapTooltip | null = null;

private firstChild: Mithril.Vnode<any, any> | null = null;
private childDomNode: HTMLElement | null = null;

Expand Down Expand Up @@ -186,11 +189,7 @@ export default class Tooltip extends Component<TooltipAttrs> {

private recreateTooltip() {
if (this.shouldRecreateTooltip && this.childDomNode !== null) {
$(this.childDomNode).tooltip(
'destroy',
// @ts-expect-error We don't want this arg to be part of the public API. It only exists to prevent deprecation warnings when using `$.tooltip` in this component.
'DANGEROUS_tooltip_jquery_fn_deprecation_exempt'
);
this.tooltip?.dispose();
this.createTooltip();
this.shouldRecreateTooltip = false;
}
Expand All @@ -205,17 +204,9 @@ export default class Tooltip extends Component<TooltipAttrs> {
if (this.childDomNode === null) return;

if (this.attrs.tooltipVisible === true) {
$(this.childDomNode).tooltip(
'show',
// @ts-expect-error We don't want this arg to be part of the public API. It only exists to prevent deprecation warnings when using `$.tooltip` in this component.
'DANGEROUS_tooltip_jquery_fn_deprecation_exempt'
);
this.tooltip?.show();
} else if (this.attrs.tooltipVisible === false) {
$(this.childDomNode).tooltip(
'hide',
// @ts-expect-error We don't want this arg to be part of the public API. It only exists to prevent deprecation warnings when using `$.tooltip` in this component.
'DANGEROUS_tooltip_jquery_fn_deprecation_exempt'
);
this.tooltip?.hide();
}
}

Expand All @@ -239,17 +230,13 @@ export default class Tooltip extends Component<TooltipAttrs> {
this.childDomNode.setAttribute('title', realText);
this.childDomNode.setAttribute('aria-label', realText);

// https://getbootstrap.com/docs/3.3/javascript/#tooltips-options
$(this.childDomNode).tooltip(
{
html,
delay,
placement: position,
trigger,
},
// @ts-expect-error We don't want this arg to be part of the public API. It only exists to prevent deprecation warnings when using `$.tooltip` in this component.
'DANGEROUS_tooltip_jquery_fn_deprecation_exempt'
);
// https://getbootstrap.com/docs/5.0/components/tooltips/#options
this.tooltip = new BootstrapTooltip(this.childDomNode, {
html,
delay: delay || 0,
placement: position,
trigger: trigger as any
});
}

private getRealText(): string {
Expand Down
31 changes: 10 additions & 21 deletions framework/core/js/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ import 'expose-loader?exposes=$,jQuery!jquery';
import 'expose-loader?exposes=m!mithril';
import 'expose-loader?exposes=dayjs!dayjs';

import 'bootstrap/js/affix';
import 'bootstrap/js/dropdown';
import 'bootstrap/js/tooltip';
import 'bootstrap/js/transition';
import 'jquery.hotkeys/jquery.hotkeys';

import Dropdown from 'bootstrap/js/dist/dropdown';
import relativeTime from 'dayjs/plugin/relativeTime';
import localizedFormat from 'dayjs/plugin/localizedFormat';

import popperMobileModifier from './utils/popperMobileModifier';

Dropdown.Default.popperConfig = {
strategy: 'fixed',
modifiers: [
popperMobileModifier
]
};

dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);

Expand All @@ -27,18 +31,3 @@ export { app };

import './utils/arrayFlatPolyfill';

const tooltipGen = $.fn.tooltip;

// Remove in a future version of Flarum.
// @ts-ignore
$.fn.tooltip = function (options, caller) {
// Show a warning when `$.tooltip` is used outside of the Tooltip component.
// This functionality is deprecated and should not be used.
if (!['DANGEROUS_tooltip_jquery_fn_deprecation_exempt'].includes(caller)) {
console.warn(
"Calling `$.tooltip` is now deprecated. Please use the `<Tooltip>` component exposed by flarum/core instead. `$.tooltip` may be removed in a future version of Flarum.\n\nIf this component doesn't meet your requirements, please open an issue: https://github.com/flarum/core/issues/new?assignees=davwheat&labels=type/bug,needs-verification&template=bug-report.md&title=Tooltip%20component%20unsuitable%20for%20use%20case"
);
}

tooltipGen.bind(this)(options);
};
14 changes: 9 additions & 5 deletions framework/core/js/src/common/utils/Drawer.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,15 @@ export default class Drawer {

if (!this.isOpen()) return;

const $drawer = $('#drawer');
const drawer = document.getElementById('drawer');

// Used to prevent `visibility: hidden` from breaking the exit animation
$drawer.css('visibility', 'visible').one('transitionend', () => $drawer.css('visibility', ''));
drawer.style.visibility = 'visible';
drawer.addEventListener('transitionend', () => (drawer.style.visibility = ''), { once: true });

this.appElement.classList.remove('drawerOpen');

this.$backdrop?.remove?.();
this.backdrop.remove();
}

/**
Expand All @@ -105,10 +106,13 @@ export default class Drawer {

this.drawerAvailableMediaQuery.addListener(this.resizeHandler);

this.$backdrop = $('<div/>').addClass('drawer-backdrop fade').appendTo('body').on('click', this.hide.bind(this));
this.backdrop = document.createElement('div');
this.backdrop.classList.add('drawer-backdrop', 'fade');
this.backdrop.addEventListener('click', this.hide.bind(this));
document.body.append(this.backdrop);

requestAnimationFrame(() => {
this.$backdrop.addClass('in');
this.backdrop.classList.add('show');

this.focusTrap.activate();
});
Expand Down
7 changes: 3 additions & 4 deletions framework/core/js/src/common/utils/liveHumanTimes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import humanTime from './humanTime';

function updateHumanTimes() {
$('[data-humantime]').each(function () {
const $this = $(this);
const ago = humanTime($this.attr('datetime'));
document.querySelectorAll('[data-humantime]').forEach(function (el) {
const ago = humanTime(el.getAttribute('datetime'));

$this.html(ago);
el.textContent = ago;
});
}

Expand Down
20 changes: 20 additions & 0 deletions framework/core/js/src/common/utils/popperMobileModifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type Modifier } from "@popperjs/core";

export default {
name: 'responsiveMobile',
enabled: true,
phase: 'beforeWrite',
fn({ state }) {
const screen = getComputedStyle(state.elements.popper).getPropertyValue('--flarum-screen');
if (screen == 'phone') {
state.styles.popper = {
margin: null as unknown as string,
position: null as unknown as string,
left: null as unknown as string,
top: null as unknown as string,
bottom: null as unknown as string,
transform: null as unknown as string
};
}
}
} satisfies Modifier<any, any>;
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export default class DiscussionListItem<CustomAttrs extends IDiscussionListItemA
if ('ontouchstart' in window) {
const slidableInstance = slidable(this.element);

this.$('.DiscussionListItem-controls').on('hidden.bs.dropdown', () => slidableInstance.reset());
this.element.querySelector('.DiscussionListItem-controls')?.addEventListener('hidden.bs.dropdown', () => slidableInstance.reset());
}
}

Expand Down
Loading

0 comments on commit 3871fcf

Please sign in to comment.