mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
6.1 KiB
166 lines
6.1 KiB
/*!
|
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
*/
|
|
import { w as win } from './index9.js';
|
|
import { r as raf } from './helpers.js';
|
|
|
|
/**
|
|
* A utility to calculate the size of an outline notch
|
|
* width relative to the content passed. This is used in
|
|
* components such as `ion-select` with `fill="outline"`
|
|
* where we need to pass slotted HTML content. This is not
|
|
* needed when rendering plaintext content because we can
|
|
* render the plaintext again hidden with `opacity: 0` inside
|
|
* of the notch. As a result we can rely on the intrinsic size
|
|
* of the element to correctly compute the notch width. We
|
|
* cannot do this with slotted content because we cannot project
|
|
* it into 2 places at once.
|
|
*
|
|
* @internal
|
|
* @param el: The host element
|
|
* @param getNotchSpacerEl: A function that returns a reference to the notch spacer element inside of the component template.
|
|
* @param getLabelSlot: A function that returns a reference to the slotted content.
|
|
*/
|
|
const createNotchController = (el, getNotchSpacerEl, getLabelSlot) => {
|
|
let notchVisibilityIO;
|
|
const needsExplicitNotchWidth = () => {
|
|
const notchSpacerEl = getNotchSpacerEl();
|
|
if (
|
|
/**
|
|
* If the notch is not being used
|
|
* then we do not need to set the notch width.
|
|
*/
|
|
notchSpacerEl === undefined ||
|
|
/**
|
|
* If either the label property is being
|
|
* used or the label slot is not defined,
|
|
* then we do not need to estimate the notch width.
|
|
*/
|
|
el.label !== undefined ||
|
|
getLabelSlot() === null) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
const calculateNotchWidth = () => {
|
|
if (needsExplicitNotchWidth()) {
|
|
/**
|
|
* Run this the frame after
|
|
* the browser has re-painted the host element.
|
|
* Otherwise, the label element may have a width
|
|
* of 0 and the IntersectionObserver will be used.
|
|
*/
|
|
raf(() => {
|
|
setNotchWidth();
|
|
});
|
|
}
|
|
};
|
|
/**
|
|
* When using a label prop we can render
|
|
* the label value inside of the notch and
|
|
* let the browser calculate the size of the notch.
|
|
* However, we cannot render the label slot in multiple
|
|
* places so we need to manually calculate the notch dimension
|
|
* based on the size of the slotted content.
|
|
*
|
|
* This function should only be used to set the notch width
|
|
* on slotted label content. The notch width for label prop
|
|
* content is automatically calculated based on the
|
|
* intrinsic size of the label text.
|
|
*/
|
|
const setNotchWidth = () => {
|
|
const notchSpacerEl = getNotchSpacerEl();
|
|
if (notchSpacerEl === undefined) {
|
|
return;
|
|
}
|
|
if (!needsExplicitNotchWidth()) {
|
|
notchSpacerEl.style.removeProperty('width');
|
|
return;
|
|
}
|
|
const width = getLabelSlot().scrollWidth;
|
|
if (
|
|
/**
|
|
* If the computed width of the label is 0
|
|
* and notchSpacerEl's offsetParent is null
|
|
* then that means the element is hidden.
|
|
* As a result, we need to wait for the element
|
|
* to become visible before setting the notch width.
|
|
*
|
|
* We do not check el.offsetParent because
|
|
* that can be null if the host element has
|
|
* position: fixed applied to it.
|
|
* notchSpacerEl does not have position: fixed.
|
|
*/
|
|
width === 0 &&
|
|
notchSpacerEl.offsetParent === null &&
|
|
win !== undefined &&
|
|
'IntersectionObserver' in win) {
|
|
/**
|
|
* If there is an IO already attached
|
|
* then that will update the notch
|
|
* once the element becomes visible.
|
|
* As a result, there is no need to create
|
|
* another one.
|
|
*/
|
|
if (notchVisibilityIO !== undefined) {
|
|
return;
|
|
}
|
|
const io = (notchVisibilityIO = new IntersectionObserver((ev) => {
|
|
/**
|
|
* If the element is visible then we
|
|
* can try setting the notch width again.
|
|
*/
|
|
if (ev[0].intersectionRatio === 1) {
|
|
setNotchWidth();
|
|
io.disconnect();
|
|
notchVisibilityIO = undefined;
|
|
}
|
|
},
|
|
/**
|
|
* Set the root to be the host element
|
|
* This causes the IO callback
|
|
* to be fired in WebKit as soon as the element
|
|
* is visible. If we used the default root value
|
|
* then WebKit would only fire the IO callback
|
|
* after any animations (such as a modal transition)
|
|
* finished, and there would potentially be a flicker.
|
|
*/
|
|
{ threshold: 0.01, root: el }));
|
|
io.observe(notchSpacerEl);
|
|
return;
|
|
}
|
|
/**
|
|
* If the element is visible then we can set the notch width.
|
|
* The notch is only visible when the label is scaled,
|
|
* which is why we multiply the width by 0.75 as this is
|
|
* the same amount the label element is scaled by in the host CSS.
|
|
* (See $form-control-label-stacked-scale in ionic.globals.scss).
|
|
*/
|
|
notchSpacerEl.style.setProperty('width', `${width * 0.75}px`);
|
|
};
|
|
const destroy = () => {
|
|
if (notchVisibilityIO) {
|
|
notchVisibilityIO.disconnect();
|
|
notchVisibilityIO = undefined;
|
|
}
|
|
};
|
|
return {
|
|
calculateNotchWidth,
|
|
destroy,
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Checks if the form element is in an invalid state based on
|
|
* Ionic validation classes.
|
|
*
|
|
* @param el The form element to check.
|
|
* @returns `true` if the element is invalid, `false` otherwise.
|
|
*/
|
|
const checkInvalidState = (el) => {
|
|
const hasIonTouched = el.classList.contains('ion-touched');
|
|
const hasIonInvalid = el.classList.contains('ion-invalid');
|
|
return hasIonTouched && hasIonInvalid;
|
|
};
|
|
|
|
export { checkInvalidState as a, createNotchController as c };
|
|
|