xref: /aosp_15_r20/external/perfetto/ui/src/base/mithril_utils.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2023 The Android Open Source Project
2*6dbdd20aSAndroid Build Coastguard Worker//
3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*6dbdd20aSAndroid Build Coastguard Worker//
7*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*6dbdd20aSAndroid Build Coastguard Worker//
9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard Workerimport m from 'mithril';
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Worker// Check if a mithril component vnode has children
18*6dbdd20aSAndroid Build Coastguard Workerexport function hasChildren<T>({children}: m.Vnode<T>): boolean {
19*6dbdd20aSAndroid Build Coastguard Worker  return (
20*6dbdd20aSAndroid Build Coastguard Worker    Array.isArray(children) &&
21*6dbdd20aSAndroid Build Coastguard Worker    children.length > 0 &&
22*6dbdd20aSAndroid Build Coastguard Worker    children.some((value) => value)
23*6dbdd20aSAndroid Build Coastguard Worker  );
24*6dbdd20aSAndroid Build Coastguard Worker}
25*6dbdd20aSAndroid Build Coastguard Worker
26*6dbdd20aSAndroid Build Coastguard Worker// A component which simply passes through it's children.
27*6dbdd20aSAndroid Build Coastguard Worker// Can be used for having something to attach lifecycle hooks to without having
28*6dbdd20aSAndroid Build Coastguard Worker// to add an extra HTML element to the DOM.
29*6dbdd20aSAndroid Build Coastguard Workerexport const Passthrough = {
30*6dbdd20aSAndroid Build Coastguard Worker  view({children}: m.VnodeDOM) {
31*6dbdd20aSAndroid Build Coastguard Worker    return children;
32*6dbdd20aSAndroid Build Coastguard Worker  },
33*6dbdd20aSAndroid Build Coastguard Worker};
34*6dbdd20aSAndroid Build Coastguard Worker
35*6dbdd20aSAndroid Build Coastguard Workerexport interface GateAttrs {
36*6dbdd20aSAndroid Build Coastguard Worker  open: boolean;
37*6dbdd20aSAndroid Build Coastguard Worker}
38*6dbdd20aSAndroid Build Coastguard Worker
39*6dbdd20aSAndroid Build Coastguard Worker// The gate component is a wrapper which can either be open or closed.
40*6dbdd20aSAndroid Build Coastguard Worker// - When open, children are rendered inside a div where display = contents.
41*6dbdd20aSAndroid Build Coastguard Worker// - When closed, children are rendered inside a div where display = none
42*6dbdd20aSAndroid Build Coastguard Worker// Use this component when we want to conditionally render certain children,
43*6dbdd20aSAndroid Build Coastguard Worker// but we want to maintain their state.
44*6dbdd20aSAndroid Build Coastguard Workerexport const Gate = {
45*6dbdd20aSAndroid Build Coastguard Worker  view({attrs, children}: m.VnodeDOM<GateAttrs>) {
46*6dbdd20aSAndroid Build Coastguard Worker    return m(
47*6dbdd20aSAndroid Build Coastguard Worker      '',
48*6dbdd20aSAndroid Build Coastguard Worker      {
49*6dbdd20aSAndroid Build Coastguard Worker        style: {display: attrs.open ? 'contents' : 'none'},
50*6dbdd20aSAndroid Build Coastguard Worker      },
51*6dbdd20aSAndroid Build Coastguard Worker      children,
52*6dbdd20aSAndroid Build Coastguard Worker    );
53*6dbdd20aSAndroid Build Coastguard Worker  },
54*6dbdd20aSAndroid Build Coastguard Worker};
55*6dbdd20aSAndroid Build Coastguard Worker
56*6dbdd20aSAndroid Build Coastguard Worker/**
57*6dbdd20aSAndroid Build Coastguard Worker * Utility function to pre-bind some mithril attrs of a component, and leave
58*6dbdd20aSAndroid Build Coastguard Worker * the others unbound and passed at run-time.
59*6dbdd20aSAndroid Build Coastguard Worker * Example use case: the Page API Passes to the registered page a PageAttrs,
60*6dbdd20aSAndroid Build Coastguard Worker * which is {subpage:string}. Imagine you write a MyPage component that takes
61*6dbdd20aSAndroid Build Coastguard Worker * some extra input attrs (e.g. the App object) and you want to bind them
62*6dbdd20aSAndroid Build Coastguard Worker * onActivate(). The results looks like this:
63*6dbdd20aSAndroid Build Coastguard Worker *
64*6dbdd20aSAndroid Build Coastguard Worker * interface MyPageAttrs extends PageAttrs { app: App; }
65*6dbdd20aSAndroid Build Coastguard Worker *
66*6dbdd20aSAndroid Build Coastguard Worker * class MyPage extends m.classComponent<MyPageAttrs> {... view() {...} }
67*6dbdd20aSAndroid Build Coastguard Worker *
68*6dbdd20aSAndroid Build Coastguard Worker * onActivate(app: App) {
69*6dbdd20aSAndroid Build Coastguard Worker *   pages.register(... bindMithrilApps(MyPage, {app: app});
70*6dbdd20aSAndroid Build Coastguard Worker * }
71*6dbdd20aSAndroid Build Coastguard Worker *
72*6dbdd20aSAndroid Build Coastguard Worker * The return value of bindMithrilApps is a mithril component that takes in
73*6dbdd20aSAndroid Build Coastguard Worker * input only a {subpage: string} and passes down to MyPage the combination
74*6dbdd20aSAndroid Build Coastguard Worker * of pre-bound and runtime attrs, that is {subpage, app}.
75*6dbdd20aSAndroid Build Coastguard Worker */
76*6dbdd20aSAndroid Build Coastguard Workerexport function bindMithrilAttrs<BaseAttrs, Attrs>(
77*6dbdd20aSAndroid Build Coastguard Worker  component: m.ComponentTypes<Attrs>,
78*6dbdd20aSAndroid Build Coastguard Worker  boundArgs: Omit<Attrs, keyof BaseAttrs>,
79*6dbdd20aSAndroid Build Coastguard Worker): m.Component<BaseAttrs> {
80*6dbdd20aSAndroid Build Coastguard Worker  return {
81*6dbdd20aSAndroid Build Coastguard Worker    view(vnode: m.Vnode<BaseAttrs>) {
82*6dbdd20aSAndroid Build Coastguard Worker      const attrs = {...vnode.attrs, ...boundArgs} as Attrs;
83*6dbdd20aSAndroid Build Coastguard Worker      const emptyAttrs: m.CommonAttributes<Attrs, {}> = {}; // Keep tsc happy.
84*6dbdd20aSAndroid Build Coastguard Worker      return m<Attrs, {}>(component, {...attrs, ...emptyAttrs});
85*6dbdd20aSAndroid Build Coastguard Worker    },
86*6dbdd20aSAndroid Build Coastguard Worker  };
87*6dbdd20aSAndroid Build Coastguard Worker}
88