Skip to content

Commit

Permalink
feat: add attachToElement property to cat-datepicker component (#462)
Browse files Browse the repository at this point in the history
feat: add attachToElement and position properties to cat-datepicker component
  • Loading branch information
glushkova91 authored Mar 7, 2024
1 parent c0a5cfd commit 1d6c299
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
<cat-dialog>
<cat-dialog-header headline="Headline"></cat-dialog-header>
<cat-datepicker
clearable
icon-right
icon="calendar-outlined"
label="Datepicker attached to dialog"
min="0001-01-01T00:00:00.000Z"
mode="daterange"
placeholder="Select date"
[attachToElement]="true"
>
</cat-datepicker>
<p>
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore
magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd
Expand Down
4 changes: 4 additions & 0 deletions angular/projects/catalyst/src/lib/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ export declare interface CatCheckbox extends Components.CatCheckbox {

@ProxyCmp({
inputs: [
'attachToElement',
'autoComplete',
'clearable',
'disabled',
Expand All @@ -299,6 +300,7 @@ export declare interface CatCheckbox extends Components.CatCheckbox {
'nativeAttributes',
'nativePickerAttributes',
'placeholder',
'position',
'readonly',
'required',
'requiredMarker',
Expand All @@ -315,6 +317,7 @@ export declare interface CatCheckbox extends Components.CatCheckbox {
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: [
'attachToElement',
'autoComplete',
'clearable',
'disabled',
Expand All @@ -334,6 +337,7 @@ export declare interface CatCheckbox extends Components.CatCheckbox {
'nativeAttributes',
'nativePickerAttributes',
'placeholder',
'position',
'readonly',
'required',
'requiredMarker',
Expand Down
2 changes: 1 addition & 1 deletion core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"jest": "^29.7.0",
"jest-cli": "^29.7.0",
"prettier": "^3.1.0",
"puppeteer": "^21.5.2",
"puppeteer": "^22.2.0",
"replace-in-file": "^7.0.2",
"rxjs": "^7.5.5",
"stencil-inline-svg": "^1.1.0",
Expand Down
23 changes: 21 additions & 2 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { Breakpoint } from "./utils/breakpoints";
import { CatDatepickerMode } from "./components/cat-datepicker/cat-datepicker.mode";
import { ErrorMap } from "./components/cat-form-hint/cat-form-hint";
import { BaseOptions } from "flatpickr/dist/types/options";
import { Placement } from "@floating-ui/dom";
import { ErrorMap } from "./components/cat-form-hint/cat-form-hint";
import { InputType } from "./components/cat-input/input-type";
import { CatSelectConnector, CatSelectMultipleTaggingValue, CatSelectTaggingValue, Item } from "./components/cat-select/cat-select";
import { Observable } from "rxjs";
export { Breakpoint } from "./utils/breakpoints";
export { CatDatepickerMode } from "./components/cat-datepicker/cat-datepicker.mode";
export { ErrorMap } from "./components/cat-form-hint/cat-form-hint";
export { Placement } from "@floating-ui/dom";
export { ErrorMap } from "./components/cat-form-hint/cat-form-hint";
export { InputType } from "./components/cat-input/input-type";
export { CatSelectConnector, CatSelectMultipleTaggingValue, CatSelectTaggingValue, Item } from "./components/cat-select/cat-select";
export { Observable } from "rxjs";
Expand Down Expand Up @@ -296,6 +297,10 @@ export namespace Components {
"value"?: any;
}
interface CatDatepicker {
/**
* Instead of body, appends the calendar to the cat-datepicker element instead
*/
"attachToElement": boolean;
/**
* Hint for form autofill feature.
*/
Expand Down Expand Up @@ -381,6 +386,11 @@ export namespace Components {
* The placeholder text to display within the input.
*/
"placeholder"?: string;
/**
* Where the calendar is rendered relative to the input vertically and horizontally. In the format of "[vertical] [horizontal]". Vertical can be auto, above or below (required). Horizontal can be left, center or right. If
* @attachToElement is passed the value should be in Placement format
*/
"position"?: BaseOptions["position"] | Placement;
/**
* The value is not editable.
*/
Expand Down Expand Up @@ -1993,6 +2003,10 @@ declare namespace LocalJSX {
"value"?: any;
}
interface CatDatepicker {
/**
* Instead of body, appends the calendar to the cat-datepicker element instead
*/
"attachToElement"?: boolean;
/**
* Hint for form autofill feature.
*/
Expand Down Expand Up @@ -2081,6 +2095,11 @@ declare namespace LocalJSX {
* The placeholder text to display within the input.
*/
"placeholder"?: string;
/**
* Where the calendar is rendered relative to the input vertically and horizontally. In the format of "[vertical] [horizontal]". Vertical can be auto, above or below (required). Horizontal can be left, center or right. If
* @attachToElement is passed the value should be in Placement format
*/
"position"?: BaseOptions["position"] | Placement;
/**
* The value is not editable.
*/
Expand Down
9 changes: 9 additions & 0 deletions core/src/components/cat-datepicker/cat-datepicker.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import flatpickr from 'flatpickr';
import weekSelectPlugin from 'flatpickr/dist/plugins/weekSelect/weekSelect';
import { CatDatepickerMode } from './cat-datepicker.mode';
import { BaseOptions, Hook } from 'flatpickr/dist/types/options';

export function getConfig(
options: {
Expand All @@ -14,6 +15,9 @@ export function getConfig(
readonly: boolean;
nativePickerAttributes: { [key: string]: string };
applyChange: (value?: string) => void;
appendTo?: HTMLElement | undefined;
position?: BaseOptions['position'];
onReady?: Hook;
},
more: flatpickr.Options.Options = {}
): flatpickr.Options.Options {
Expand All @@ -40,11 +44,16 @@ export function getConfig(
weekNumbers: true,
minuteIncrement: options.step,
clickOpens: !options.disabled && !options.readonly,
appendTo: options.appendTo,
position: options.position ? options.position : 'auto',
onReady: (_dates, _dateStr, flatpickr) => {
for (const key in options.nativePickerAttributes) {
const value = options.nativePickerAttributes[key];
flatpickr.calendarContainer.setAttribute(key, value);
}
if (options.appendTo && typeof options.onReady === 'function') {
options.onReady(_dates, _dateStr, flatpickr);
}
},
onClose: function (dates, _dateStr, instance) {
if (options.mode === 'daterange' && dates.length < 2) {
Expand Down
2 changes: 2 additions & 0 deletions core/src/components/cat-datepicker/cat-datepicker.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
@use 'variables' as *;
@use 'mixins' as *;
@import '~flatpickr/dist/flatpickr.css';
@import 'vendor/flatpickr';

:host {
display: flex;
Expand Down
1 change: 1 addition & 0 deletions core/src/components/cat-datepicker/cat-datepicker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('cat-datepicker', () => {
<cat-datepicker label="Label">
<mock:shadow-root>
<cat-input errorupdate="0" label="Label" requiredmarker="optional"></cat-input>
<div class="datepicker-wrapper"></div>
</mock:shadow-root>
</cat-datepicker>
`);
Expand Down
54 changes: 51 additions & 3 deletions core/src/components/cat-datepicker/cat-datepicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { getConfig } from './cat-datepicker.config';
import { getFormat } from './cat-datepicker.format';
import { getLocale } from './cat-datepicker.locale';
import { CatDatepickerMode } from './cat-datepicker.mode';
import { autoUpdate, computePosition, flip, Placement, ReferenceElement } from '@floating-ui/dom';
import { BaseOptions } from 'flatpickr/dist/types/options';

@Component({
tag: 'cat-datepicker',
Expand All @@ -16,6 +18,7 @@ import { CatDatepickerMode } from './cat-datepicker.mode';
export class CatDatepickerFlat {
private pickr?: flatpickr.Instance;
private _input?: HTMLCatInputElement;
private _calendarWrapper?: HTMLDivElement;
private get input(): HTMLInputElement | undefined {
return this._input?.shadowRoot?.querySelector('input') ?? undefined;
}
Expand Down Expand Up @@ -131,6 +134,19 @@ export class CatDatepickerFlat {
*/
@Prop() step = 5;

/**
* Instead of body, appends the calendar to the cat-datepicker element instead
*/
@Prop() attachToElement = false;

/**
* Where the calendar is rendered relative to the input vertically and horizontally.
* In the format of "[vertical] [horizontal]". Vertical can be auto, above or below (required).
* Horizontal can be left, center or right.
* If @attachToElement is passed the value should be in Placement format
*/
@Prop() position?: BaseOptions['position'] | Placement;

/**
* The value as ISO Date string, e.g. 2017-03-04T01:23:43.000Z or as a week number string.
*/
Expand Down Expand Up @@ -249,7 +265,7 @@ export class CatDatepickerFlat {
}

render() {
return (
return [
<cat-input
ref={el => (this._input = el)}
requiredMarker={this.requiredMarker}
Expand Down Expand Up @@ -296,8 +312,9 @@ export class CatDatepickerFlat {
<slot name="hint"></slot>
</span>
)}
</cat-input>
);
</cat-input>,
<div ref={el => (this._calendarWrapper = el)} class="datepicker-wrapper"></div>
];
}

private initDatepicker(input?: HTMLInputElement): flatpickr.Instance | undefined {
Expand All @@ -320,9 +337,40 @@ export class CatDatepickerFlat {
step: this.step,
disabled: this.disabled,
readonly: this.readonly,
appendTo: this.attachToElement ? this._calendarWrapper : undefined,
nativePickerAttributes: { ...nativePickerAttributes, ...this.nativePickerAttributes },
// flatpickr has open bug about incorrect positioning when appendTo is used,
// we have to use custom logic to calculate position
// https://github.com/flatpickr/flatpickr/issues/1619
position: this.attachToElement
? (flatpickr, positionElement) => {
this.updatePosition(flatpickr, positionElement);
}
: (this.position as BaseOptions['position']) || undefined,
onReady: (_dates, _dateStr, flatpickr) => {
autoUpdate(input, flatpickr.calendarContainer, () => this.updatePosition(flatpickr, flatpickr._input));
},
applyChange: value => (this.value = value)
})
);
}

private updatePosition(flatpickr: flatpickr.Instance, positionElement: HTMLElement | undefined): void {
if (positionElement) {
computePosition(positionElement as ReferenceElement, flatpickr.calendarContainer, {
strategy: 'fixed',
placement: this.position || 'bottom-start',
middleware: [flip()]
}).then(({ x, y, placement }) => {
if (flatpickr.calendarContainer) {
flatpickr.calendarContainer.dataset.placement = placement;
Object.assign(flatpickr.calendarContainer.style, {
left: `${x}px`,
top: `${y}px`,
position: 'fixed'
});
}
});
}
}
}
Loading

0 comments on commit 1d6c299

Please sign in to comment.