custom-model-property.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. /**
  2. * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { CustomPropertyDescriptor, Model } from '../../mol-model/structure';
  7. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  8. import { ValueBox } from '../../mol-util';
  9. import { CustomProperty } from './custom-property';
  10. export { CustomModelProperty }
  11. namespace CustomModelProperty {
  12. export interface Provider<Params extends PD.Params, Value> extends CustomProperty.Provider<Model, Params, Value> { }
  13. export interface ProviderBuilder<Params extends PD.Params, Value> {
  14. readonly label: string
  15. readonly descriptor: CustomPropertyDescriptor
  16. readonly defaultParams: Params
  17. readonly getParams: (data: Model) => Params
  18. readonly isApplicable: (data: Model) => boolean
  19. readonly obtain: (ctx: CustomProperty.Context, data: Model, props: PD.Values<Params>) => Promise<Value>
  20. readonly type: 'static' | 'dynamic'
  21. }
  22. export function createProvider<Params extends PD.Params, Value>(builder: ProviderBuilder<Params, Value>): CustomProperty.Provider<Model, Params, Value> {
  23. const descriptorName = builder.descriptor.name
  24. const propertyDataName = builder.type === 'static' ? '_staticPropertyData' : '_dynamicPropertyData'
  25. const get = (data: Model) => {
  26. if (!(descriptorName in data[propertyDataName])) {
  27. (data[propertyDataName][descriptorName] as CustomProperty.Container<PD.Values<Params>, Value>) = {
  28. props: { ...PD.getDefaultValues(builder.getParams(data)) },
  29. data: ValueBox.create(undefined)
  30. }
  31. }
  32. return data[propertyDataName][descriptorName] as CustomProperty.Container<PD.Values<Params>, Value>;
  33. }
  34. const set = (data: Model, props: PD.Values<Params>, value: Value | undefined) => {
  35. const property = get(data);
  36. (data[propertyDataName][descriptorName] as CustomProperty.Container<PD.Values<Params>, Value>) = {
  37. props,
  38. data: ValueBox.withValue(property.data, value)
  39. };
  40. }
  41. return {
  42. label: builder.label,
  43. descriptor: builder.descriptor,
  44. getParams: builder.getParams,
  45. defaultParams: builder.defaultParams,
  46. isApplicable: builder.isApplicable,
  47. attach: async (ctx: CustomProperty.Context, data: Model, props: Partial<PD.Values<Params>> = {}, addRef) => {
  48. if (addRef) data.customProperties.reference(builder.descriptor, true);
  49. const property = get(data)
  50. const p = PD.merge(builder.defaultParams, property.props, props)
  51. if (property.data.value && PD.areEqual(builder.defaultParams, property.props, p)) return
  52. const value = await builder.obtain(ctx, data, p)
  53. data.customProperties.add(builder.descriptor);
  54. set(data, p, value);
  55. },
  56. ref: (data: Model, add: boolean) => data.customProperties.reference(builder.descriptor, add),
  57. get: (data: Model) => get(data)?.data,
  58. set: (data: Model, props: Partial<PD.Values<Params>> = {}) => {
  59. const property = get(data)
  60. const p = PD.merge(builder.defaultParams, property.props, props)
  61. if (!PD.areEqual(builder.defaultParams, property.props, p)) {
  62. // this invalidates property.value
  63. set(data, p, undefined)
  64. }
  65. }
  66. }
  67. }
  68. }