1- import { LoadingAndErrorComponent , LoadingAnimation , Menu , MenuItem , ConfirmDialog } from '@helpwave/hightide'
1+ import { LoadingAndErrorComponent , LoadingAnimation , Menu , MenuItem , ConfirmDialog , Button } from '@helpwave/hightide'
22import { Plus } from 'lucide-react'
3- import { useMemo , useState , useEffect } from 'react'
3+ import React , { useMemo , useState , useEffect } from 'react'
44import { useTasksTranslation } from '@/i18n/useTasksTranslation'
55import { PropertyEntry } from '@/components/PropertyEntry'
66import { useGetPropertyDefinitionsQuery , FieldType , PropertyEntity } from '@/api/gql/generated'
@@ -218,6 +218,7 @@ export type PropertyListProps = {
218218 multiSelectValues ?: string [ ] | null ,
219219 } > ,
220220 onPropertyValueChange ?: ( definitionId : string , value : PropertyValue ) => void ,
221+ fullWidthAddButton ?: boolean ,
221222}
222223
223224const mapFieldTypeFromBackend = ( fieldType : FieldType ) : PropertyFieldType => {
@@ -242,7 +243,8 @@ export const PropertyList = ({
242243 subjectId,
243244 subjectType,
244245 propertyValues = [ ] ,
245- onPropertyValueChange
246+ onPropertyValueChange,
247+ fullWidthAddButton = false ,
246248} : PropertyListProps ) => {
247249 const translation = useTasksTranslation ( )
248250
@@ -397,71 +399,151 @@ export const PropertyList = ({
397399 />
398400 )
399401 } ) }
400- { availableProperties . length > 0 && (
401- < Menu < HTMLDivElement >
402- trigger = { ( { toggleOpen } , ref ) => (
403- < div
404- ref = { ref }
405- className = "flex-row-4 px-4 py-2 items-center border-2 border-dashed bg-property-title-background text-property-title-text hover:border-primary rounded-xl cursor-pointer"
406- onClick = { toggleOpen }
402+ { fullWidthAddButton && (
403+ < div className = "w-full" >
404+ { availableProperties . length > 0 ? (
405+ < Menu < HTMLButtonElement >
406+ trigger = { ( { toggleOpen } , ref ) => (
407+ < Button
408+ ref = { ref }
409+ startIcon = { < Plus size = { 20 } /> }
410+ onClick = { toggleOpen }
411+ className = "w-full"
412+ >
413+ { translation ( 'addProperty' ) }
414+ </ Button >
415+ ) }
416+ menuClassName = "min-w-[200px] p-2 "
417+ alignmentVertical = "topOutside"
407418 >
408- < Plus size = { 20 } />
409- < span > { translation ( 'addProperty' ) } </ span >
410- </ div >
411- ) }
412- menuClassName = "min-w-[200px] p-2 "
413- alignmentVertical = "topOutside"
414- >
415- { ( { close } ) => (
416- < LoadingAndErrorComponent
417- isLoading = { isLoading }
418- hasError = { isError }
419- loadingComponent = { < LoadingAnimation classname = "min-h-20" /> }
420- >
421- { availableProperties
422- . filter ( prop => ! attachedProperties . some ( attached => attached . property . id === prop . id ) )
423- . map ( property => {
424- const getDefaultValue = ( ) : PropertyValue => {
425- switch ( property . fieldType ) {
426- case 'text' :
427- return { textValue : '' }
428- case 'number' :
429- return { numberValue : undefined }
430- case 'checkbox' :
431- return { boolValue : false }
432- case 'date' :
433- case 'dateTime' :
434- return { }
435- case 'singleSelect' :
436- return property . selectData ?. options && property . selectData . options . length > 0
437- ? { singleSelectValue : property . selectData . options [ 0 ] ?. id || undefined }
438- : { }
439- case 'multiSelect' :
440- return { multiSelectValue : [ ] }
441- default :
442- return { }
419+ { ( { close } ) => (
420+ < LoadingAndErrorComponent
421+ isLoading = { isLoading }
422+ hasError = { isError }
423+ loadingComponent = { < LoadingAnimation classname = "min-h-20" /> }
424+ >
425+ { availableProperties
426+ . filter ( prop => ! attachedProperties . some ( attached => attached . property . id === prop . id ) )
427+ . map ( property => {
428+ const getDefaultValue = ( ) : PropertyValue => {
429+ switch ( property . fieldType ) {
430+ case 'text' :
431+ return { textValue : '' }
432+ case 'number' :
433+ return { numberValue : undefined }
434+ case 'checkbox' :
435+ return { boolValue : false }
436+ case 'date' :
437+ case 'dateTime' :
438+ return { }
439+ case 'singleSelect' :
440+ return property . selectData ?. options && property . selectData . options . length > 0
441+ ? { singleSelectValue : property . selectData . options [ 0 ] ?. id || undefined }
442+ : { }
443+ case 'multiSelect' :
444+ return { multiSelectValue : [ ] }
445+ default :
446+ return { }
447+ }
443448 }
444- }
445449
446- return (
447- < MenuItem
448- key = { property . id }
449- onClick = { ( ) => {
450- if ( onPropertyValueChange ) {
451- const defaultValue = getDefaultValue ( )
452- onPropertyValueChange ( property . id , defaultValue )
453- }
454- close ( )
455- } }
456- className = "rounded-md cursor-pointer"
457- >
458- { property . name }
459- </ MenuItem >
460- )
461- } ) }
462- </ LoadingAndErrorComponent >
450+ return (
451+ < MenuItem
452+ key = { property . id }
453+ onClick = { ( ) => {
454+ if ( onPropertyValueChange ) {
455+ const defaultValue = getDefaultValue ( )
456+ onPropertyValueChange ( property . id , defaultValue )
457+ }
458+ close ( )
459+ } }
460+ className = "rounded-md cursor-pointer"
461+ >
462+ { property . name }
463+ </ MenuItem >
464+ )
465+ } ) }
466+ </ LoadingAndErrorComponent >
467+ ) }
468+ </ Menu >
469+ ) : (
470+ < Button
471+ startIcon = { < Plus size = { 20 } /> }
472+ disabled = { true }
473+ className = "w-full"
474+ >
475+ { translation ( 'addProperty' ) }
476+ </ Button >
463477 ) }
464- </ Menu >
478+ </ div >
479+ ) }
480+ { ! fullWidthAddButton && (
481+ availableProperties . length > 0 && (
482+ < Menu < HTMLDivElement >
483+ trigger = { ( { toggleOpen } , ref ) => (
484+ < div
485+ ref = { ref as React . RefObject < HTMLDivElement > }
486+ className = "flex-row-4 px-4 py-2 items-center border-2 border-dashed bg-property-title-background text-property-title-text hover:border-primary rounded-xl cursor-pointer"
487+ onClick = { toggleOpen }
488+ >
489+ < Plus size = { 20 } />
490+ < span > { translation ( 'addProperty' ) } </ span >
491+ </ div >
492+ ) }
493+ menuClassName = "min-w-[200px] p-2 "
494+ alignmentVertical = "topOutside"
495+ >
496+ { ( { close } ) => (
497+ < LoadingAndErrorComponent
498+ isLoading = { isLoading }
499+ hasError = { isError }
500+ loadingComponent = { < LoadingAnimation classname = "min-h-20" /> }
501+ >
502+ { availableProperties
503+ . filter ( prop => ! attachedProperties . some ( attached => attached . property . id === prop . id ) )
504+ . map ( property => {
505+ const getDefaultValue = ( ) : PropertyValue => {
506+ switch ( property . fieldType ) {
507+ case 'text' :
508+ return { textValue : '' }
509+ case 'number' :
510+ return { numberValue : undefined }
511+ case 'checkbox' :
512+ return { boolValue : false }
513+ case 'date' :
514+ case 'dateTime' :
515+ return { }
516+ case 'singleSelect' :
517+ return property . selectData ?. options && property . selectData . options . length > 0
518+ ? { singleSelectValue : property . selectData . options [ 0 ] ?. id || undefined }
519+ : { }
520+ case 'multiSelect' :
521+ return { multiSelectValue : [ ] }
522+ default :
523+ return { }
524+ }
525+ }
526+
527+ return (
528+ < MenuItem
529+ key = { property . id }
530+ onClick = { ( ) => {
531+ if ( onPropertyValueChange ) {
532+ const defaultValue = getDefaultValue ( )
533+ onPropertyValueChange ( property . id , defaultValue )
534+ }
535+ close ( )
536+ } }
537+ className = "rounded-md cursor-pointer"
538+ >
539+ { property . name }
540+ </ MenuItem >
541+ )
542+ } ) }
543+ </ LoadingAndErrorComponent >
544+ ) }
545+ </ Menu >
546+ )
465547 ) }
466548 </ div >
467549 < ConfirmDialog
0 commit comments