issue-1445/pdf-export
Johan Girod 2021-08-26 19:11:11 +02:00
parent a3f73a551c
commit 22e6288658
14 changed files with 102 additions and 77 deletions

View File

@ -1,4 +1,3 @@
import { ThemeColorsContext } from 'Components/utils/colors'
import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting'
import { formatValue } from 'publicodes'
import React, { useContext } from 'react'
@ -7,18 +6,32 @@ import { useTranslation } from 'react-i18next'
import { animated, config, useSpring } from 'react-spring'
import { DisableAnimationContext } from './utils/DisableAnimationContext'
const ANIMATION_SPRING = config.gentle
type ChartItemBarProps = {
numberToPlot: number
unit?: string
style: React.CSSProperties
display: boolean
percentage: number
}
function ChartItemBar({ style, numberToPlot, unit }: ChartItemBarProps) {
function ChartItemBar({
numberToPlot,
unit,
display,
percentage,
}: ChartItemBarProps) {
const language = useTranslation().i18n.language
const style = useSpring({
config: config.slow,
delay: 100,
immediate: useContext(DisableAnimationContext),
from: {
flex: 0,
},
flex: display ? percentage : 0,
})
return (
<div className="distribution-chart__bar-container">
<div className="distribution-chart__bar-container ui__ print-background-force">
<animated.div className="distribution-chart__bar" style={style} />
<div
css={`
@ -65,23 +78,20 @@ export default function BarChartBranch({
const [intersectionRef, brancheInViewport] = useDisplayOnIntersecting({
threshold: 0.5,
})
const numberToPlot = brancheInViewport ? value : 0
const styles = useSpring({
config: ANIMATION_SPRING,
to: {
flex: numberToPlot / maximum,
opacity: numberToPlot ? 1 : 0,
const disableAnimation = useContext(DisableAnimationContext)
const display = disableAnimation || brancheInViewport
const style = useSpring({
from: {
opacity: 0,
},
}) as { flex: number; opacity: number } // TODO: problème avec les types de react-spring ?
return !useContext(DisableAnimationContext) ? (
<animated.div
ref={intersectionRef}
className="distribution-chart__item"
style={{ opacity: styles.opacity }}
>
immediate: disableAnimation,
opacity: display ? 1 : 0,
})
return (
<animated.div ref={intersectionRef} style={style}>
<InnerBarChartBranch
value={numberToPlot}
value={value}
display={display}
maximum={maximum}
title={title}
unit={unit}
@ -89,15 +99,6 @@ export default function BarChartBranch({
description={description}
/>
</animated.div>
) : (
<InnerBarChartBranch
value={value}
maximum={maximum}
title={title}
unit={unit}
icon={icon}
description={description}
/>
)
}
@ -106,6 +107,7 @@ type InnerBarChartBranchProps = {
icon?: string
maximum: number
description?: string
display: boolean
unit?: string
value: number
}
@ -115,12 +117,12 @@ function InnerBarChartBranch({
title,
icon,
maximum,
display,
description,
unit,
}: InnerBarChartBranchProps) {
const { color } = useContext(ThemeColorsContext)
return (
<>
<div className="distribution-chart__item">
{icon && <BranchIcon icon={icon} />}
<div className="distribution-chart__item-content">
<p className="distribution-chart__counterparts">
@ -129,14 +131,12 @@ function InnerBarChartBranch({
{description && <small>{description}</small>}
</p>
<ChartItemBar
style={{
backgroundColor: color,
flex: value / maximum,
}}
display={display}
numberToPlot={value}
percentage={value / maximum}
unit={unit}
/>
</div>
</>
</div>
)
}

View File

@ -17,6 +17,8 @@
.distribution-chart__bar {
border-radius: 0.4em;
background-color: var(--color);
min-height: 1.5em;
}

View File

@ -14,6 +14,7 @@
.payslip__container h4 {
border-bottom: 1px solid black;
color-adjust: exact !important;
margin: 0;
margin-top: 1.5em;
padding-bottom: 0.5em;

View File

@ -6,6 +6,7 @@ import { ASTNode, formatValue, ParsedRules, reduceAST } from 'publicodes'
import { RuleNode } from 'publicodes/dist/types/rule'
import { Fragment, useContext } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { ExplicableRule } from './conversation/Explicable'
import './PaySlip.css'
import { Line, SalaireBrutSection, SalaireNetSection } from './PaySlipSections'
@ -124,7 +125,10 @@ export default function PaySlip() {
return (
<Fragment key={section.dottedName}>
<h5 className="payslip__cotisationTitle">
<RuleLink dottedName={section.dottedName} />
{section.title}{' '}
<em className="ui__ print-display-none">
<ExplicableRule dottedName={section.dottedName} />
</em>
</h5>
{cotisations.map((cotisation) => (
<Cotisation key={cotisation} dottedName={cotisation} />
@ -216,14 +220,14 @@ function Cotisation({ dottedName }: { dottedName: DottedName }) {
<>
<RuleLink
dottedName={dottedName}
style={{ backgroundColor: 'var(--lightestColor)' }}
className="ui__ lighter-bg print-background-force"
/>
<span style={{ backgroundColor: 'var(--lightestColor)' }}>
<span className="ui__ lighter-bg print-background-force">
{partPatronale?.nodeValue
? formatValue(partPatronale, { displayedUnit: '€', language })
: ''}
</span>
<span style={{ backgroundColor: 'var(--lightestColor)' }}>
<span className="ui__ lighter-bg print-background-force">
{partSalariale?.nodeValue
? formatValue(partSalariale, { displayedUnit: '€', language })
: ''}

View File

@ -242,7 +242,7 @@ function TargetInputOrValue({
)}
</span>
{(isActive || isFocused) && (
<div style={{ minWidth: '100%' }}>
<div style={{ minWidth: '100%' }} className="ui__ print-display-none">
<FromTop>
<div css="display: flex; justify-content: flex-end; margin-bottom: -0.4rem">
<InputSuggestions

View File

@ -4,14 +4,14 @@ import { Trans } from 'react-i18next'
export default function ExportRecover() {
return (
<section className="screen-display-none print-break-avoid">
<h2>
<Trans i18nKey="pages.simulateurs.print-info.title">
Vous souhaitez retrouver cette simulation ?
</Trans>
</h2>
<section className="ui__ screen-display-none print-break-avoid notice">
<p>
<strong>
<Trans i18nKey="pages.simulateurs.print-info.title">
Vous souhaitez retrouver cette simulation ?
</Trans>
</strong>
<Trans i18nKey="pages.simulateurs.print-info.recover">
Retrouvez la, ainsi que d'autres outils d'aide à la création et à la
gestion d'entreprise, sur{' '}

View File

@ -1,4 +1,5 @@
import Value, { Condition } from 'Components/EngineValue'
import { FromBottom } from 'Components/ui/animate'
import Emoji from 'Components/utils/Emoji'
import { useEngine } from 'Components/utils/EngineContext'
import assuranceMaladieSrc from 'Images/assurance-maladie.svg'

View File

@ -3,7 +3,6 @@ import PaySlip from 'Components/PaySlip'
import StackedBarChart from 'Components/StackedBarChart'
import { FromTop } from 'Components/ui/animate'
import { ThemeColorsContext } from 'Components/utils/colors'
import Emoji from 'Components/utils/Emoji'
import { useInversionFail } from 'Components/utils/EngineContext'
import { useContext, useRef } from 'react'
import emoji from 'react-easy-emoji'
@ -108,7 +107,7 @@ function RevenueRepartitionSection(props: { onSeePayslip: () => void }) {
function PaySlipSection() {
return (
<section className="ui__ print-break-avoid">
<section>
<h2>
<Trans>Fiche de paie</Trans>
</h2>
@ -129,11 +128,12 @@ export const DistributionSection = ({
{children}
<p className="ui__ notice">
<Trans>
<Emoji emoji="" /> Pour en savoir plus, rendez-vous sur le site{' '}
Pour en savoir plus, rendez-vous sur le site{' '}
<a href="https://www.aquoiserventlescotisations.urssaf.fr/">
aquoiserventlescotisations.urssaf.fr
</a>
</Trans>
</p>
<div className="ui__ print-break-after" />
</section>
)

View File

@ -7,6 +7,13 @@ html {
font-size: 0.9em;
}
}
@media print {
html {
line-height: 1.4rem;
font-size: 0.9em;
}
}
@media (min-width: 500px) and (max-width: 1920px) {
html {
font-size: 1.1em;
@ -18,12 +25,6 @@ html {
}
}
@media print {
html {
font-size: 1em;
}
}
body {
font-weight: 400;
color: #040e19;

View File

@ -209,7 +209,7 @@ input.ui__::placeholder {
color: grey;
}
@media screen {
.screen-display-none {
.ui__.screen-display-none {
display: none !important;
}
}
@ -220,14 +220,15 @@ input.ui__::placeholder {
}
.ui__.print-break-avoid {
page-break-inside: avoid;
break-inside: avoid;
}
.ui__.print-break-after {
break-after: always;
}
.ui__.print-background-force {
color-adjust: exact !important;
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
.ui__.toggle input[type='radio']:checked ~ * {

View File

@ -1,11 +1,7 @@
import React, { createContext, useCallback, useEffect, useState } from 'react'
export const DisableAnimationContext = createContext(false)
export function DisableAnimationOnPrintProvider({
children,
}: {
children: React.ReactNode
}) {
export const useIsPrintContext = () => {
const [isPrintContext, setPrintContext] = useState(false)
const onPrintContextChange = useCallback(
(matchMedia: MediaQueryListEvent | MediaQueryList) => {
@ -22,6 +18,24 @@ export function DisableAnimationOnPrintProvider({
}
}, [onPrintContextChange])
// Fix for Firefox (see https://bugzilla.mozilla.org/show_bug.cgi?id=774398)
useEffect(() => {
window.onbeforeprint = () => setPrintContext(true)
return () => {
window.onbeforeprint = null
}
}, [setPrintContext])
return isPrintContext
}
export function DisableAnimationOnPrintProvider({
children,
}: {
children: React.ReactNode
}) {
const isPrintContext = useIsPrintContext()
console.log(isPrintContext)
return (
<DisableAnimationContext.Provider value={isPrintContext}>
{children}

View File

@ -1,4 +1,5 @@
import { useEffect, useRef, useState } from 'react'
import { useIsPrintContext } from './DisableAnimationContext'
export default function ({
root = null,
@ -41,7 +42,8 @@ export default function ({
return () => {
node && unobserve && observer.unobserve(node)
}
}, [root, rootMargin, threshold, ref.current])
}, [root, rootMargin, threshold, unobserve])
return [ref, wasOnScreen]
const isPrintContext = useIsPrintContext()
return [ref, isPrintContext || wasOnScreen]
}

View File

@ -1,6 +1,5 @@
import { Condition } from 'Components/EngineValue'
import PreviousSimulationBanner from 'Components/PreviousSimulationBanner'
import ExportRecover from 'Components/simulationExplanation/ExportRecover'
import { ThemeColorsProvider } from 'Components/utils/colors'
import { IsEmbeddedContext } from 'Components/utils/embeddedContext'
import Emoji from 'Components/utils/Emoji'
@ -87,10 +86,10 @@ export default function PageData({
<ThemeColorsProvider color={inIframe ? undefined : meta?.color}>
<Component />
{config && <PreviousSimulationBanner />}
<div className="ui__ print-break-after" />
{!inIframe && (
<>
{seoExplanations}
<ExportRecover></ExportRecover>
<NextSteps
iframePath={privateIframe ? undefined : iframePath}
nextSteps={nextSteps}

View File

@ -139,7 +139,7 @@ export function getSimulatorsData({
shortName: t('pages.simulateurs.salarié.shortname', 'Salarié'),
seoExplanations: (
<Trans i18nKey="pages.simulateurs.salarié.seo">
<section className="ui__ print-break-avoid">
<section>
<h2>Comment calculer le salaire net ?</h2>
<p>
Lors de l'entretien d'embauche l'employeur propose en général une
@ -256,7 +256,7 @@ export function getSimulatorsData({
),
seoExplanations: (
<Trans i18nKey="pages.simulateurs.ei.seo explanation">
<section className="ui__ print-break-avoid">
<section>
<h2>
Comment calculer le revenu net d'un dirigeant d'entreprise
individuelle (EI) ?
@ -398,7 +398,7 @@ export function getSimulatorsData({
},
seoExplanations: (
<Trans i18nKey="pages.simulateurs.sasu.seo-explanation">
<section className="ui__ print-break-avoid">
<section>
<h2>Comment calculer le salaire d'un dirigeant de SASU ? </h2>
<p>
Comme pour un salarié classique, le{' '}
@ -516,7 +516,7 @@ export function getSimulatorsData({
),
seoExplanations: (
<Trans i18nKey="pages.simulateurs.auto-entrepreneur.seo explanation">
<section className="ui__ print-break-avoid">
<section>
<h2>Comment calculer le revenu net d'un auto-entrepreneur ?</h2>
<p>
Un auto-entrepreneur doit payer des cotisations et contributions
@ -673,7 +673,7 @@ export function getSimulatorsData({
),
seoExplanations: (
<Trans i18nKey="pages.simulateurs.chômage-partiel.seo">
<section className="ui__ print-break-avoid">
<section>
<h2>Comment calculer l'indemnité d'activité partielle ?</h2>
<p>
L'indemnité d'activité partielle de base est fixée par la loi à{' '}
@ -1077,7 +1077,7 @@ export function getSimulatorsData({
component: ISSimulation,
seoExplanations: (
<Trans i18nKey="pages.simulateurs.is.seo">
<section className="ui__ print-break-avoid">
<section>
<h2>Comment est calculé limpôt sur les sociétés ?</h2>
<p>
Limpôt sur les sociétés sapplique aux bénéfices réalisés par les