mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-01-26 17:54:22 +01:00
Merge branch 'dev' into feature/list-your-product-ui
This commit is contained in:
@@ -12,5 +12,6 @@ export * from './useCurrentSection'
|
||||
export * from './usePreload'
|
||||
export * from './useCarousel'
|
||||
export * from './usePrompt'
|
||||
export * from './useIsDraggingOnElement'
|
||||
export * from './useCountdown'
|
||||
|
||||
|
||||
76
src/utils/hooks/useIsDraggingOnElement.ts
Normal file
76
src/utils/hooks/useIsDraggingOnElement.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||
|
||||
|
||||
|
||||
function addEventListener<K extends keyof HTMLElementEventMap>(element: HTMLElement, type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions) {
|
||||
element.addEventListener(type, listener, options);
|
||||
return () => element.removeEventListener(type, listener, options);
|
||||
}
|
||||
|
||||
function setImmediate(callback: (...args: any[]) => void, ...args: any[]) {
|
||||
let cancelled = false;
|
||||
Promise.resolve().then(() => cancelled || callback(...args));
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
|
||||
function noop() { }
|
||||
|
||||
function handleDragOver(ev: DragEvent) {
|
||||
ev.preventDefault();
|
||||
ev.dataTransfer!.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
|
||||
export const useIsDraggingOnElement = (options?: Partial<{
|
||||
ref: MutableRefObject<HTMLElement>
|
||||
}>) => {
|
||||
const listenersRef = useRef<any[]>([]);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
let count = 0;
|
||||
let cancelImmediate = noop;
|
||||
|
||||
const element = options?.ref?.current ?? document as unknown as HTMLElement;
|
||||
|
||||
listenersRef.current = [
|
||||
addEventListener(element, 'dragover', handleDragOver),
|
||||
addEventListener(element, 'dragenter', ev => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (count === 0) {
|
||||
setIsDragging(true)
|
||||
}
|
||||
++count;
|
||||
}),
|
||||
addEventListener(element, 'dragleave', ev => {
|
||||
ev.preventDefault();
|
||||
cancelImmediate = setImmediate(() => {
|
||||
--count;
|
||||
if (count === 0) {
|
||||
setIsDragging(false)
|
||||
}
|
||||
})
|
||||
|
||||
}),
|
||||
addEventListener(element, 'drop', ev => {
|
||||
ev.preventDefault();
|
||||
cancelImmediate();
|
||||
if (count > 0) {
|
||||
count = 0;
|
||||
setIsDragging(false)
|
||||
}
|
||||
}),
|
||||
]
|
||||
|
||||
return () => {
|
||||
listenersRef.current.forEach(f => f());
|
||||
}
|
||||
}, [options?.ref])
|
||||
|
||||
|
||||
return isDragging
|
||||
}
|
||||
@@ -16,8 +16,10 @@ import "src/styles/index.scss";
|
||||
import 'react-loading-skeleton/dist/skeleton.css'
|
||||
import { ApolloProvider } from '@apollo/client';
|
||||
import { apolloClient } from '../apollo';
|
||||
import { FormProvider, useForm, UseFormProps, Controller } from 'react-hook-form';
|
||||
import { Controller, FormProvider, useForm, UseFormProps, Controller } from 'react-hook-form';
|
||||
import ModalsContainer from 'src/Components/Modals/ModalsContainer/ModalsContainer';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import { NotificationsService } from 'src/services';
|
||||
|
||||
|
||||
// Enable the Mocks Service Worker
|
||||
@@ -63,6 +65,11 @@ export const WrapperDecorator: DecoratorFn = (Story, options) => {
|
||||
effect='solid'
|
||||
delayShow={1000}
|
||||
/>
|
||||
<ToastContainer
|
||||
{...NotificationsService.defaultOptions}
|
||||
newestOnTop={false}
|
||||
limit={2}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -112,16 +119,42 @@ export const centerDecorator: DecoratorFn = (Story) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
export function WrapForm<T = any>(options?: Partial<UseFormProps<T>>): DecoratorFn {
|
||||
export function WrapForm<T = any>(options?: Partial<UseFormProps<T> & { logValues: boolean }>): DecoratorFn {
|
||||
const Func: DecoratorFn = (Story) => {
|
||||
const methods = useForm<T>(options);
|
||||
|
||||
if (options?.logValues) {
|
||||
console.log(methods.watch())
|
||||
}
|
||||
|
||||
return <FormProvider {...methods} >
|
||||
<Story />
|
||||
<Story onChang />
|
||||
</FormProvider>
|
||||
}
|
||||
return Func
|
||||
}
|
||||
|
||||
export function WrapFormController<T = any>(options: Partial<UseFormProps<T> & { logValues: boolean }> & { name: string }): DecoratorFn {
|
||||
const Func: DecoratorFn = (Story) => {
|
||||
|
||||
const methods = useForm<T>(options);
|
||||
|
||||
if (options?.logValues) {
|
||||
console.log(methods.watch(options.name as any))
|
||||
}
|
||||
|
||||
return <Controller
|
||||
control={methods.control}
|
||||
name={options.name as any}
|
||||
render={({ field: { value, onChange, onBlur } }) =>
|
||||
<Story controller={{ value, onChange, onBlur }} />
|
||||
}
|
||||
/>
|
||||
}
|
||||
return Func
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export const WithModals: DecoratorFn = (Component) => <>
|
||||
|
||||
1
src/utils/validation/index.ts
Normal file
1
src/utils/validation/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './misc';
|
||||
12
src/utils/validation/misc.ts
Normal file
12
src/utils/validation/misc.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import * as yup from "yup";
|
||||
|
||||
export const imageSchema = yup.object().shape({
|
||||
id: yup.string().nullable(true),
|
||||
name: yup.string().nullable(true),
|
||||
url: yup.string().trim().required().url(),
|
||||
});
|
||||
|
||||
export const tagSchema = yup.object().shape({
|
||||
title: yup.string().trim().min(2).required(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user