Create persons from family view (#841)

* Allow creating new person in children table

* WIP: allow creating new person for parent

* Allow creating new person for parent

* WIP: allow creating new persons from new familiy view

* Remove select object list

* Revert changes on new family view

* Align unlink icon with text

* Fix margin around parents

* Adjust margin

* Remove parent labels
This commit is contained in:
M. Schüpbach
2025-11-25 21:15:43 +01:00
committed by GitHub
parent 6ae9cdc75b
commit db973d66c2
11 changed files with 276 additions and 98 deletions

View File

@@ -1,13 +1,13 @@
import {html} from 'lit'
import '@material/mwc-button'
import '@material/mwc-icon-button'
import {mdiGenderFemale, mdiGenderMale} from '@mdi/js'
import {GrampsjsEditableTable} from './GrampsjsEditableTable.js'
import './GrampsjsFormChildRef.js'
import {renderIcon} from '../icons.js'
import {fireEvent} from '../util.js'
import '@material/mwc-icon-button'
import '@material/mwc-button'
import {GrampsjsEditableTable} from './GrampsjsEditableTable.js'
import './GrampsjsFormChildRef.js'
import './GrampsjsFormNewChild.js'
function genderIcon(gender) {
if (gender === 'M') {
@@ -24,6 +24,7 @@ export class GrampsjsChildren extends GrampsjsEditableTable {
return {
profile: {type: Array},
highlightId: {type: String},
dialogContent: {type: String},
}
}
@@ -59,21 +60,26 @@ export class GrampsjsChildren extends GrampsjsEditableTable {
return this.edit
? html`
<mwc-icon-button
class="edit large"
icon="add_circle"
@click="${this._handleAddClick}"
class="edit"
icon="add_link"
@click="${this._handleShare}"
></mwc-icon-button>
<mwc-icon-button
class="edit"
icon="add"
@click="${this._handleAdd}"
></mwc-icon-button>
${this.dialogContent}
`
: ''
}
_handleAddClick() {
_handleShare() {
this.dialogContent = html`
<grampsjs-form-childref
new
@object:save="${this._handleChildRefSave}"
@object:cancel="${this._handleChildRefCancel}"
@object:cancel="${this._handleDialogCancel}"
.appState="${this.appState}"
objType="${this.objType}"
dialogTitle=${this._('Add existing child to family')}
@@ -82,6 +88,28 @@ export class GrampsjsChildren extends GrampsjsEditableTable {
`
}
_handleAdd() {
this.dialogContent = html`
<grampsjs-form-new-child
@object:save="${this._handleNewChildSave}"
@object:cancel="${this._handleDialogCancel}"
.appState="${this.appState}"
dialogTitle="${this._('Add a new person')}"
>
</grampsjs-form-new-child>
`
}
_handleNewChildSave(e) {
fireEvent(this, 'edit:action', {
action: 'newChild',
data: e.detail.data,
})
e.preventDefault()
e.stopPropagation()
this.dialogContent = ''
}
_handleChildRefSave(e) {
fireEvent(this, 'edit:action', {action: 'addChildRef', data: e.detail.data})
e.preventDefault()
@@ -89,7 +117,7 @@ export class GrampsjsChildren extends GrampsjsEditableTable {
this.dialogContent = ''
}
_handleChildRefCancel() {
_handleDialogCancel() {
this.dialogContent = ''
}

View File

@@ -1,11 +1,13 @@
import {html, css} from 'lit'
import {css, html} from 'lit'
import '@material/mwc-icon'
import {GrampsjsObject} from './GrampsjsObject.js'
import {ringsIcon} from '../icons.js'
import {renderPerson, fireEvent} from '../util.js'
import {fireEvent, renderPerson} from '../util.js'
import './GrampsjsFormEditFamily.js'
import './GrampsjsFormNewPerson.js'
import './GrampsjsFormPersonRef.js'
import {GrampsjsObject} from './GrampsjsObject.js'
export class GrampsjsFamily extends GrampsjsObject {
static get styles() {
@@ -13,6 +15,19 @@ export class GrampsjsFamily extends GrampsjsObject {
super.styles,
css`
:host {
.items-center {
display: flex;
align-items: center;
}
.parent {
margin-top: 1em;
margin-bottom: 1em;
}
.relationship-type {
margin-top: 2em;
}
}
`,
]
@@ -29,7 +44,11 @@ export class GrampsjsFamily extends GrampsjsObject {
renderProfile() {
return html`
<h2>${this._renderTitle()}</h2>
${this._renderFather()} ${this._renderMother()}
${this._renderParent('father')} ${this._renderParent('mother')}
<div class="relationship-type">
${this._renderRelType()} ${this._renderMarriage()}
${this._renderDivorce()}
</div>
${this.edit
? html`
<mwc-icon-button
@@ -39,10 +58,6 @@ export class GrampsjsFamily extends GrampsjsObject {
></mwc-icon-button>
`
: ''}
<p>
${this._renderRelType()} ${this._renderMarriage()}
${this._renderDivorce()}
</p>
`
}
@@ -66,12 +81,40 @@ export class GrampsjsFamily extends GrampsjsObject {
`
}
_renderFather() {
return html` <p>${renderPerson(this.data?.profile?.father || {})}</p> `
}
_renderParent(parent) {
const profile = this.data?.profile[parent]
const hasProfile = Object.keys(profile ?? {}).length > 0
_renderMother() {
return html` <p>${renderPerson(this.data?.profile?.mother || {})}</p> `
return html`
<div class="parent">
<div class="items-center">
${renderPerson(profile || {})}
${this.edit && hasProfile
? html`
<mwc-icon-button
class="edit"
icon="link_off"
@click="${e => this._handleParentChanged(e, parent)}"
></mwc-icon-button>
`
: ''}
</div>
${this.edit
? html`
<mwc-icon-button
class="edit"
icon="add_link"
@click="${() => this._handleParentShare(parent)}"
></mwc-icon-button>
<mwc-icon-button
class="edit"
icon="add"
@click="${() => this._handleAddNewParent(parent)}"
></mwc-icon-button>
`
: ''}
</div>
`
}
_renderMarriage() {
@@ -101,16 +144,41 @@ export class GrampsjsFamily extends GrampsjsObject {
`
}
_handleAddNewParent(parent) {
this.dialogContent = html`
<grampsjs-form-new-person
@object:save="${e => this._handleNewParentSave(e, parent)}"
@object:cancel="${this._handleCancelDialog}"
.appState="${this.appState}"
dialogTitle="${this._('Add a new person')}"
>
</grampsjs-form-new-person>
`
}
_handleParentShare(parent) {
this.dialogContent = html`
<grampsjs-form-personref
@object:save="${e => this._handleParentChanged(e, parent)}"
@object:cancel="${this._handleCancelDialog}"
.appState="${this.appState}"
dialogTitle="${this._('Select an existing person')}"
>
</grampsjs-form-personref>
`
}
_handleParentChanged(e, parent) {
const handle = e.detail.data?.ref ?? ''
const updatedFamily = {[`${parent}_handle`]: handle}
fireEvent(this, 'edit:action', {action: 'updateProp', data: updatedFamily})
this.dialogContent = ''
}
_handleEditFamily() {
const data = {
father_handle: this.data.father_handle,
mother_handle: this.data.mother_handle,
type: this.data?.type?.string || this.data.type,
}
const father = this.data?.extended?.father
const mother = this.data?.extended?.mother
const fatherProfile = this.data?.profile?.father
const motherProfile = this.data?.profile?.mother
this.dialogContent = html`
<grampsjs-form-edit-family
@@ -118,15 +186,25 @@ export class GrampsjsFamily extends GrampsjsObject {
@object:cancel="${this._handleCancelDialog}"
.appState="${this.appState}"
.data=${data}
.father=${father}
.mother=${mother}
.fatherProfile=${fatherProfile}
.motherProfile=${motherProfile}
>
</grampsjs-form-edit-family>
`
}
_handleNewParentSave(e, parent) {
const data = {
...e.detail.data,
parent,
}
fireEvent(this, 'edit:action', {
action: 'newParent',
data,
})
e.preventDefault()
e.stopPropagation()
this.dialogContent = ''
}
_handleSaveDetails(e) {
fireEvent(this, 'edit:action', {action: 'updateProp', data: e.detail.data})
e.preventDefault()

View File

@@ -10,69 +10,8 @@ import './GrampsjsFormSelectObjectList.js'
import {GrampsjsObjectForm} from './GrampsjsObjectForm.js'
class GrampsjsFormEditFamily extends GrampsjsObjectForm {
static get properties() {
return {
father: {type: Object},
mother: {type: Object},
fatherProfile: {type: Object},
motherProfile: {type: Object},
}
}
constructor() {
super()
this.father = {}
this.mother = {}
this.fatherProfile = {}
this.motherProfile = {}
}
renderForm() {
return html`
<h4 class="label">${this._('Father')}</h4>
<grampsjs-form-select-object-list
id="father"
objectType="person"
label="${this._('Select a person as the father')}"
.appState="${this.appState}"
.objectsInitial="${this.data.father_handle
? [
{
object_type: 'person',
object: {
...this.father,
profile: this.fatherProfile,
},
handle: this.data.father_handle,
},
]
: []}"
class="edit"
></grampsjs-form-select-object-list>
<h4 class="label">${this._('Mother')}</h4>
<grampsjs-form-select-object-list
id="mother"
objectType="person"
label="${this._('Select a person as the mother')}"
.appState="${this.appState}"
.objectsInitial="${this.data.mother_handle
? [
{
object_type: 'person',
object: {
...this.mother,
profile: this.motherProfile,
},
handle: this.data.mother_handle,
},
]
: []}"
class="edit"
></grampsjs-form-select-object-list>
<grampsjs-form-select-type
id="family-rel-type"
heading="${this._('Relationship type:').replace(':', '')}"

View File

@@ -0,0 +1,56 @@
import {html} from 'lit'
import {GrampsjsNewPersonMixin} from '../mixins/GrampsjsNewPersonMixin.js'
import {fireEvent} from '../util.js'
import './GrampsjsFormSelectType.js'
import {GrampsjsObjectForm} from './GrampsjsObjectForm.js'
export class GrampsjsFormNewChild extends GrampsjsNewPersonMixin(
GrampsjsObjectForm
) {
_handleDialogSave() {
const {frel, mrel} = this.data
const processedData = this._processedData()
const data = {
processedData,
frel,
mrel,
}
fireEvent(this, 'object:save', {data})
this._reset()
}
renderForm() {
return html`
${super.renderForm()}
<grampsjs-form-select-type
required
id="child-frel"
heading="${this._('Relationship to _Father:').replace(':', '')}"
.appState="${this.appState}"
?loadingTypes=${this.loadingTypes}
typeName="child_reference_types"
defaultValue="Birth"
.types="${this.types}"
.typesLocale="${this.typesLocale}"
>
</grampsjs-form-select-type>
<grampsjs-form-select-type
required
id="child-mrel"
heading="${this._('Relationship to _Mother:').replace(':', '')}"
.appState="${this.appState}"
?loadingTypes=${this.loadingTypes}
typeName="child_reference_types"
defaultValue="Birth"
.types="${this.types}"
.typesLocale="${this.typesLocale}"
>
</grampsjs-form-select-type>
`
}
}
window.customElements.define('grampsjs-form-new-child', GrampsjsFormNewChild)

View File

@@ -0,0 +1,21 @@
import {GrampsjsNewPersonMixin} from '../mixins/GrampsjsNewPersonMixin.js'
import {fireEvent} from '../util.js'
import './GrampsjsFormSelectType.js'
import {GrampsjsObjectForm} from './GrampsjsObjectForm.js'
export class GrampsjsFormNewPerson extends GrampsjsNewPersonMixin(
GrampsjsObjectForm
) {
_handleDialogSave() {
const processedData = this._processedData()
const data = {
processedData,
}
fireEvent(this, 'object:save', {data})
this._reset()
}
}
window.customElements.define('grampsjs-form-new-person', GrampsjsFormNewPerson)

View File

@@ -0,0 +1,25 @@
/*
Form for adding a new person reference
*/
import {html} from 'lit'
import './GrampsjsFormSelectObjectList.js'
import {GrampsjsObjectForm} from './GrampsjsObjectForm.js'
class GrampsjsFormPersonRef extends GrampsjsObjectForm {
renderForm() {
return html`
<grampsjs-form-select-object-list
fixedMenuPosition
style="min-height: 300px;"
objectType="person"
.appState="${this.appState}"
id="person-select"
label="${this._('Select')}"
class="edit"
></grampsjs-form-select-object-list>
`
}
}
window.customElements.define('grampsjs-form-personref', GrampsjsFormPersonRef)

View File

@@ -20,6 +20,7 @@ import {GrampsjsAppStateMixin} from '../mixins/GrampsjsAppStateMixin.js'
// labels for button
const btnLabel = {
person: 'Select an existing person',
place: 'Select an existing place',
source: 'Select an existing source',
media: 'Select an existing media object',

View File

@@ -165,11 +165,11 @@ export const GrampsjsNewPersonMixin = superClass =>
const handleDeath = makeHandle()
const birthString = this.translateTypeName(false, 'event_types', 'Birth')
const deathString = this.translateTypeName(false, 'event_types', 'Death')
const {birth, death, ...person} = this.data
const {birth, death, frel, mrel, ...person} = this.data
const hasBirth = birth.place || (birth?.date && !dateIsEmpty(birth.date))
const hasDeath = death.place || (death?.date && !dateIsEmpty(death.date))
if (!hasBirth && !hasDeath) {
return [person]
return [{...person, handle: handlePerson}]
}
if (!hasDeath) {
return [

View File

@@ -82,6 +82,7 @@ export const grampsStrings = [
'about',
'About',
'Above the name',
'Add a new person',
'Add a new event',
'Add a new media object',
'Add an existing person as a child of the family',
@@ -525,6 +526,7 @@ export const grampsStrings = [
'Select a file',
'Select a person as the father',
'Select a person as the mother',
'Select an existing person',
'Select an existing media object',
'Select an existing note',
'Select an existing place',

View File

@@ -214,6 +214,7 @@ export const objectIcon = {
}
export const objectTypeToEndpoint = {
object: 'objects',
person: 'people',
family: 'families',
event: 'events',

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-param-reassign */
/* eslint-disable class-methods-use-this */
import {html, css} from 'lit'
import {css, html} from 'lit'
import '@material/mwc-fab'
import '@material/mwc-icon'
@@ -304,6 +304,33 @@ export class GrampsjsViewObject extends GrampsjsView {
)
}
})
} else if (e.detail.action === 'newParent') {
const {processedData, parent} = e.detail.data
const {handle} = processedData.filter(obj => obj._class === 'Person')[0]
this._postObject(processedData, 'object').then(data => {
if ('data' in data) {
const updatedFamily = {[`${parent}_handle`]: handle}
this.updateProp(this._data, this._className, updatedFamily)
}
})
} else if (e.detail.action === 'newChild') {
const {processedData, frel, mrel} = e.detail.data
const {handle} = processedData.filter(obj => obj._class === 'Person')[0]
this._postObject(processedData, 'object').then(data => {
if ('data' in data) {
const childRefData = {
ref: handle,
frel,
mrel,
}
this.addObject(
childRefData,
this._data,
this._className,
'child_ref_list'
)
}
})
} else if (e.detail.action === 'newEvent') {
const {role, ...eventData} = e.detail.data
this._postObject(eventData, 'event').then(data => {