{/* prettier-ignore */}
{/*
Copyright 2023 Comcast Cable Communications Management, LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

SPDX-License-Identifier: Apache-2.0
\*/}

import { Meta } from '@storybook/blocks';
import * as withThemeStylesStories from './withThemeStyles.stories';

<Meta of={withThemeStylesStories} />

# withThemeStyles

Use `withThemeStyles` to create composable components that can be easily themed and decoupled from functionality.

The `withThemeStyles` mixin provides flexibility with _composability_: the concept of combining the properties of multiple components to create a new component. This is not intended to be a replacement for building a component API, but rather a tool for augmentation.

## Usage

Here is an example of applying `withThemeStyles`. Start with an un-styled `Box` component containing a defined width and height.

```js
import lng from '@lightningjs/core';

class Box extends lng.Component {
  static _template() {
    return {
      w: 150,
      h: 150,
      rect: true
    };
  }
}
```

In order to re-use this `Box` to create a new component called `BlueBox`, we can extend the base class.

```js
class BlueBox extends Box {
  static _template() {
    return {
      ...super._template(),
      color: 0xff0000ff
    };
  }
}
```

This is fine if you want a box that is always blue, but what if you want to change the color of your component without having to extend it and override that single value? Or if you want a box that changes based on certain factors, such as its [mode](?path=/docs/docs-theming-modes--docs)? Or what if you want to have multiple color palettes (in LUI, these are called [tones](?path=/docs/docs-theming-tones--docs)) that you can switch between depending on if your application is in light or dark mode?

By utilizing `withThemeStyles`, you can tie the color of your box to the tokens used in your theme or a single style object. The preferred way of accomplishing this is by extending the `Base` component, which utilizes `withThemeStyles` and saves you from additional setup.

If you are not extending the `Base` component, you can style your component in the following ways.

### Defining a Style Object

The first way of handling styles is to wrap your component in the `withThemeStyles` mixin, and then define the `__themeStyle` getter.

```js
const style = {
  color: 0xff0000ff
};

class Box extends withThemeStyles(lng.Component) {
  static get __componentName() {
    return 'Box';
  }

  static get __themeStyle() {
    return style;
  }

  static _template() {
    return {
      w: 150,
      h: 150,
      rect: true
    };
  }

  _update() {
    this.color = this.style.color;
  }
}
```

With this setup, the `Box` component's `color` property is mapped to a style value external to the `Box` class. This enables us to change the value of color externally, without the need to change any code in the `Box` class. Thus, the class can remain focused on the functional aspects of the component, and all style-related properties can be handled elsewhere.

It is worth noting that this method ensures all instances of your component are styled the same. If you need to style individual instances differently, another method is recommended.

### Passing In a Style Object

The second option is to wrap your component in `withThemeStyles` and to pass in your style object as the second argument.

This method is usually more appropriate for in-line styling, rather than defining the styles of a new component. This is because you rely on the component already being set up for use with theming, and this method will only apply the given styles to that particular instance of your component. (We advise changes to be made to all instances of a component when possible to ensure proper adherence to your design system.)

```js
const style = {
  color: 0xff0000ff
};

class Box extends lng.Component {
  static get __componentName() {
    return 'Box';
  }

  static _template() {
    return {
      w: 150,
      h: 150,
      rect: true
    };
  }

  _update() {
    this.color = this.style.color;
  }
}

class NewComponent extends lng.Component {
  _template() {
    Box: {
      type: withThemeStyles(Box, style);
    }
  }
}
```

In both cases, you'll notice that there is a `__componentName` getter in our `Box` class. This name is what is used to reference our component for any overrides and extension assignments that might exist in our theme's `componentConfig`.

### Mode Changes

Instead of extending a component to access the focus or unfocus methods of a component, or to add any custom functionality for a specific mode, you can tap into "on" methods for mode changes. Similarly to the `on[KeyName]` methods exposed via the [withHandleKey](?path=/docs/utilities-withhandlekey--docs) mixin, all mode changes are linked to `on[ModeName]` methods, which are triggered when the `mode` of the component is set to a new value.

For example, if a component has an `unfocused`, `focused`, and `disabled` mode, the `onUnfocused`, `onFocused`, and `onDisabled` methods will be called on change to the associated mode. Any custom modes beyond these three will also be added to the method list. When using a component, all of those methods can be accessed and set like other component properties, like this:

```js
class NewComponent extends lng.Component {
  _template() {
    Box: {
      type: withThemeStyles(Box, style),
      onFocused: () => { console.log('mode changed to focused!'); }
    }
  }
}
```

For more information on all of the ways you can set up your styles to fully utilize this mixin, read through the [Theming Documentation](?path=/docs/docs-theming-overview--docs).
