Compare commits

...

7 Commits

Author SHA1 Message Date
Ephraim Duncan 66a00634eb Merge branch 'main' into feat/signing-required-field-colors 2026-05-27 13:46:33 +00:00
ephraimduncan 19326aec56 Merge remote-tracking branch 'origin/main' into pr-2726
# Conflicts:
#	packages/lib/translations/de/web.po
#	packages/lib/translations/en/web.po
#	packages/lib/translations/es/web.po
#	packages/lib/translations/fr/web.po
#	packages/lib/translations/it/web.po
#	packages/lib/translations/ja/web.po
#	packages/lib/translations/ko/web.po
#	packages/lib/translations/nl/web.po
#	packages/lib/translations/pl/web.po
#	packages/lib/translations/pt-BR/web.po
#	packages/lib/translations/zh/web.po
2026-05-14 15:45:32 +00:00
ephraimduncan d34a6eddf4 Merge branch 'main' into feat/signing-required-field-colors
Resolves conflict in document-signing-field-container.tsx (kept HEAD's
required-field-color logic) and applies biome formatting to the
auto-merged envelope-signer-page-renderer.tsx.
2026-05-12 11:03:37 +00:00
Ephraim Duncan da2d8ffa7e Merge branch 'main' into feat/signing-required-field-colors 2026-05-07 12:41:01 +00:00
ephraimduncan 58ccfd5512 fix: restore field color after validation 2026-04-23 21:45:54 +00:00
ephraimduncan 223fbef143 refactor: route required field color through recipient CSS var pipeline
- Add --recipient-red HSL var and expose as recipient.red in tailwind config
- required entry delegates to generateStyles('red'), matching the pattern
  used by every other recipient color (hover/active/combobox states, shared
  ring shadow layers)
- Gate recipient ring on !isValidating in FieldRootContainer so the
  validation ring is the sole source of truth when active
- Extend RECIPIENT_DYNAMIC_CLASS safelist via CSS_VAR_RECIPIENT_COLORS
2026-04-23 17:47:25 +00:00
ephraimduncan 4c486fc1e9 feat: highlight unsigned required fields in red on signing page
Add a 'required' key to TRecipientColor with a red ring and translucent
hover fill, mirroring the recipient color shape.

Both the signing DOM container and the envelope canvas renderer now
route unsigned required fields to 'required', falling back to the
default green once a field is inserted.

On the envelope canvas, orange is now reserved for the single 'next'
field the tooltip points at (recipientFieldsRemaining[0]). Other
unsigned required fields stay red when the pending-field tooltip
is active.
2026-04-23 16:43:48 +00:00
7 changed files with 27 additions and 10 deletions
@@ -1,5 +1,6 @@
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { FieldRootContainer } from '@documenso/ui/components/field/field';
import { getRecipientColorStyles } from '@documenso/ui/lib/recipient-colors';
@@ -117,7 +118,12 @@ export const DocumentSigningFieldContainer = ({
};
return (
<FieldRootContainer color={getRecipientColorStyles(field.fieldMeta?.readOnly ? 'readOnly' : 0)} field={field}>
<FieldRootContainer
color={getRecipientColorStyles(
field.fieldMeta?.readOnly ? 'readOnly' : isFieldUnsignedAndRequired(field) ? 'required' : 0,
)}
field={field}
>
{!field.inserted && !loading && !readOnlyField && (
<button
type="submit"
@@ -129,10 +129,14 @@ export const EnvelopeSignerPageRenderer = ({ pageData }: { pageData: PageRenderD
const fieldToRender = ZFullFieldSchema.parse(unparsedField);
const isNextPendingField = showPendingFieldTooltip && recipientFieldsRemaining[0]?.id === fieldToRender.id;
const color = fieldToRender.fieldMeta?.readOnly
? 'readOnly'
: showPendingFieldTooltip && isFieldUnsignedAndRequired(fieldToRender)
? 'orange'
: isFieldUnsignedAndRequired(fieldToRender)
? isNextPendingField
? 'orange'
: 'required'
: 'green';
const { fieldGroup } = renderField({
+3 -3
View File
@@ -1,12 +1,12 @@
{
"name": "@documenso/root",
"version": "2.11.0",
"version": "2.10.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@documenso/root",
"version": "2.11.0",
"version": "2.10.1",
"hasInstallScript": true,
"workspaces": [
"apps/*",
@@ -406,7 +406,7 @@
},
"apps/remix": {
"name": "@documenso/remix",
"version": "2.11.0",
"version": "2.10.1",
"dependencies": {
"@cantoo/pdf-lib": "^2.5.3",
"@documenso/api": "*",
+1
View File
@@ -116,6 +116,7 @@ module.exports = {
orange: 'hsl(var(--recipient-orange))',
yellow: 'hsl(var(--recipient-yellow))',
pink: 'hsl(var(--recipient-pink))',
red: 'hsl(var(--recipient-red))',
},
},
backgroundImage: {
+1 -1
View File
@@ -118,7 +118,7 @@ export function FieldRootContainer({ field, children, color, className, readonly
data-readonly={readonly ? 'true' : 'false'}
className={cn(
'field--FieldRootContainer field-card-container dark-mode-disabled group relative z-20 flex h-full w-full items-center rounded-[2px] bg-white/90 ring-2 ring-gray-200 transition-all',
color?.base,
!(isValidating && isFieldUnsignedAndRequired(field)) && color?.base,
{
'px-2': field.type !== FieldType.SIGNATURE && field.type !== FieldType.FREE_SIGNATURE,
'justify-center': !field.inserted,
+8 -3
View File
@@ -1,7 +1,7 @@
import { colord } from 'colord';
import { once } from 'remeda';
export type TRecipientColor = 'readOnly' | (typeof AVAILABLE_RECIPIENT_COLORS)[number];
export type TRecipientColor = 'readOnly' | 'required' | (typeof AVAILABLE_RECIPIENT_COLORS)[number];
export type RecipientColorStyles = {
base: string;
@@ -33,6 +33,7 @@ const RECIPIENT_COLOR_STYLES: Record<TRecipientColor, () => RecipientColorStyles
'ring-2 ring-recipient-green shadow-[0_0_0_5px_hsl(var(--recipient-green)/10%),0_0_0_2px_hsl(var(--recipient-green)/60%),0_0_0_0.5px_hsl(var(--recipient-green))]',
comboBoxItem: '',
}),
required: once(() => generateStyles('red')),
green: once(() => generateStyles('green')),
blue: once(() => generateStyles('blue')),
purple: once(() => generateStyles('purple')),
@@ -41,7 +42,7 @@ const RECIPIENT_COLOR_STYLES: Record<TRecipientColor, () => RecipientColorStyles
pink: once(() => generateStyles('pink')),
};
const generateStyles = (recipientColor: TRecipientColor): RecipientColorStyles => {
const generateStyles = (recipientColor: TCssVarRecipientColor): RecipientColorStyles => {
const { bg, border, ring, text } = CSS_PROPERTY;
const { active, hover, groupHover, groupHoverFieldItem } = CSS_VARIANT;
@@ -79,9 +80,13 @@ const CSS_VARIANT = {
const AVAILABLE_RECIPIENT_COLORS = ['green', 'blue', 'purple', 'orange', 'yellow', 'pink'] as const;
const CSS_VAR_RECIPIENT_COLORS = [...AVAILABLE_RECIPIENT_COLORS, 'red'] as const;
type TCssVarRecipientColor = (typeof CSS_VAR_RECIPIENT_COLORS)[number];
export const RECIPIENT_DYNAMIC_CLASS = {
pattern: new RegExp(
`(${Object.values(CSS_PROPERTY).join('|')})-recipient-(${AVAILABLE_RECIPIENT_COLORS.join('|')})(\\/(15|30))?$`,
`(${Object.values(CSS_PROPERTY).join('|')})-recipient-(${CSS_VAR_RECIPIENT_COLORS.join('|')})(\\/(15|30))?$`,
),
variants: Object.values(CSS_VARIANT),
};
+1
View File
@@ -55,6 +55,7 @@
--recipient-orange: 36 92% 54%;
--recipient-yellow: 51 100% 43%;
--recipient-pink: 313 65% 57%;
--recipient-red: 0 91% 71%;
/* Base - Neutral */
--new-neutral-50: 0, 0%, 96%;