Merge pull request #3811 from tchak/add-champ-after
Insérer les nouveaux types de champs après le type de champ visible à l'écran
This commit is contained in:
commit
012bbfb1d8
7 changed files with 89 additions and 19 deletions
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { sortableElement, sortableHandle } from 'react-sortable-hoc';
|
import { sortableElement, sortableHandle } from 'react-sortable-hoc';
|
||||||
|
import { useInView } from 'react-intersection-observer';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
import DescriptionInput from './DescriptionInput';
|
import DescriptionInput from './DescriptionInput';
|
||||||
|
@ -29,6 +30,10 @@ const TypeDeChamp = sortableElement(
|
||||||
const canBeMandatory =
|
const canBeMandatory =
|
||||||
!isHeaderSection && !isExplication && !state.isAnnotation;
|
!isHeaderSection && !isExplication && !state.isAnnotation;
|
||||||
|
|
||||||
|
const [ref, inView] = useInView({
|
||||||
|
threshold: [0.6]
|
||||||
|
});
|
||||||
|
|
||||||
const updateHandlers = createUpdateHandlers(
|
const updateHandlers = createUpdateHandlers(
|
||||||
dispatch,
|
dispatch,
|
||||||
typeDeChamp,
|
typeDeChamp,
|
||||||
|
@ -42,8 +47,10 @@ const TypeDeChamp = sortableElement(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={isLastItem ? state.lastTypeDeChampRef : null}
|
ref={ref}
|
||||||
data-index={index}
|
data-index={index}
|
||||||
|
data-in-view={inView ? true : undefined}
|
||||||
|
data-repetition={isRepetition ? true : undefined}
|
||||||
className={`type-de-champ form flex column justify-start ${
|
className={`type-de-champ form flex column justify-start ${
|
||||||
isHeaderSection ? 'type-header-section' : ''
|
isHeaderSection ? 'type-header-section' : ''
|
||||||
}`}
|
}`}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useReducer, useRef } from 'react';
|
import React, { useReducer } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
|
@ -11,11 +11,7 @@ function TypeDeChampRepetitionOptions({
|
||||||
state: parentState,
|
state: parentState,
|
||||||
typeDeChamp
|
typeDeChamp
|
||||||
}) {
|
}) {
|
||||||
const lastTypeDeChampRef = useRef(null);
|
const [state, dispatch] = useReducer(typeDeChampsReducer, parentState);
|
||||||
const [state, dispatch] = useReducer(typeDeChampsReducer, {
|
|
||||||
...parentState,
|
|
||||||
lastTypeDeChampRef
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useReducer, useRef } from 'react';
|
import React, { useReducer } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
|
@ -7,10 +7,8 @@ import TypeDeChamp from './TypeDeChamp';
|
||||||
import typeDeChampsReducer from '../typeDeChampsReducer';
|
import typeDeChampsReducer from '../typeDeChampsReducer';
|
||||||
|
|
||||||
function TypeDeChamps({ state: rootState, typeDeChamps }) {
|
function TypeDeChamps({ state: rootState, typeDeChamps }) {
|
||||||
const lastTypeDeChampRef = useRef(null);
|
|
||||||
const [state, dispatch] = useReducer(typeDeChampsReducer, {
|
const [state, dispatch] = useReducer(typeDeChampsReducer, {
|
||||||
...rootState,
|
...rootState,
|
||||||
lastTypeDeChampRef,
|
|
||||||
typeDeChamps
|
typeDeChamps
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -37,30 +37,52 @@ export default function typeDeChampsReducer(state, { type, params, done }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNewTypeDeChamp(state, typeDeChamps, done) {
|
function addTypeDeChamp(state, typeDeChamps, insertAfter, done) {
|
||||||
const typeDeChamp = {
|
const typeDeChamp = {
|
||||||
...state.defaultTypeDeChampAttributes,
|
...state.defaultTypeDeChampAttributes,
|
||||||
order_place: typeDeChamps.length
|
order_place: typeDeChamps.length
|
||||||
};
|
};
|
||||||
|
|
||||||
createTypeDeChampOperation(typeDeChamp, state.queue)
|
createTypeDeChampOperation(typeDeChamp, state.queue)
|
||||||
.then(() => {
|
.then(async () => {
|
||||||
|
if (insertAfter) {
|
||||||
|
// Move the champ to the correct position server-side
|
||||||
|
await moveTypeDeChampOperation(
|
||||||
|
typeDeChamp,
|
||||||
|
insertAfter.index,
|
||||||
|
state.queue
|
||||||
|
);
|
||||||
|
}
|
||||||
state.flash.success();
|
state.flash.success();
|
||||||
done();
|
done();
|
||||||
if (state.lastTypeDeChampRef) {
|
if (insertAfter) {
|
||||||
scrollToComponent(state.lastTypeDeChampRef.current);
|
scrollToComponent(insertAfter.target.nextElementSibling);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(message => state.flash.error(message));
|
.catch(message => state.flash.error(message));
|
||||||
|
|
||||||
|
let newTypeDeChamps = [...typeDeChamps, typeDeChamp];
|
||||||
|
if (insertAfter) {
|
||||||
|
// Move the champ to the correct position client-side
|
||||||
|
newTypeDeChamps = arrayMove(
|
||||||
|
newTypeDeChamps,
|
||||||
|
typeDeChamps.length,
|
||||||
|
insertAfter.index
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
typeDeChamps: [...typeDeChamps, typeDeChamp]
|
typeDeChamps: newTypeDeChamps
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addNewTypeDeChamp(state, typeDeChamps, done) {
|
||||||
|
return addTypeDeChamp(state, typeDeChamps, findItemToInsertAfter(), done);
|
||||||
|
}
|
||||||
|
|
||||||
function addNewRepetitionTypeDeChamp(state, typeDeChamps, typeDeChamp, done) {
|
function addNewRepetitionTypeDeChamp(state, typeDeChamps, typeDeChamp, done) {
|
||||||
return addNewTypeDeChamp(
|
return addTypeDeChamp(
|
||||||
{
|
{
|
||||||
...state,
|
...state,
|
||||||
defaultTypeDeChampAttributes: {
|
defaultTypeDeChampAttributes: {
|
||||||
|
@ -69,6 +91,7 @@ function addNewRepetitionTypeDeChamp(state, typeDeChamps, typeDeChamp, done) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
typeDeChamps,
|
typeDeChamps,
|
||||||
|
null,
|
||||||
done
|
done
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -182,3 +205,23 @@ function getUpdateHandler(typeDeChamp, { queue, flash }) {
|
||||||
}
|
}
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findItemToInsertAfter() {
|
||||||
|
const target = getFirstTarget();
|
||||||
|
|
||||||
|
return {
|
||||||
|
target,
|
||||||
|
index: parseInt(target.dataset.index) + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFirstTarget() {
|
||||||
|
const [target] = document.querySelectorAll('[data-in-view]');
|
||||||
|
if (target) {
|
||||||
|
const parentTarget = target.closest('[data-repetition]');
|
||||||
|
if (parentTarget) {
|
||||||
|
return parentTarget;
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,3 +5,7 @@ import '@babel/polyfill';
|
||||||
import 'dom4';
|
import 'dom4';
|
||||||
import './polyfills/insertAdjacentElement';
|
import './polyfills/insertAdjacentElement';
|
||||||
import './polyfills/dataset';
|
import './polyfills/dataset';
|
||||||
|
|
||||||
|
if (typeof window.IntersectionObserver === 'undefined') {
|
||||||
|
import('intersection-observer');
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"debounce": "^1.2.0",
|
"debounce": "^1.2.0",
|
||||||
"dom4": "^2.1.4",
|
"dom4": "^2.1.4",
|
||||||
"highcharts": "^6.1.2",
|
"highcharts": "^6.1.2",
|
||||||
|
"intersection-observer": "^0.6.0",
|
||||||
"jquery": "^3.4.1",
|
"jquery": "^3.4.1",
|
||||||
"leaflet": "^1.4.0",
|
"leaflet": "^1.4.0",
|
||||||
"leaflet-freedraw": "^2.10.0",
|
"leaflet-freedraw": "^2.10.0",
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
"ramda": "=0.24.1",
|
"ramda": "=0.24.1",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
|
"react-intersection-observer": "^8.23.0",
|
||||||
"react-scroll-to-component": "^1.0.2",
|
"react-scroll-to-component": "^1.0.2",
|
||||||
"react-sortable-hoc": "^1.7.1",
|
"react-sortable-hoc": "^1.7.1",
|
||||||
"react_ujs": "^2.5.0",
|
"react_ujs": "^2.5.0",
|
||||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -688,6 +688,13 @@
|
||||||
"@babel/plugin-transform-react-jsx-self" "^7.0.0"
|
"@babel/plugin-transform-react-jsx-self" "^7.0.0"
|
||||||
"@babel/plugin-transform-react-jsx-source" "^7.0.0"
|
"@babel/plugin-transform-react-jsx-source" "^7.0.0"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.0.0":
|
||||||
|
version "7.4.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d"
|
||||||
|
integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
"@babel/runtime@^7.2.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2":
|
"@babel/runtime@^7.2.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2":
|
||||||
version "7.4.3"
|
version "7.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc"
|
||||||
|
@ -4166,9 +4173,14 @@ internal-ip@^4.2.0:
|
||||||
ipaddr.js "^1.9.0"
|
ipaddr.js "^1.9.0"
|
||||||
|
|
||||||
interpret@^1.1.0:
|
interpret@^1.1.0:
|
||||||
version "1.2.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
|
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
|
||||||
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
|
integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=
|
||||||
|
|
||||||
|
intersection-observer@^0.6.0:
|
||||||
|
version "0.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.6.0.tgz#d64aae04211b4cec051537168f5fa670a4acc770"
|
||||||
|
integrity sha512-WUVAqGJr08yh73XKe1JhylQ9BiBIytrkt8SH5Knu7Uy44ij5cICi6PbVLIbV/D2eIx9LJVkGBo9WF80R4VXJ+w==
|
||||||
|
|
||||||
invariant@^2.2.2, invariant@^2.2.4:
|
invariant@^2.2.2, invariant@^2.2.4:
|
||||||
version "2.2.4"
|
version "2.2.4"
|
||||||
|
@ -6737,6 +6749,14 @@ react-dom@^16.8.6:
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
scheduler "^0.13.6"
|
scheduler "^0.13.6"
|
||||||
|
|
||||||
|
react-intersection-observer@^8.23.0:
|
||||||
|
version "8.23.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-8.23.0.tgz#1533aaf39cc70300ff8ca37e6551d2d68a589cd0"
|
||||||
|
integrity sha512-kHXfxhGKvVDNkrvmh9CKCnAWvJBigyB7oSDzMXL54weFDwwI4WfTr58YauZ0RRPkGzoD/hYEuzfS1wipXn23fA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.0.0"
|
||||||
|
invariant "^2.2.4"
|
||||||
|
|
||||||
react-is@^16.8.1:
|
react-is@^16.8.1:
|
||||||
version "16.8.6"
|
version "16.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
||||||
|
|
Loading…
Add table
Reference in a new issue