# # Create a Component

This section introduces how to create a custom component through the capabilities provided by UIExtension, and uses the implementation of a Counter functionality as an example to explain the usage of UIExtension components.

## # Basic Structure of a Component

A basic component typically consists of the following parts:

1. Layout template: This defines the structure and layout of the component's view. The template syntax can be referred to the layout template.
2. Styles: The component's styles can be added using the `style` attribute or the `class` attribute, similar to HTML. Styles added using the `class` attribute require a separate css file.
3. Scripts: This contains the logic of the component. UIExtension components need to implement a new component class through integration to handle the behavior and interaction of the component.
4. Module: If a component needs to be referenced by other components, it must be assigned a name and registered in a module. For more information about modularization, please refer to Modular.

Here is an example of a basic component structure:

    /* my-component.css */
    .my-counter {
        display: flex;
    }

    // my-component.js

    const { SeniorComponentFactory } = UIExtension;
    class MyComponent extends SeniorComponentFactory.createSuperClass({
        template: `
            <div class="my-counter" @var.my_counter="$component">
                <button class="my-btn" @on.click="my_counter.increment()">+</button>
                <div class="my-viewer">@{my_counter.count}</div>
                <button class="my-btn" @on.click="my_counter.decrement()">-</button>
            </div>
        `
    }) {
        static getName() {}
        init() {
            super.init();
            this.count = 0;
        }
        increment() {
            this.count ++;
            this.digest(); // After data change, update must be triggered manually
        }
        decrement() {
            this.count --;
            this.digest(); // After data change, update must be triggered manually
        }
    }
    modular.module('custom', []).registerComponent(MyComponent);

## # Create a Simple Component

Based on the above description of the basic structure of a component, let's now create a component that displays a clock. Click the `run` button below to start the example:

    <style>
        .my-clock {
            font-size: 32px;
            border: 1px solid #ddd;
            padding: .3em;
        }
    </style>
    <html>
        <template id="layout-template-container">
            <webpdf>
                <custom:clock></custom:clock>
                <div class="fv__ui-body">
                    <viewer @touch-to-scroll></viewer>
                </div>
            </webpdf>
        </template>
    </html>
    <script>
        const { PDFUI, SeniorComponentFactory, modular, appearances: { Appearance } } = UIExtension;
        class ClockComponent extends SeniorComponentFactory.createSuperClass({
            template: `
                <div class="my-clock" @var.clock="$component">
                    @{clock.currentTime|timeformat('yyyy-MM-DD HH:mm:ss')}
                </div>
            `
        }) {
            static getName() {
                return 'clock'
            }
            init() {
                super.init();
                this.currentTime = new Date();
            }
            mounted() {
                super.mounted();
                const timerId = setInterval(() => {
                    this.currentTime = new Date();
                    this.digest();
                }, 1000);
                this.addDestroyHook(() => {
                    // This step ensures that the timer is stopped once the component is destroyed.
                    clearInterval(timerId);
                });
            }
        }
        modular.module('custom', []).registerComponent(ClockComponent);

        const CustomAppearance = Appearance.extend({
            getLayoutTemplate: function() {
                return document.getElementById('layout-template-container').innerHTML;
            }
        });
        const libPath = window.top.location.origin + '/lib';
        const pdfui = new PDFUI({
                viewerOptions: {
                    libPath: libPath,
                    jr: {
                        licenseSN: licenseSN,
                        licenseKey: licenseKey
                    },
                },
                renderTo: document.body,
                appearance: CustomAppearance
            });
    </script>

Running the example above, you will see a clock component that updates in real-time. This is just a simple component, but it demonstrates how to create and use a component, initialize and run a timer in the `init` and `mounted` lifecycle of the component, and how to reference component object properties in the component template. You can further extend and customize this example based on your needs.

## # Event Triggering and Binding in Components

Components can not only display data, but also interact with users. Parent components can also interact with child components by listening to their events. With UIExtension, we can implement event triggering and listening to achieve interactive functionality.

### # Event Triggering

To trigger an event in a component, we can use the `trigger` method. This method accepts an event name (required) and multiple data to be transmitted (optional). Here is an example:

    class DidaComponent extends SeniorComponentFactory.createSuperClass({
        template: `
            <div></div>
        `
    }) {
        static getName() {
            return 'dida'
        }
        mounted() {
            super.mounted();
            const execute = () => {
                if(this.isDestroyed) {
                    return;
                }
                this.trigger('dida', performance.now());
                requestIdleCallback(execute);
            };
            requestIdleCallback(execute);
        }
    }
    modular.module('custom', []).registerComponent(DidaComponent);

In this example, the `<dida></dida>` component triggers a `dida` event and passes a timestamp whenever it is idle.

### # Event Listening

There are two ways to listen to the events in a component. One is to use the `@on.event-name` directive, and the other is to use the `Component#on` interface. The following example will continue to use the `dida` component mentioned above to demonstrate these two usages:

    class DidaBoxComponent extends SeniorComponentFactory.createSuperClass({
        template: `<div @var.box="$component">
            <custom:dida name="dida" @on.dida="box.onDidaDirectiveEvent($args[0])"></custom:dida>
        </div>
        `
    }) {
        static getName() {
            return 'dida-box';
        }
        onDidaDirectiveEvent(time) {
            console.log('Event listened through directive was triggered', time)
        }
        mounted() {
            super.mounted();
            this.getComponentByName('dida').on('dida', time => {
                console.log('Event listened through "on" interface was triggered', time)
            })
        }
    }

### # Native DOM Event Listening

The `@on` directive can not only listen to custom events triggered by components, but also listen to native DOM events. For specific usage, please refer to the @on section.

## # Component Lifecycle

The lifecycle of a UIExtension component is relatively simple, with three commonly used methods: `init`, `mounted`, and `destroy`. As shown in the examples above, `init` and `mounted` lifecycles can be implemented by overriding the methods of the parent class. The `init` method is called when the component is constructed, and is usually used to initialize some properties. The `mounted` method is called after the component is inserted into the DOM tree, and can be used to perform some DOM operations on itself or its child components. The `destroy` method requires adding tasks to be destroyed using `addDestroyHook`. These tasks usually involve removing side effects, such as unregistering events or clearing timers. You can refer to the `ClockComponent` mentioned in the section Create a Simple Component.

### # Component Communication

We know that child components can communicate with parent components by triggering events, while parent components can communicate with child components by calling their methods. But how can components communicate with each other if they are not parent-child? The UIExtension framework also supports simple injection functionality, which allows singleton objects to be injected and enables communication between any components. The implementation of injection is very simple. Here is an example of a counter functionality:

1. First, create a `CounterService` class. The role of `CounterService` is to keep track of a `count` property that can be shared by any component:

       class CounterService {
           constructor() {
               this.count = 0;
           }
       }

2. Next, create two components: one for modifying the count and one for displaying the count. Both of these components inject the `CounterService`:

       class ModifyButtonComponent extends SeniorComponentFactory.createSuperClass({
           template: `<button @on.click="$component.onClick()"></button>`
       }) {
           static getName() {
               return 'modify';
           }
           static inject() {
               return {
                   service: CounterService
               };
           }
           createDOMElement() {
               return document.createElement('button');
           }
           init() {
               this.step = 0;
           }
           onClick() {
               this.service.count += this.step;
               this.digest();
           }
           setStep(step) {
               this.step = step;
           }
       }
       class ShowCountComponent extends SeniorComponentFactory.createSuperClass({
           template: `<span style="border: 1px solid #ddd;padding: .5em 1em; display: inline-block;">@{$component.service.count}</span>`
       }) {
           static getName() {
               return 'show-count';
           }
           static inject() {
               return {
                   service: CounterService
               };
           }
       }

Let's take a look at the final result:

    <style>
        .my-clock {
            font-size: 32px;
            border: 1px solid #ddd;
            padding: .3em;
        }
    </style>
    <html>
        <template id="layout-template-container">
            <webpdf>
                <div style="display: flex;">
                    <custom:modify @setter.step="1">Increment 1</custom:modify>
                    <custom:show-count></custom:show-count>
                    <custom:modify @setter.step="-2">Decrement 2</custom:modify>
                </div>
                <div class="fv__ui-body">
                    <viewer @touch-to-scroll></viewer>
                </div>
            </webpdf>
        </template>
    </html>
    <script>
        const { PDFUI, SeniorComponentFactory, modular, appearances: { Appearance } } = UIExtension;
        class CounterService {
            constructor() {
                this.count = 0;
            }
        }
        class ModifyButtonComponent extends SeniorComponentFactory.createSuperClass({
            template: `<button @on.click="$component.onClick()"></button>`
        }) {
            static getName() {
                return 'modify';
            }
            static inject() {
                return {
                    service: CounterService
                };
            }
            createDOMElement() {
                return document.createElement('button');
            }
            init() {
                this.step = 0;
            }
            onClick() {
                this.service.count += this.step;
                this.digest();
            }
            setStep(step) {
                this.step = step;
            }
        }
        class ShowCountComponent extends SeniorComponentFactory.createSuperClass({
            template: `<span style="border: 1px solid #ddd;padding: .5em 1em; display: inline-block;">@{$component.service.count}</span>`
        }) {
            static getName() {
                return 'show-count';
            }
            static inject() {
                return {
                    service: CounterService
                };
            }
        }
        modular.module('custom', [])
            .registerComponent(ModifyButtonComponent)
            .registerComponent(ShowCountComponent)
            ;

        const CustomAppearance = Appearance.extend({
            getLayoutTemplate: function() {
                return document.getElementById('layout-template-container').innerHTML;
            },
            // To facilitate testing, you can override this method to prevent the control from being disabled when no document is open.
            disableAll() {}
        });
        const libPath = window.top.location.origin + '/lib';
        const pdfui = new PDFUI({
                viewerOptions: {
                    libPath: libPath,
                    jr: {
                        licenseSN: licenseSN,
                        licenseKey: licenseKey
                    }
                },
                renderTo: document.body,
                appearance: CustomAppearance
        });
    </script>

In the above example, the `CounterService` class is injected into both the `ModifyButtonComponent` and `ShowCountComponent` components. This allows the two components to access and modify the `count` property of the `CounterService` instance. The `ModifyButtonComponent` component increments the count when clicked, while the `ShowCountComponent` component displays the current count value.

By injecting the `CounterService` into the two components, they share the same service instance, enabling communication between them. Any changes made to the `count` property in one component will also be reflected in the other component.

Dependency injection is a powerful feature that enables components to communicate and share data without being tightly coupled. It promotes modularity and reusability in applications.