mirror of
https://github.com/home-assistant/frontend.git
synced 2025-10-13 05:39:54 +00:00
Compare commits
2 Commits
power
...
improve_ar
Author | SHA1 | Date | |
---|---|---|---|
![]() |
37b79c67c9 | ||
![]() |
0715835e0d |
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -11,7 +11,7 @@ body:
|
|||||||
|
|
||||||
**Please do not report issues for custom cards.**
|
**Please do not report issues for custom cards.**
|
||||||
|
|
||||||
[fr]: https://github.com/orgs/home-assistant/discussions
|
[fr]: https://github.com/home-assistant/frontend/discussions
|
||||||
[releases]: https://github.com/home-assistant/home-assistant/releases
|
[releases]: https://github.com/home-assistant/home-assistant/releases
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
@@ -108,9 +108,9 @@ body:
|
|||||||
render: yaml
|
render: yaml
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: JavaScript errors shown in your browser console/inspector
|
label: Javascript errors shown in your browser console/inspector
|
||||||
description: >
|
description: >
|
||||||
If you come across any JavaScript or other error logs, e.g., in your
|
If you come across any Javascript or other error logs, e.g., in your
|
||||||
browser console/inspector please provide them.
|
browser console/inspector please provide them.
|
||||||
render: txt
|
render: txt
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Request a feature for the UI / Dashboards
|
- name: Request a feature for the UI / Dashboards
|
||||||
url: https://github.com/orgs/home-assistant/discussions
|
url: https://github.com/home-assistant/frontend/discussions/category_choices
|
||||||
about: Request a new feature for the Home Assistant frontend.
|
about: Request a new feature for the Home Assistant frontend.
|
||||||
- name: Report a bug that is NOT related to the UI / Dashboards
|
- name: Report a bug that is NOT related to the UI / Dashboards
|
||||||
url: https://github.com/home-assistant/core/issues
|
url: https://github.com/home-assistant/core/issues
|
||||||
|
53
.github/ISSUE_TEMPLATE/task.yml
vendored
53
.github/ISSUE_TEMPLATE/task.yml
vendored
@@ -1,53 +0,0 @@
|
|||||||
name: Task
|
|
||||||
description: For staff only - Create a task
|
|
||||||
type: Task
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
## ⚠️ RESTRICTED ACCESS
|
|
||||||
|
|
||||||
**This form is restricted to Open Home Foundation staff and authorized contributors only.**
|
|
||||||
|
|
||||||
If you are a community member wanting to contribute, please:
|
|
||||||
- For bug reports: Use the [bug report form](https://github.com/home-assistant/frontend/issues/new?template=bug_report.yml)
|
|
||||||
- For feature requests: Submit to [Feature Requests](https://github.com/orgs/home-assistant/discussions)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### For authorized contributors
|
|
||||||
|
|
||||||
Use this form to create tasks for development work, improvements, or other actionable items that need to be tracked.
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: |
|
|
||||||
Provide a clear and detailed description of the task that needs to be accomplished.
|
|
||||||
|
|
||||||
Be specific about what needs to be done, why it's important, and any constraints or requirements.
|
|
||||||
placeholder: |
|
|
||||||
Describe the task, including:
|
|
||||||
- What needs to be done
|
|
||||||
- Why this task is needed
|
|
||||||
- Expected outcome
|
|
||||||
- Any constraints or requirements
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: additional_context
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: |
|
|
||||||
Any additional information, links, research, or context that would be helpful.
|
|
||||||
|
|
||||||
Include links to related issues, research, prototypes, roadmap opportunities etc.
|
|
||||||
placeholder: |
|
|
||||||
- Roadmap opportunity: [link]
|
|
||||||
- Epic: [link]
|
|
||||||
- Feature request: [link]
|
|
||||||
- Technical design documents: [link]
|
|
||||||
- Prototype/mockup: [link]
|
|
||||||
- Dependencies: [links]
|
|
||||||
validations:
|
|
||||||
required: false
|
|
596
.github/copilot-instructions.md
vendored
596
.github/copilot-instructions.md
vendored
@@ -1,596 +0,0 @@
|
|||||||
# GitHub Copilot & Claude Code Instructions
|
|
||||||
|
|
||||||
You are an assistant helping with development of the Home Assistant frontend. The frontend is built using Lit-based Web Components and TypeScript, providing a responsive and performant interface for home automation control.
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Quick Reference](#quick-reference)
|
|
||||||
- [Core Architecture](#core-architecture)
|
|
||||||
- [Development Standards](#development-standards)
|
|
||||||
- [Component Library](#component-library)
|
|
||||||
- [Common Patterns](#common-patterns)
|
|
||||||
- [Text and Copy Guidelines](#text-and-copy-guidelines)
|
|
||||||
- [Development Workflow](#development-workflow)
|
|
||||||
- [Review Guidelines](#review-guidelines)
|
|
||||||
|
|
||||||
## Quick Reference
|
|
||||||
|
|
||||||
### Essential Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn lint # ESLint + Prettier + TypeScript + Lit
|
|
||||||
yarn format # Auto-fix ESLint + Prettier
|
|
||||||
yarn lint:types # TypeScript compiler
|
|
||||||
yarn test # Vitest
|
|
||||||
script/develop # Development server
|
|
||||||
```
|
|
||||||
|
|
||||||
### Component Prefixes
|
|
||||||
|
|
||||||
- `ha-` - Home Assistant components
|
|
||||||
- `hui-` - Lovelace UI components
|
|
||||||
- `dialog-` - Dialog components
|
|
||||||
|
|
||||||
### Import Patterns
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import type { HomeAssistant } from "../types";
|
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
|
||||||
import { showAlertDialog } from "../dialogs/generic/show-alert-dialog";
|
|
||||||
```
|
|
||||||
|
|
||||||
## Core Architecture
|
|
||||||
|
|
||||||
The Home Assistant frontend is a modern web application that:
|
|
||||||
|
|
||||||
- Uses Web Components (custom elements) built with Lit framework
|
|
||||||
- Is written entirely in TypeScript with strict type checking
|
|
||||||
- Communicates with the backend via WebSocket API
|
|
||||||
- Provides comprehensive theming and internationalization
|
|
||||||
|
|
||||||
## Development Standards
|
|
||||||
|
|
||||||
### Code Quality Requirements
|
|
||||||
|
|
||||||
**Linting and Formatting (Enforced by Tools)**
|
|
||||||
|
|
||||||
- ESLint config extends Airbnb, TypeScript strict, Lit, Web Components, Accessibility
|
|
||||||
- Prettier with ES5 trailing commas enforced
|
|
||||||
- No console statements (`no-console: "error"`) - use proper logging
|
|
||||||
- Import organization: No unused imports, consistent type imports
|
|
||||||
|
|
||||||
**Naming Conventions**
|
|
||||||
|
|
||||||
- PascalCase for types and classes
|
|
||||||
- camelCase for variables, methods
|
|
||||||
- Private methods require leading underscore
|
|
||||||
- Public methods forbid leading underscore
|
|
||||||
|
|
||||||
### TypeScript Usage
|
|
||||||
|
|
||||||
- **Always use strict TypeScript**: Enable all strict flags, avoid `any` types
|
|
||||||
- **Proper type imports**: Use `import type` for type-only imports
|
|
||||||
- **Define interfaces**: Create proper interfaces for data structures
|
|
||||||
- **Type component properties**: All Lit properties must be properly typed
|
|
||||||
- **No unused variables**: Prefix with `_` if intentionally unused
|
|
||||||
- **Consistent imports**: Use `@typescript-eslint/consistent-type-imports`
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Good
|
|
||||||
import type { HomeAssistant } from "../types";
|
|
||||||
|
|
||||||
interface EntityConfig {
|
|
||||||
entity: string;
|
|
||||||
name?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@property({ type: Object })
|
|
||||||
hass!: HomeAssistant;
|
|
||||||
|
|
||||||
// Bad
|
|
||||||
@property()
|
|
||||||
hass: any;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Web Components with Lit
|
|
||||||
|
|
||||||
- **Use Lit 3.x patterns**: Follow modern Lit practices
|
|
||||||
- **Extend appropriate base classes**: Use `LitElement`, `SubscribeMixin`, or other mixins as needed
|
|
||||||
- **Define custom element names**: Use `ha-` prefix for components
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
@customElement("ha-my-component")
|
|
||||||
export class HaMyComponent extends LitElement {
|
|
||||||
@property({ attribute: false })
|
|
||||||
hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@state()
|
|
||||||
private _config?: MyComponentConfig;
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`<div>Content</div>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Component Guidelines
|
|
||||||
|
|
||||||
- **Use composition**: Prefer composition over inheritance
|
|
||||||
- **Lazy load panels**: Heavy panels should be dynamically imported
|
|
||||||
- **Optimize renders**: Use `@state()` for internal state, `@property()` for public API
|
|
||||||
- **Handle loading states**: Always show appropriate loading indicators
|
|
||||||
- **Support themes**: Use CSS custom properties from theme
|
|
||||||
|
|
||||||
### Data Management
|
|
||||||
|
|
||||||
- **Use WebSocket API**: All backend communication via home-assistant-js-websocket
|
|
||||||
- **Cache appropriately**: Use collections and caching for frequently accessed data
|
|
||||||
- **Handle errors gracefully**: All API calls should have error handling
|
|
||||||
- **Update real-time**: Subscribe to state changes for live updates
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Good
|
|
||||||
try {
|
|
||||||
const result = await fetchEntityRegistry(this.hass.connection);
|
|
||||||
this._processResult(result);
|
|
||||||
} catch (err) {
|
|
||||||
showAlertDialog(this, {
|
|
||||||
text: `Failed to load: ${err.message}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Styling Guidelines
|
|
||||||
|
|
||||||
- **Use CSS custom properties**: Leverage the theme system
|
|
||||||
- **Mobile-first responsive**: Design for mobile, enhance for desktop
|
|
||||||
- **Follow Material Design**: Use Material Web Components where appropriate
|
|
||||||
- **Support RTL**: Ensure all layouts work in RTL languages
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
--spacing: 16px;
|
|
||||||
padding: var(--spacing);
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
background-color: var(--card-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
:host {
|
|
||||||
--spacing: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Performance Best Practices
|
|
||||||
|
|
||||||
- **Code split**: Split code at the panel/dialog level
|
|
||||||
- **Lazy load**: Use dynamic imports for heavy components
|
|
||||||
- **Optimize bundle**: Keep initial bundle size minimal
|
|
||||||
- **Use virtual scrolling**: For long lists, implement virtual scrolling
|
|
||||||
- **Memoize computations**: Cache expensive calculations
|
|
||||||
|
|
||||||
### Testing Requirements
|
|
||||||
|
|
||||||
- **Write tests**: Add tests for data processing and utilities
|
|
||||||
- **Test with Vitest**: Use the established test framework
|
|
||||||
- **Mock appropriately**: Mock WebSocket connections and API calls
|
|
||||||
- **Test accessibility**: Ensure components are accessible
|
|
||||||
|
|
||||||
## Component Library
|
|
||||||
|
|
||||||
### Dialog Components
|
|
||||||
|
|
||||||
**Available Dialog Types:**
|
|
||||||
|
|
||||||
- `ha-md-dialog` - Preferred for new code (Material Design 3)
|
|
||||||
- `ha-dialog` - Legacy component still widely used
|
|
||||||
|
|
||||||
**Opening Dialogs (Fire Event Pattern - Recommended):**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
fireEvent(this, "show-dialog", {
|
|
||||||
dialogTag: "dialog-example",
|
|
||||||
dialogImport: () => import("./dialog-example"),
|
|
||||||
dialogParams: { title: "Example", data: someData },
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**Dialog Implementation Requirements:**
|
|
||||||
|
|
||||||
- Implement `HassDialog<T>` interface
|
|
||||||
- Use `createCloseHeading()` for standard headers
|
|
||||||
- Import `haStyleDialog` for consistent styling
|
|
||||||
- Return `nothing` when no params (loading state)
|
|
||||||
- Fire `dialog-closed` event when closing
|
|
||||||
- Add `dialogInitialFocus` for accessibility
|
|
||||||
|
|
||||||
````
|
|
||||||
|
|
||||||
### Form Component (ha-form)
|
|
||||||
- Schema-driven using `HaFormSchema[]`
|
|
||||||
- Supports entity, device, area, target, number, boolean, time, action, text, object, select, icon, media, location selectors
|
|
||||||
- Built-in validation with error display
|
|
||||||
- Use `dialogInitialFocus` in dialogs
|
|
||||||
- Use `computeLabel`, `computeError`, `computeHelper` for translations
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
<ha-form
|
|
||||||
.hass=${this.hass}
|
|
||||||
.data=${this._data}
|
|
||||||
.schema=${this._schema}
|
|
||||||
.error=${this._errors}
|
|
||||||
.computeLabel=${(schema) => this.hass.localize(`ui.panel.${schema.name}`)}
|
|
||||||
@value-changed=${this._valueChanged}
|
|
||||||
></ha-form>
|
|
||||||
````
|
|
||||||
|
|
||||||
### Alert Component (ha-alert)
|
|
||||||
|
|
||||||
- Types: `error`, `warning`, `info`, `success`
|
|
||||||
- Properties: `title`, `alert-type`, `dismissable`, `icon`, `action`, `rtl`
|
|
||||||
- Content announced by screen readers when dynamically displayed
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ha-alert alert-type="error">Error message</ha-alert>
|
|
||||||
<ha-alert alert-type="warning" title="Warning">Description</ha-alert>
|
|
||||||
<ha-alert alert-type="success" dismissable>Success message</ha-alert>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Patterns
|
|
||||||
|
|
||||||
### Creating a Panel
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
@customElement("ha-panel-myfeature")
|
|
||||||
export class HaPanelMyFeature extends SubscribeMixin(LitElement) {
|
|
||||||
@property({ attribute: false })
|
|
||||||
hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true })
|
|
||||||
narrow!: boolean;
|
|
||||||
|
|
||||||
@property()
|
|
||||||
route!: Route;
|
|
||||||
|
|
||||||
hassSubscribe() {
|
|
||||||
return [
|
|
||||||
subscribeEntityRegistry(this.hass.connection, (entities) => {
|
|
||||||
this._entities = entities;
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Creating a Dialog
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
@customElement("dialog-my-feature")
|
|
||||||
export class DialogMyFeature
|
|
||||||
extends LitElement
|
|
||||||
implements HassDialog<MyDialogParams>
|
|
||||||
{
|
|
||||||
@property({ attribute: false })
|
|
||||||
hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@state()
|
|
||||||
private _params?: MyDialogParams;
|
|
||||||
|
|
||||||
public async showDialog(params: MyDialogParams): Promise<void> {
|
|
||||||
this._params = params;
|
|
||||||
}
|
|
||||||
|
|
||||||
public closeDialog(): void {
|
|
||||||
this._params = undefined;
|
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
if (!this._params) {
|
|
||||||
return nothing;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<ha-dialog
|
|
||||||
open
|
|
||||||
@closed=${this.closeDialog}
|
|
||||||
.heading=${createCloseHeading(this.hass, this._params.title)}
|
|
||||||
>
|
|
||||||
<!-- Dialog content -->
|
|
||||||
<ha-button
|
|
||||||
appearance="plain"
|
|
||||||
@click=${this.closeDialog}
|
|
||||||
slot="secondaryAction"
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.common.cancel")}
|
|
||||||
</ha-button>
|
|
||||||
<ha-button @click=${this._submit} slot="primaryAction">
|
|
||||||
${this.hass.localize("ui.common.save")}
|
|
||||||
</ha-button>
|
|
||||||
</ha-dialog>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = [haStyleDialog, css``];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dialog Design Guidelines
|
|
||||||
|
|
||||||
- Max width: 560px (Alert/confirmation: 320px fixed width)
|
|
||||||
- Close X-icon on top left (all screen sizes)
|
|
||||||
- Submit button grouped with cancel at bottom right
|
|
||||||
- Keep button labels short: "Save", "Delete", "Enable"
|
|
||||||
- Destructive actions use red warning button
|
|
||||||
- Always use a title (best practice)
|
|
||||||
- Strive for minimalism
|
|
||||||
|
|
||||||
#### Creating a Lovelace Card
|
|
||||||
|
|
||||||
**Purpose**: Cards allow users to tell different stories about their house (based on gallery)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
@customElement("hui-my-card")
|
|
||||||
export class HuiMyCard extends LitElement implements LovelaceCard {
|
|
||||||
@property({ attribute: false })
|
|
||||||
hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@state()
|
|
||||||
private _config?: MyCardConfig;
|
|
||||||
|
|
||||||
public setConfig(config: MyCardConfig): void {
|
|
||||||
if (!config.entity) {
|
|
||||||
throw new Error("Entity required");
|
|
||||||
}
|
|
||||||
this._config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCardSize(): number {
|
|
||||||
return 3; // Height in grid units
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional: Editor for card configuration
|
|
||||||
public static getConfigElement(): LovelaceCardEditor {
|
|
||||||
return document.createElement("hui-my-card-editor");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional: Stub config for card picker
|
|
||||||
public static getStubConfig(): object {
|
|
||||||
return { entity: "" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Card Guidelines:**
|
|
||||||
|
|
||||||
- Cards are highly customizable for different households
|
|
||||||
- Implement `LovelaceCard` interface with `setConfig()` and `getCardSize()`
|
|
||||||
- Use proper error handling in `setConfig()`
|
|
||||||
- Consider all possible states (loading, error, unavailable)
|
|
||||||
- Support different entity types and states
|
|
||||||
- Follow responsive design principles
|
|
||||||
- Add configuration editor when needed
|
|
||||||
|
|
||||||
### Internationalization
|
|
||||||
|
|
||||||
- **Use localize**: Always use the localization system
|
|
||||||
- **Add translation keys**: Add keys to src/translations/en.json
|
|
||||||
- **Support placeholders**: Use proper placeholder syntax
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
this.hass.localize("ui.panel.config.updates.update_available", {
|
|
||||||
count: 5,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Accessibility
|
|
||||||
|
|
||||||
- **ARIA labels**: Add appropriate ARIA labels
|
|
||||||
- **Keyboard navigation**: Ensure all interactions work with keyboard
|
|
||||||
- **Screen reader support**: Test with screen readers
|
|
||||||
- **Color contrast**: Meet WCAG AA standards
|
|
||||||
|
|
||||||
## Development Workflow
|
|
||||||
|
|
||||||
### Setup and Commands
|
|
||||||
|
|
||||||
1. **Setup**: `script/setup` - Install dependencies
|
|
||||||
2. **Develop**: `script/develop` - Development server
|
|
||||||
3. **Lint**: `yarn lint` - Run all linting before committing
|
|
||||||
4. **Test**: `yarn test` - Add and run tests
|
|
||||||
5. **Build**: `script/build_frontend` - Test production build
|
|
||||||
|
|
||||||
### Common Pitfalls to Avoid
|
|
||||||
|
|
||||||
- Don't use `querySelector` - Use refs or component properties
|
|
||||||
- Don't manipulate DOM directly - Let Lit handle rendering
|
|
||||||
- Don't use global styles - Scope styles to components
|
|
||||||
- Don't block the main thread - Use web workers for heavy computation
|
|
||||||
- Don't ignore TypeScript errors - Fix all type issues
|
|
||||||
|
|
||||||
### Security Best Practices
|
|
||||||
|
|
||||||
- Sanitize HTML - Never use `unsafeHTML` with user content
|
|
||||||
- Validate inputs - Always validate user inputs
|
|
||||||
- Use HTTPS - All external resources must use HTTPS
|
|
||||||
- CSP compliance - Ensure code works with Content Security Policy
|
|
||||||
|
|
||||||
### Text and Copy Guidelines
|
|
||||||
|
|
||||||
#### Terminology Standards
|
|
||||||
|
|
||||||
**Delete vs Remove** (Based on gallery/src/pages/Text/remove-delete-add-create.markdown)
|
|
||||||
|
|
||||||
- **Use "Remove"** for actions that can be restored or reapplied:
|
|
||||||
- Removing a user's permission
|
|
||||||
- Removing a user from a group
|
|
||||||
- Removing links between items
|
|
||||||
- Removing a widget from dashboard
|
|
||||||
- Removing an item from a cart
|
|
||||||
- **Use "Delete"** for permanent, non-recoverable actions:
|
|
||||||
- Deleting a field
|
|
||||||
- Deleting a value in a field
|
|
||||||
- Deleting a task
|
|
||||||
- Deleting a group
|
|
||||||
- Deleting a permission
|
|
||||||
- Deleting a calendar event
|
|
||||||
|
|
||||||
**Create vs Add** (Create pairs with Delete, Add pairs with Remove)
|
|
||||||
|
|
||||||
- **Use "Add"** for already-existing items:
|
|
||||||
- Adding a permission to a user
|
|
||||||
- Adding a user to a group
|
|
||||||
- Adding links between items
|
|
||||||
- Adding a widget to dashboard
|
|
||||||
- Adding an item to a cart
|
|
||||||
- **Use "Create"** for something made from scratch:
|
|
||||||
- Creating a new field
|
|
||||||
- Creating a new task
|
|
||||||
- Creating a new group
|
|
||||||
- Creating a new permission
|
|
||||||
- Creating a new calendar event
|
|
||||||
|
|
||||||
#### Writing Style (Consistent with Home Assistant Documentation)
|
|
||||||
|
|
||||||
- **Use American English**: Standard spelling and terminology
|
|
||||||
- **Friendly, informational tone**: Be inspiring, personal, comforting, engaging
|
|
||||||
- **Address users directly**: Use "you" and "your"
|
|
||||||
- **Be inclusive**: Objective, non-discriminatory language
|
|
||||||
- **Be concise**: Use clear, direct language
|
|
||||||
- **Be consistent**: Follow established terminology patterns
|
|
||||||
- **Use active voice**: "Delete the automation" not "The automation should be deleted"
|
|
||||||
- **Avoid jargon**: Use terms familiar to home automation users
|
|
||||||
|
|
||||||
#### Language Standards
|
|
||||||
|
|
||||||
- **Always use "Home Assistant"** in full, never "HA" or "HASS"
|
|
||||||
- **Avoid abbreviations**: Spell out terms when possible
|
|
||||||
- **Use sentence case everywhere**: Titles, headings, buttons, labels, UI elements
|
|
||||||
- ✅ "Create new automation"
|
|
||||||
- ❌ "Create New Automation"
|
|
||||||
- ✅ "Device settings"
|
|
||||||
- ❌ "Device Settings"
|
|
||||||
- **Oxford comma**: Use in lists (item 1, item 2, and item 3)
|
|
||||||
- **Replace Latin terms**: Use "like" instead of "e.g.", "for example" instead of "i.e."
|
|
||||||
- **Avoid CAPS for emphasis**: Use bold or italics instead
|
|
||||||
- **Write for all skill levels**: Both technical and non-technical users
|
|
||||||
|
|
||||||
#### Key Terminology
|
|
||||||
|
|
||||||
- **"add-on"** (hyphenated, not "addon")
|
|
||||||
- **"integration"** (preferred over "component")
|
|
||||||
- **Technical terms**: Use lowercase (automation, entity, device, service)
|
|
||||||
|
|
||||||
#### Translation Considerations
|
|
||||||
|
|
||||||
- **Add translation keys**: All user-facing text must be translatable
|
|
||||||
- **Use placeholders**: Support dynamic content in translations
|
|
||||||
- **Keep context**: Provide enough context for translators
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Good
|
|
||||||
this.hass.localize("ui.panel.config.automation.delete_confirm", {
|
|
||||||
name: automation.alias,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bad - hardcoded text
|
|
||||||
("Are you sure you want to delete this automation?");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Review Issues (From PR Analysis)
|
|
||||||
|
|
||||||
#### User Experience and Accessibility
|
|
||||||
|
|
||||||
- **Form validation**: Always provide proper field labels and validation feedback
|
|
||||||
- **Form accessibility**: Prevent password managers from incorrectly identifying fields
|
|
||||||
- **Loading states**: Show clear progress indicators during async operations
|
|
||||||
- **Error handling**: Display meaningful error messages when operations fail
|
|
||||||
- **Mobile responsiveness**: Ensure components work well on small screens
|
|
||||||
- **Hit targets**: Make clickable areas large enough for touch interaction
|
|
||||||
- **Visual feedback**: Provide clear indication of interactive states
|
|
||||||
|
|
||||||
#### Dialog and Modal Patterns
|
|
||||||
|
|
||||||
- **Dialog width constraints**: Respect minimum and maximum width requirements
|
|
||||||
- **Interview progress**: Show clear progress for multi-step operations
|
|
||||||
- **State persistence**: Handle dialog state properly during background operations
|
|
||||||
- **Cancel behavior**: Ensure cancel/close buttons work consistently
|
|
||||||
- **Form prefilling**: Use smart defaults but allow user override
|
|
||||||
|
|
||||||
#### Component Design Patterns
|
|
||||||
|
|
||||||
- **Terminology consistency**: Use "Join"/"Apply" instead of "Group" when appropriate
|
|
||||||
- **Visual hierarchy**: Ensure proper font sizes and spacing ratios
|
|
||||||
- **Grid alignment**: Components should align to the design grid system
|
|
||||||
- **Badge placement**: Position badges and indicators consistently
|
|
||||||
- **Color theming**: Respect theme variables and design system colors
|
|
||||||
|
|
||||||
#### Code Quality Issues
|
|
||||||
|
|
||||||
- **Null checking**: Always check if entities exist before accessing properties
|
|
||||||
- **TypeScript safety**: Handle potentially undefined array/object access
|
|
||||||
- **Import organization**: Remove unused imports and use proper type imports
|
|
||||||
- **Event handling**: Properly subscribe and unsubscribe from events
|
|
||||||
- **Memory leaks**: Clean up subscriptions and event listeners
|
|
||||||
|
|
||||||
#### Configuration and Props
|
|
||||||
|
|
||||||
- **Optional parameters**: Make configuration fields optional when sensible
|
|
||||||
- **Smart defaults**: Provide reasonable default values
|
|
||||||
- **Future extensibility**: Design APIs that can be extended later
|
|
||||||
- **Validation**: Validate configuration before applying changes
|
|
||||||
|
|
||||||
## Review Guidelines
|
|
||||||
|
|
||||||
### Core Requirements Checklist
|
|
||||||
|
|
||||||
- [ ] TypeScript strict mode passes (`yarn lint:types`)
|
|
||||||
- [ ] No ESLint errors or warnings (`yarn lint:eslint`)
|
|
||||||
- [ ] Prettier formatting applied (`yarn lint:prettier`)
|
|
||||||
- [ ] Lit analyzer passes (`yarn lint:lit`)
|
|
||||||
- [ ] Component follows Lit best practices
|
|
||||||
- [ ] Proper error handling implemented
|
|
||||||
- [ ] Loading states handled
|
|
||||||
- [ ] Mobile responsive
|
|
||||||
- [ ] Theme variables used
|
|
||||||
- [ ] Translations added
|
|
||||||
- [ ] Accessible to screen readers
|
|
||||||
- [ ] Tests added (where applicable)
|
|
||||||
- [ ] No console statements (use proper logging)
|
|
||||||
- [ ] Unused imports removed
|
|
||||||
- [ ] Proper naming conventions
|
|
||||||
|
|
||||||
### Text and Copy Checklist
|
|
||||||
|
|
||||||
- [ ] Follows terminology guidelines (Delete vs Remove, Create vs Add)
|
|
||||||
- [ ] Localization keys added for all user-facing text
|
|
||||||
- [ ] Uses "Home Assistant" (never "HA" or "HASS")
|
|
||||||
- [ ] Sentence case for ALL text (titles, headings, buttons, labels)
|
|
||||||
- [ ] American English spelling
|
|
||||||
- [ ] Friendly, informational tone
|
|
||||||
- [ ] Avoids abbreviations and jargon
|
|
||||||
- [ ] Correct terminology (add-on not addon, integration not component)
|
|
||||||
|
|
||||||
### Component-Specific Checks
|
|
||||||
|
|
||||||
- [ ] Dialogs implement HassDialog interface
|
|
||||||
- [ ] Dialog styling uses haStyleDialog
|
|
||||||
- [ ] Dialog accessibility includes dialogInitialFocus
|
|
||||||
- [ ] ha-alert used correctly for messages
|
|
||||||
- [ ] ha-form uses proper schema structure
|
|
||||||
- [ ] Components handle all states (loading, error, unavailable)
|
|
||||||
- [ ] Entity existence checked before property access
|
|
||||||
- [ ] Event subscriptions properly cleaned up
|
|
12
.github/workflows/cast_deployment.yaml
vendored
12
.github/workflows/cast_deployment.yaml
vendored
@@ -21,12 +21,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
- name: Deploy to Netlify
|
- name: Deploy to Netlify
|
||||||
id: deploy
|
id: deploy
|
||||||
run: |
|
run: |
|
||||||
npx -y netlify-cli@23.7.3 deploy --dir=cast/dist --alias dev
|
npx -y netlify-cli deploy --dir=cast/dist --alias dev
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}
|
||||||
@@ -56,12 +56,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
- name: Deploy to Netlify
|
- name: Deploy to Netlify
|
||||||
id: deploy
|
id: deploy
|
||||||
run: |
|
run: |
|
||||||
npx -y netlify-cli@23.7.3 deploy --dir=cast/dist --prod
|
npx -y netlify-cli deploy --dir=cast/dist --prod
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_CAST_SITE_ID }}
|
||||||
|
22
.github/workflows/ci.yaml
vendored
22
.github/workflows/ci.yaml
vendored
@@ -24,9 +24,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
- name: Build resources
|
- name: Build resources
|
||||||
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
|
run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data gather-gallery-pages
|
||||||
- name: Setup lint cache
|
- name: Setup lint cache
|
||||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
uses: actions/cache@v4.2.3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
node_modules/.cache/prettier
|
node_modules/.cache/prettier
|
||||||
@@ -58,9 +58,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -76,9 +76,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
IS_TEST: "true"
|
IS_TEST: "true"
|
||||||
- name: Upload bundle stats
|
- name: Upload bundle stats
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@v4.6.2
|
||||||
with:
|
with:
|
||||||
name: frontend-bundle-stats
|
name: frontend-bundle-stats
|
||||||
path: build/stats/*.json
|
path: build/stats/*.json
|
||||||
@@ -100,9 +100,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -113,7 +113,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
IS_TEST: "true"
|
IS_TEST: "true"
|
||||||
- name: Upload bundle stats
|
- name: Upload bundle stats
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@v4.6.2
|
||||||
with:
|
with:
|
||||||
name: supervisor-bundle-stats
|
name: supervisor-bundle-stats
|
||||||
path: build/stats/*.json
|
path: build/stats/*.json
|
||||||
|
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
@@ -36,14 +36,14 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -57,4 +57,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
uses: github/codeql-action/analyze@v3
|
||||||
|
12
.github/workflows/demo_deployment.yaml
vendored
12
.github/workflows/demo_deployment.yaml
vendored
@@ -22,12 +22,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
- name: Deploy to Netlify
|
- name: Deploy to Netlify
|
||||||
id: deploy
|
id: deploy
|
||||||
run: |
|
run: |
|
||||||
npx -y netlify-cli@23.7.3 deploy --dir=demo/dist --prod
|
npx -y netlify-cli deploy --dir=demo/dist --prod
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_DEV_SITE_ID }}
|
||||||
@@ -57,12 +57,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -78,7 +78,7 @@ jobs:
|
|||||||
- name: Deploy to Netlify
|
- name: Deploy to Netlify
|
||||||
id: deploy
|
id: deploy
|
||||||
run: |
|
run: |
|
||||||
npx -y netlify-cli@23.7.3 deploy --dir=demo/dist --prod
|
npx -y netlify-cli deploy --dir=demo/dist --prod
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEMO_SITE_ID }}
|
||||||
|
6
.github/workflows/design_deployment.yaml
vendored
6
.github/workflows/design_deployment.yaml
vendored
@@ -16,10 +16,10 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
- name: Deploy to Netlify
|
- name: Deploy to Netlify
|
||||||
id: deploy
|
id: deploy
|
||||||
run: |
|
run: |
|
||||||
npx -y netlify-cli@23.7.3 deploy --dir=gallery/dist --prod
|
npx -y netlify-cli deploy --dir=gallery/dist --prod
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_GALLERY_SITE_ID }}
|
||||||
|
6
.github/workflows/design_preview.yaml
vendored
6
.github/workflows/design_preview.yaml
vendored
@@ -21,10 +21,10 @@ jobs:
|
|||||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
- name: Deploy preview to Netlify
|
- name: Deploy preview to Netlify
|
||||||
id: deploy
|
id: deploy
|
||||||
run: |
|
run: |
|
||||||
npx -y netlify-cli@23.7.3 deploy --dir=gallery/dist --alias "deploy-preview-${{ github.event.number }}" \
|
npx -y netlify-cli deploy --dir=gallery/dist --alias "deploy-preview-${{ github.event.number }}" \
|
||||||
--json > deploy_output.json
|
--json > deploy_output.json
|
||||||
env:
|
env:
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||||
|
2
.github/workflows/labeler.yaml
vendored
2
.github/workflows/labeler.yaml
vendored
@@ -10,6 +10,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Apply labels
|
- name: Apply labels
|
||||||
uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
|
uses: actions/labeler@v5.0.0
|
||||||
with:
|
with:
|
||||||
sync-labels: true
|
sync-labels: true
|
||||||
|
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
|
- uses: dessant/lock-threads@v5.0.1
|
||||||
with:
|
with:
|
||||||
github-token: ${{ github.token }}
|
github-token: ${{ github.token }}
|
||||||
process-only: "issues, prs"
|
process-only: "issues, prs"
|
||||||
|
10
.github/workflows/nightly.yaml
vendored
10
.github/workflows/nightly.yaml
vendored
@@ -20,15 +20,15 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -57,14 +57,14 @@ jobs:
|
|||||||
run: tar -czvf translations.tar.gz translations
|
run: tar -czvf translations.tar.gz translations
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@v4.6.2
|
||||||
with:
|
with:
|
||||||
name: wheels
|
name: wheels
|
||||||
path: dist/home_assistant_frontend*.whl
|
path: dist/home_assistant_frontend*.whl
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Upload translations
|
- name: Upload translations
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@v4.6.2
|
||||||
with:
|
with:
|
||||||
name: translations
|
name: translations
|
||||||
path: translations.tar.gz
|
path: translations.tar.gz
|
||||||
|
2
.github/workflows/relative-ci.yaml
vendored
2
.github/workflows/relative-ci.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Send bundle stats and build information to RelativeCI
|
- name: Send bundle stats and build information to RelativeCI
|
||||||
uses: relative-ci/agent-action@1707825cbfcc7452b2913d273414705415ae64d4 # v3.0.1
|
uses: relative-ci/agent-action@v2.2.0
|
||||||
with:
|
with:
|
||||||
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
|
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
2
.github/workflows/release-drafter.yaml
vendored
2
.github/workflows/release-drafter.yaml
vendored
@@ -18,6 +18,6 @@ jobs:
|
|||||||
pull-requests: read
|
pull-requests: read
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0
|
- uses: release-drafter/release-drafter@v6.1.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
23
.github/workflows/release.yaml
vendored
23
.github/workflows/release.yaml
vendored
@@ -23,10 +23,10 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
uses: home-assistant/actions/helpers/verify-version@master
|
uses: home-assistant/actions/helpers/verify-version@master
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
script/release
|
script/release
|
||||||
|
|
||||||
- name: Upload release assets
|
- name: Upload release assets
|
||||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
uses: softprops/action-gh-release@v2.2.2
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
dist/*.whl
|
dist/*.whl
|
||||||
@@ -73,9 +73,8 @@ jobs:
|
|||||||
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
|
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
|
||||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||||
|
|
||||||
# home-assistant/wheels doesn't support SHA pinning
|
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: home-assistant/wheels@2025.09.1
|
uses: home-assistant/wheels@2025.03.0
|
||||||
with:
|
with:
|
||||||
abi: cp313
|
abi: cp313
|
||||||
tag: musllinux_1_2
|
tag: musllinux_1_2
|
||||||
@@ -91,9 +90,9 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -108,7 +107,7 @@ jobs:
|
|||||||
- name: Tar folder
|
- name: Tar folder
|
||||||
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
|
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
|
||||||
- name: Upload release asset
|
- name: Upload release asset
|
||||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
uses: softprops/action-gh-release@v2.2.2
|
||||||
with:
|
with:
|
||||||
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
|
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
|
||||||
|
|
||||||
@@ -120,9 +119,9 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
uses: actions/setup-node@v4.4.0
|
||||||
with:
|
with:
|
||||||
node-version-file: ".nvmrc"
|
node-version-file: ".nvmrc"
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@@ -137,6 +136,6 @@ jobs:
|
|||||||
- name: Tar folder
|
- name: Tar folder
|
||||||
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
|
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
|
||||||
- name: Upload release asset
|
- name: Upload release asset
|
||||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
uses: softprops/action-gh-release@v2.2.2
|
||||||
with:
|
with:
|
||||||
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz
|
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz
|
||||||
|
58
.github/workflows/restrict-task-creation.yml
vendored
58
.github/workflows/restrict-task-creation.yml
vendored
@@ -1,58 +0,0 @@
|
|||||||
name: Restrict task creation
|
|
||||||
|
|
||||||
# yamllint disable-line rule:truthy
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-authorization:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# Only run if this is a Task issue type (from the issue form)
|
|
||||||
if: github.event.issue.type.name == 'Task'
|
|
||||||
steps:
|
|
||||||
- name: Check if user is authorized
|
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const issueAuthor = context.payload.issue.user.login;
|
|
||||||
|
|
||||||
// Check if user is an organization member
|
|
||||||
try {
|
|
||||||
await github.rest.orgs.checkMembershipForUser({
|
|
||||||
org: 'home-assistant',
|
|
||||||
username: issueAuthor
|
|
||||||
});
|
|
||||||
console.log(`✅ ${issueAuthor} is an organization member`);
|
|
||||||
return; // Authorized
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`❌ ${issueAuthor} is not authorized to create Task issues`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the issue with a comment
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
body: `Hi @${issueAuthor}, thank you for your contribution!\n\n` +
|
|
||||||
`Task issues are restricted to Open Home Foundation staff and authorized contributors.\n\n` +
|
|
||||||
`If you would like to:\n` +
|
|
||||||
`- Report a bug: Please use the [bug report form](https://github.com/home-assistant/frontend/issues/new?template=bug_report.yml)\n` +
|
|
||||||
`- Request a feature: Please submit to [Feature Requests](https://github.com/orgs/home-assistant/discussions)\n\n` +
|
|
||||||
`If you believe you should have access to create Task issues, please contact the maintainers.`
|
|
||||||
});
|
|
||||||
|
|
||||||
await github.rest.issues.update({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
state: 'closed'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add a label to indicate this was auto-closed
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
labels: ['auto-closed']
|
|
||||||
});
|
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 90 days stale policy
|
- name: 90 days stale policy
|
||||||
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
uses: actions/stale@v9.1.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
days-before-stale: 90
|
days-before-stale: 90
|
||||||
|
3
.github/workflows/translations.yaml
vendored
3
.github/workflows/translations.yaml
vendored
@@ -1,7 +1,6 @@
|
|||||||
name: Translations
|
name: Translations
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
@@ -14,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
uses: actions/checkout@v4.2.2
|
||||||
|
|
||||||
- name: Upload Translations
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -53,7 +53,3 @@ src/cast/dev_const.ts
|
|||||||
|
|
||||||
# test coverage
|
# test coverage
|
||||||
test/coverage/
|
test/coverage/
|
||||||
|
|
||||||
# AI tooling
|
|
||||||
.claude
|
|
||||||
|
|
||||||
|
@@ -1 +1 @@
|
|||||||
yarn run lint-staged --relative
|
yarn run lint-staged --relative --shell "/bin/bash"
|
||||||
|
942
.yarn/releases/yarn-4.10.3.cjs
vendored
942
.yarn/releases/yarn-4.10.3.cjs
vendored
File diff suppressed because one or more lines are too long
948
.yarn/releases/yarn-4.9.1.cjs
vendored
Executable file
948
.yarn/releases/yarn-4.9.1.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
@@ -6,4 +6,4 @@ enableGlobalCache: false
|
|||||||
|
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-4.10.3.cjs
|
yarnPath: .yarn/releases/yarn-4.9.1.cjs
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
# People marked here will be automatically requested for a review
|
|
||||||
# when the code that they own is touched.
|
|
||||||
# https://github.com/blog/2392-introducing-code-owners
|
|
||||||
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
|
|
||||||
|
|
||||||
# Part of the frontend that mobile developper should review
|
|
||||||
src/external_app/ @bgoncal @TimoPtr
|
|
||||||
test/external_app/ @bgoncal @TimoPtr
|
|
@@ -183,6 +183,7 @@ module.exports.babelOptions = ({
|
|||||||
include: /\/node_modules\//,
|
include: /\/node_modules\//,
|
||||||
exclude: [
|
exclude: [
|
||||||
"element-internals-polyfill",
|
"element-internals-polyfill",
|
||||||
|
"@shoelace-style",
|
||||||
"@?lit(?:-labs|-element|-html)?",
|
"@?lit(?:-labs|-element|-html)?",
|
||||||
].map((p) => new RegExp(`/node_modules/${p}/`)),
|
].map((p) => new RegExp(`/node_modules/${p}/`)),
|
||||||
},
|
},
|
||||||
|
@@ -14,5 +14,5 @@
|
|||||||
"name": "Home Assistant Cast",
|
"name": "Home Assistant Cast",
|
||||||
"short_name": "HA Cast",
|
"short_name": "HA Cast",
|
||||||
"start_url": "/?homescreen=1",
|
"start_url": "/?homescreen=1",
|
||||||
"theme_color": "#009ac7"
|
"theme_color": "#03A9F4"
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
|
||||||
import type { ActionDetail } from "@material/mwc-list/mwc-list";
|
import type { ActionDetail } from "@material/mwc-list/mwc-list";
|
||||||
import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js";
|
import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js";
|
||||||
import type { Auth, Connection } from "home-assistant-js-websocket";
|
import type { Auth, Connection } from "home-assistant-js-websocket";
|
||||||
@@ -18,7 +20,6 @@ import { atLeastVersion } from "../../../../src/common/config/version";
|
|||||||
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
|
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
|
||||||
import "../../../../src/components/ha-icon";
|
import "../../../../src/components/ha-icon";
|
||||||
import "../../../../src/components/ha-list";
|
import "../../../../src/components/ha-list";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import "../../../../src/components/ha-list-item";
|
import "../../../../src/components/ha-list-item";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
@@ -62,20 +63,12 @@ class HcCast extends LitElement {
|
|||||||
<p class="question action-item">
|
<p class="question action-item">
|
||||||
Stay logged in?
|
Stay logged in?
|
||||||
<span>
|
<span>
|
||||||
<ha-button
|
<mwc-button @click=${this._handleSaveTokens}>
|
||||||
appearance="plain"
|
|
||||||
size="small"
|
|
||||||
@click=${this._handleSaveTokens}
|
|
||||||
>
|
|
||||||
YES
|
YES
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
<ha-button
|
<mwc-button @click=${this._handleSkipSaveTokens}>
|
||||||
appearance="plain"
|
|
||||||
size="small"
|
|
||||||
@click=${this._handleSkipSaveTokens}
|
|
||||||
>
|
|
||||||
NO
|
NO
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
@@ -85,10 +78,10 @@ class HcCast extends LitElement {
|
|||||||
: !this.castManager.status
|
: !this.castManager.status
|
||||||
? html`
|
? html`
|
||||||
<p class="center-item">
|
<p class="center-item">
|
||||||
<ha-button @click=${this._handleLaunch}>
|
<mwc-button raised @click=${this._handleLaunch}>
|
||||||
<ha-svg-icon slot="start" .path=${mdiCast}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiCast}></ha-svg-icon>
|
||||||
Start Casting
|
Start Casting
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
@@ -128,22 +121,14 @@ class HcCast extends LitElement {
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
${this.castManager.status
|
${this.castManager.status
|
||||||
? html`
|
? html`
|
||||||
<ha-button appearance="plain" @click=${this._handleLaunch}>
|
<mwc-button @click=${this._handleLaunch}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon .path=${mdiCastConnected}></ha-svg-icon>
|
||||||
slot="start"
|
|
||||||
.path=${mdiCastConnected}
|
|
||||||
></ha-svg-icon>
|
|
||||||
Manage
|
Manage
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<ha-button
|
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
|
||||||
variant="danger"
|
|
||||||
appearance="plain"
|
|
||||||
@click=${this._handleLogout}
|
|
||||||
>Log out</ha-button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</hc-layout>
|
</hc-layout>
|
||||||
`;
|
`;
|
||||||
@@ -242,7 +227,7 @@ class HcCast extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.question:before {
|
.question:before {
|
||||||
border-radius: var(--ha-border-radius-sm);
|
border-radius: 4px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
@@ -260,6 +245,13 @@ class HcCast extends LitElement {
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mwc-button ha-svg-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-inline-end: 8px;
|
||||||
|
margin-inline-start: initial;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
ha-list-item ha-icon,
|
ha-list-item ha-icon,
|
||||||
ha-list-item ha-svg-icon {
|
ha-list-item ha-svg-icon {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
import { mdiCastConnected, mdiCast } from "@mdi/js";
|
import { mdiCastConnected, mdiCast } from "@mdi/js";
|
||||||
import type {
|
import type {
|
||||||
Auth,
|
Auth,
|
||||||
@@ -27,7 +28,6 @@ import "../../../../src/layouts/hass-loading-screen";
|
|||||||
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
|
import { registerServiceWorker } from "../../../../src/util/register-service-worker";
|
||||||
import "./hc-layout";
|
import "./hc-layout";
|
||||||
import "../../../../src/components/ha-textfield";
|
import "../../../../src/components/ha-textfield";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
|
|
||||||
const seeFAQ = (qid) => html`
|
const seeFAQ = (qid) => html`
|
||||||
See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more
|
See <a href="./faq.html${qid ? `#${qid}` : ""}">the FAQ</a> for more
|
||||||
@@ -83,14 +83,11 @@ export class HcConnect extends LitElement {
|
|||||||
Unable to connect to ${tokens!.hassUrl}.
|
Unable to connect to ${tokens!.hassUrl}.
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-button appearance="plain" href="/">Retry</ha-button>
|
<a href="/">
|
||||||
|
<mwc-button> Retry </mwc-button>
|
||||||
|
</a>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<ha-button
|
<mwc-button @click=${this._handleLogout}>Log out</mwc-button>
|
||||||
appearance="plain"
|
|
||||||
variant="danger"
|
|
||||||
@click=${this._handleLogout}
|
|
||||||
>Log out</ha-button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</hc-layout>
|
</hc-layout>
|
||||||
`;
|
`;
|
||||||
@@ -131,19 +128,16 @@ export class HcConnect extends LitElement {
|
|||||||
${this.error ? html` <p class="error">${this.error}</p> ` : ""}
|
${this.error ? html` <p class="error">${this.error}</p> ` : ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-button appearance="plain" @click=${this._handleDemo}>
|
<mwc-button @click=${this._handleDemo}>
|
||||||
Show Demo
|
Show Demo
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="end"
|
|
||||||
.path=${this.castManager.castState === "CONNECTED"
|
.path=${this.castManager.castState === "CONNECTED"
|
||||||
? mdiCastConnected
|
? mdiCastConnected
|
||||||
: mdiCast}
|
: mdiCast}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<ha-button appearance="plain" @click=${this._handleConnect}
|
<mwc-button @click=${this._handleConnect}>Authorize</mwc-button>
|
||||||
>Authorize</ha-button
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</hc-layout>
|
</hc-layout>
|
||||||
`;
|
`;
|
||||||
@@ -315,6 +309,10 @@ export class HcConnect extends LitElement {
|
|||||||
color: darkred;
|
color: darkred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mwc-button ha-svg-icon {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.spacer {
|
.spacer {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
@@ -88,15 +88,14 @@ class HcLayout extends LitElement {
|
|||||||
font-family: var(--ha-card-header-font-family, inherit);
|
font-family: var(--ha-card-header-font-family, inherit);
|
||||||
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
||||||
letter-spacing: -0.012em;
|
letter-spacing: -0.012em;
|
||||||
line-height: var(--ha-line-height-condensed);
|
line-height: 32px;
|
||||||
padding: 24px 16px 16px;
|
padding: 24px 16px 16px;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero {
|
.hero {
|
||||||
border-radius: var(--ha-border-radius-sm) var(--ha-border-radius-sm)
|
border-radius: 4px 4px 0 0;
|
||||||
var(--ha-border-radius-square) var(--ha-border-radius-square);
|
|
||||||
}
|
}
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: var(--ha-font-size-m);
|
font-size: var(--ha-font-size-m);
|
||||||
|
@@ -75,7 +75,7 @@ export const castDemoEntities: () => Entity[] = () =>
|
|||||||
longitude: 4.8903147,
|
longitude: 4.8903147,
|
||||||
radius: 100,
|
radius: 100,
|
||||||
friendly_name: "Home",
|
friendly_name: "Home",
|
||||||
icon: "mdi:home",
|
icon: "hass:home",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"input_number.harmonyvolume": {
|
"input_number.harmonyvolume": {
|
||||||
@@ -88,7 +88,7 @@ export const castDemoEntities: () => Entity[] = () =>
|
|||||||
step: 1,
|
step: 1,
|
||||||
mode: "slider",
|
mode: "slider",
|
||||||
friendly_name: "Volume",
|
friendly_name: "Volume",
|
||||||
icon: "mdi:volume-high",
|
icon: "hass:volume-high",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"climate.upstairs": {
|
"climate.upstairs": {
|
||||||
|
@@ -56,7 +56,7 @@ export const castDemoLovelace: () => LovelaceConfig = () => {
|
|||||||
type: "weblink",
|
type: "weblink",
|
||||||
url: "/lovelace/climate",
|
url: "/lovelace/climate",
|
||||||
name: "Climate controls",
|
name: "Climate controls",
|
||||||
icon: "mdi:arrow-right",
|
icon: "hass:arrow-right",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -76,7 +76,7 @@ export const castDemoLovelace: () => LovelaceConfig = () => {
|
|||||||
type: "weblink",
|
type: "weblink",
|
||||||
url: "/lovelace/overview",
|
url: "/lovelace/overview",
|
||||||
name: "Back",
|
name: "Back",
|
||||||
icon: "mdi:arrow-left",
|
icon: "hass:arrow-left",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@@ -75,5 +75,5 @@
|
|||||||
"name": "Home Assistant Demo",
|
"name": "Home Assistant Demo",
|
||||||
"short_name": "HA Demo",
|
"short_name": "HA Demo",
|
||||||
"start_url": "/?homescreen=1",
|
"start_url": "/?homescreen=1",
|
||||||
"theme_color": "#009ac7"
|
"theme_color": "#03A9F4"
|
||||||
}
|
}
|
||||||
|
@@ -143,7 +143,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
|||||||
state: "on",
|
state: "on",
|
||||||
attributes: {
|
attributes: {
|
||||||
friendly_name: "Home Automation",
|
friendly_name: "Home Automation",
|
||||||
icon: "mdi:home-automation",
|
icon: "hass:home-automation",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"input_boolean.tvtime": {
|
"input_boolean.tvtime": {
|
||||||
|
@@ -4,7 +4,7 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
|||||||
title: "Home Assistant",
|
title: "Home Assistant",
|
||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
icon: "mdi:home-assistant",
|
icon: "hass:home-assistant",
|
||||||
id: "home",
|
id: "home",
|
||||||
title: "Home",
|
title: "Home",
|
||||||
cards: [
|
cards: [
|
||||||
|
@@ -1236,7 +1236,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
path: "security",
|
path: "security",
|
||||||
icon: "mdi:shield-home",
|
icon: "hass:shield-home",
|
||||||
name: "Security",
|
name: "Security",
|
||||||
background:
|
background:
|
||||||
'center / cover no-repeat url("/assets/jimpower/background-15.jpg") fixed',
|
'center / cover no-repeat url("/assets/jimpower/background-15.jpg") fixed',
|
||||||
|
@@ -89,14 +89,11 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="actions small-hidden">
|
<div class="actions small-hidden">
|
||||||
<ha-button
|
<a href="https://www.home-assistant.io" target="_blank">
|
||||||
appearance="plain"
|
<ha-button>
|
||||||
size="small"
|
|
||||||
href="https://www.home-assistant.io"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
|
${this.hass.localize("ui.panel.page-demo.cards.demo.learn_more")}
|
||||||
</ha-button>
|
</ha-button>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
|
@@ -68,7 +68,7 @@
|
|||||||
}
|
}
|
||||||
#ha-launch-screen .ha-launch-screen-spacer-top {
|
#ha-launch-screen .ha-launch-screen-spacer-top {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin-top: calc( 2 * max(var(--safe-area-inset-top, 0px), 48px) + 46px );
|
margin-top: calc( 2 * max(env(safe-area-inset-bottom), 48px) + 46px );
|
||||||
padding-top: 48px;
|
padding-top: 48px;
|
||||||
}
|
}
|
||||||
#ha-launch-screen .ha-launch-screen-spacer-bottom {
|
#ha-launch-screen .ha-launch-screen-spacer-bottom {
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
padding-top: 48px;
|
padding-top: 48px;
|
||||||
}
|
}
|
||||||
.ohf-logo {
|
.ohf-logo {
|
||||||
margin: max(var(--safe-area-inset-bottom, 0px), 48px) 0;
|
margin: max(env(safe-area-inset-bottom), 48px) 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@@ -1,30 +1,7 @@
|
|||||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
let changeFunction;
|
|
||||||
|
|
||||||
export const mockFrontend = (hass: MockHomeAssistant) => {
|
export const mockFrontend = (hass: MockHomeAssistant) => {
|
||||||
hass.mockWS("frontend/get_user_data", () => ({
|
hass.mockWS("frontend/get_user_data", () => ({
|
||||||
value: null,
|
value: null,
|
||||||
}));
|
}));
|
||||||
hass.mockWS("frontend/set_user_data", ({ key, value }) => {
|
|
||||||
if (key === "sidebar") {
|
|
||||||
changeFunction?.({
|
|
||||||
value: {
|
|
||||||
panelOrder: value.panelOrder || [],
|
|
||||||
hiddenPanels: value.hiddenPanels || [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
hass.mockWS("frontend/subscribe_user_data", (_msg, _hass, onChange) => {
|
|
||||||
changeFunction = onChange;
|
|
||||||
onChange?.({
|
|
||||||
value: {
|
|
||||||
panelOrder: [],
|
|
||||||
hiddenPanels: [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
return () => {};
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@@ -11,7 +11,6 @@ import tseslint from "typescript-eslint";
|
|||||||
import eslintConfigPrettier from "eslint-config-prettier";
|
import eslintConfigPrettier from "eslint-config-prettier";
|
||||||
import { configs as litConfigs } from "eslint-plugin-lit";
|
import { configs as litConfigs } from "eslint-plugin-lit";
|
||||||
import { configs as wcConfigs } from "eslint-plugin-wc";
|
import { configs as wcConfigs } from "eslint-plugin-wc";
|
||||||
import { configs as a11yConfigs } from "eslint-plugin-lit-a11y";
|
|
||||||
|
|
||||||
const _filename = fileURLToPath(import.meta.url);
|
const _filename = fileURLToPath(import.meta.url);
|
||||||
const _dirname = path.dirname(_filename);
|
const _dirname = path.dirname(_filename);
|
||||||
@@ -22,14 +21,13 @@ const compat = new FlatCompat({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default tseslint.config(
|
export default tseslint.config(
|
||||||
...compat.extends("airbnb-base"),
|
...compat.extends("airbnb-base", "plugin:lit-a11y/recommended"),
|
||||||
eslintConfigPrettier,
|
eslintConfigPrettier,
|
||||||
litConfigs["flat/all"],
|
litConfigs["flat/all"],
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
tseslint.configs.strict,
|
tseslint.configs.strict,
|
||||||
tseslint.configs.stylistic,
|
tseslint.configs.stylistic,
|
||||||
wcConfigs["flat/recommended"],
|
wcConfigs["flat/recommended"],
|
||||||
a11yConfigs.recommended,
|
|
||||||
{
|
{
|
||||||
plugins: {
|
plugins: {
|
||||||
"unused-imports": unusedImports,
|
"unused-imports": unusedImports,
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import type { Button } from "@material/mwc-button";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { html, LitElement, css, nothing } from "lit";
|
import { html, LitElement, css, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-button";
|
|
||||||
import type { HaButton } from "../../../src/components/ha-button";
|
|
||||||
|
|
||||||
@customElement("demo-black-white-row")
|
@customElement("demo-black-white-row")
|
||||||
class DemoBlackWhiteRow extends LitElement {
|
class DemoBlackWhiteRow extends LitElement {
|
||||||
@@ -25,9 +25,12 @@ class DemoBlackWhiteRow extends LitElement {
|
|||||||
<slot name="light"></slot>
|
<slot name="light"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-button .disabled=${this.disabled} @click=${this.handleSubmit}>
|
<mwc-button
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
@click=${this.handleSubmit}
|
||||||
|
>
|
||||||
Submit
|
Submit
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,9 +40,12 @@ class DemoBlackWhiteRow extends LitElement {
|
|||||||
<slot name="dark"></slot>
|
<slot name="dark"></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-button .disabled=${this.disabled} @click=${this.handleSubmit}>
|
<mwc-button
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
@click=${this.handleSubmit}
|
||||||
|
>
|
||||||
Submit
|
Submit
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
${this.value
|
${this.value
|
||||||
@@ -68,7 +74,7 @@ class DemoBlackWhiteRow extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(ev) {
|
handleSubmit(ev) {
|
||||||
const content = (ev.target as HaButton).closest(".content")!;
|
const content = (ev.target as Button).closest(".content")!;
|
||||||
fireEvent(this, "submitted" as any, {
|
fireEvent(this, "submitted" as any, {
|
||||||
slot: content.classList.contains("light") ? "light" : "dark",
|
slot: content.classList.contains("light") ? "light" : "dark",
|
||||||
});
|
});
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/dialogs/more-info/more-info-content";
|
import "../../../src/dialogs/more-info/more-info-content";
|
||||||
import "../../../src/state-summary/state-card-content";
|
import "../../../src/state-summary/state-card-content";
|
||||||
import "../ha-demo-options";
|
import "../ha-demo-options";
|
||||||
import type { HomeAssistant } from "../../../src/types";
|
import type { HomeAssistant } from "../../../src/types";
|
||||||
import { computeShowNewMoreInfo } from "../../../src/dialogs/more-info/const";
|
|
||||||
|
|
||||||
@customElement("demo-more-info")
|
@customElement("demo-more-info")
|
||||||
class DemoMoreInfo extends LitElement {
|
class DemoMoreInfo extends LitElement {
|
||||||
@@ -22,13 +21,11 @@ class DemoMoreInfo extends LitElement {
|
|||||||
<div class="root">
|
<div class="root">
|
||||||
<div id="card">
|
<div id="card">
|
||||||
<ha-card>
|
<ha-card>
|
||||||
${!computeShowNewMoreInfo(state)
|
<state-card-content
|
||||||
? html`<state-card-content
|
|
||||||
.stateObj=${state}
|
.stateObj=${state}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
in-dialog
|
in-dialog
|
||||||
></state-card-content>`
|
></state-card-content>
|
||||||
: nothing}
|
|
||||||
|
|
||||||
<more-info-content
|
<more-info-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@@ -38,12 +38,12 @@ class PageDescription extends HaMarkdown {
|
|||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
font-size: 42px;
|
font-size: 42px;
|
||||||
line-height: var(--ha-line-height-condensed);
|
line-height: 56px;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: var(--ha-font-size-l);
|
font-size: var(--ha-font-size-l);
|
||||||
line-height: var(--ha-line-height-normal);
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
.root {
|
.root {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
|
@@ -1106,7 +1106,7 @@ export default {
|
|||||||
friendly_name: "Philips Hue",
|
friendly_name: "Philips Hue",
|
||||||
entity_picture: null,
|
entity_picture: null,
|
||||||
description:
|
description:
|
||||||
"Press the button on the bridge to register Philips Hue with Home Assistant.",
|
"Press the button on the bridge to register Philips Hue with Home Assistant.\n\n",
|
||||||
submit_caption: "I have pressed the button",
|
submit_caption: "I have pressed the button",
|
||||||
},
|
},
|
||||||
last_changed: "2018-07-19T10:44:46.515160+00:00",
|
last_changed: "2018-07-19T10:44:46.515160+00:00",
|
||||||
|
@@ -17,10 +17,6 @@ export const createMediaPlayerEntities = () => [
|
|||||||
new Date().getTime() - 23000
|
new Date().getTime() - 23000
|
||||||
).toISOString(),
|
).toISOString(),
|
||||||
volume_level: 0.5,
|
volume_level: 0.5,
|
||||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
|
||||||
source: "AirPlay",
|
|
||||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
|
||||||
sound_mode: "Music",
|
|
||||||
}),
|
}),
|
||||||
getEntity("media_player", "music_playing", "playing", {
|
getEntity("media_player", "music_playing", "playing", {
|
||||||
friendly_name: "Playing The Music",
|
friendly_name: "Playing The Music",
|
||||||
@@ -28,8 +24,8 @@ export const createMediaPlayerEntities = () => [
|
|||||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||||
media_artist: "Technohead",
|
media_artist: "Technohead",
|
||||||
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
|
||||||
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media + Grouping
|
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
|
||||||
supported_features: 784959,
|
supported_features: 195135,
|
||||||
entity_picture: "/images/album_cover.jpg",
|
entity_picture: "/images/album_cover.jpg",
|
||||||
media_duration: 300,
|
media_duration: 300,
|
||||||
media_position: 0,
|
media_position: 0,
|
||||||
@@ -38,9 +34,6 @@ export const createMediaPlayerEntities = () => [
|
|||||||
new Date().getTime() - 23000
|
new Date().getTime() - 23000
|
||||||
).toISOString(),
|
).toISOString(),
|
||||||
volume_level: 0.5,
|
volume_level: 0.5,
|
||||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
|
||||||
sound_mode: "Music",
|
|
||||||
group_members: ["media_player.playing", "media_player.stream_playing"],
|
|
||||||
}),
|
}),
|
||||||
getEntity("media_player", "stream_playing", "playing", {
|
getEntity("media_player", "stream_playing", "playing", {
|
||||||
friendly_name: "Playing the Stream",
|
friendly_name: "Playing the Stream",
|
||||||
@@ -156,18 +149,15 @@ export const createMediaPlayerEntities = () => [
|
|||||||
}),
|
}),
|
||||||
getEntity("media_player", "receiver_on", "on", {
|
getEntity("media_player", "receiver_on", "on", {
|
||||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
|
||||||
volume_level: 0.63,
|
volume_level: 0.63,
|
||||||
is_volume_muted: false,
|
is_volume_muted: false,
|
||||||
source: "TV",
|
source: "TV",
|
||||||
sound_mode: "Movie",
|
|
||||||
friendly_name: "Receiver (selectable sources)",
|
friendly_name: "Receiver (selectable sources)",
|
||||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||||
supported_features: 84364,
|
supported_features: 84364,
|
||||||
}),
|
}),
|
||||||
getEntity("media_player", "receiver_off", "off", {
|
getEntity("media_player", "receiver_off", "off", {
|
||||||
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
source_list: ["AirPlay", "Blu-Ray", "TV", "USB", "iPod (USB)"],
|
||||||
sound_mode_list: ["Movie", "Music", "Game", "Pure Audio"],
|
|
||||||
friendly_name: "Receiver (selectable sources)",
|
friendly_name: "Receiver (selectable sources)",
|
||||||
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
// Volume Set + Volume Mute + On + Off + Select Source + Play + Sound Mode
|
||||||
supported_features: 84364,
|
supported_features: 84364,
|
||||||
|
@@ -208,7 +208,7 @@ class HaGallery extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar a[active]::before {
|
.sidebar a[active]::before {
|
||||||
border-radius: var(--ha-border-radius-lg);
|
border-radius: 12px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 2px;
|
right: 2px;
|
||||||
@@ -241,7 +241,7 @@ class HaGallery extends LitElement {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: var(--ha-border-radius-lg);
|
border-radius: 12px;
|
||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,12 +252,12 @@ class HaGallery extends LitElement {
|
|||||||
.page-footer .header {
|
.page-footer .header {
|
||||||
font-size: var(--ha-font-size-l);
|
font-size: var(--ha-font-size-l);
|
||||||
font-weight: var(--ha-font-weight-medium);
|
font-weight: var(--ha-font-weight-medium);
|
||||||
line-height: var(--ha-line-height-normal);
|
line-height: 28px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-footer .secondary {
|
.page-footer .secondary {
|
||||||
line-height: var(--ha-line-height-normal);
|
line-height: 23px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@ import { HaDeviceAction } from "../../../../src/panels/config/automation/action/
|
|||||||
import { HaEventAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-event";
|
import { HaEventAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-event";
|
||||||
import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if";
|
import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if";
|
||||||
import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel";
|
import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel";
|
||||||
|
import { HaPlayMediaAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-play_media";
|
||||||
import { HaRepeatAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-repeat";
|
import { HaRepeatAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-repeat";
|
||||||
import { HaSequenceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-sequence";
|
import { HaSequenceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-sequence";
|
||||||
import { HaServiceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-service";
|
import { HaServiceAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-service";
|
||||||
@@ -31,6 +32,7 @@ const SCHEMAS: { name: string; actions: Action[] }[] = [
|
|||||||
{ name: "Service", actions: [HaServiceAction.defaultConfig] },
|
{ name: "Service", actions: [HaServiceAction.defaultConfig] },
|
||||||
{ name: "Condition", actions: [HaConditionAction.defaultConfig] },
|
{ name: "Condition", actions: [HaConditionAction.defaultConfig] },
|
||||||
{ name: "Delay", actions: [HaDelayAction.defaultConfig] },
|
{ name: "Delay", actions: [HaDelayAction.defaultConfig] },
|
||||||
|
{ name: "Play media", actions: [HaPlayMediaAction.defaultConfig] },
|
||||||
{ name: "Wait", actions: [HaWaitAction.defaultConfig] },
|
{ name: "Wait", actions: [HaWaitAction.defaultConfig] },
|
||||||
{ name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] },
|
{ name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] },
|
||||||
{ name: "Repeat", actions: [HaRepeatAction.defaultConfig] },
|
{ name: "Repeat", actions: [HaRepeatAction.defaultConfig] },
|
||||||
|
@@ -147,13 +147,13 @@ The `title ` option should not be used without a description.
|
|||||||
|
|
||||||
<ha-alert alert-type="success">
|
<ha-alert alert-type="success">
|
||||||
This is a success alert — check it out!
|
This is a success alert — check it out!
|
||||||
<ha-button slot="action">Undo</ha-button>
|
<mwc-button slot="action" label="Undo"></mwc-button>
|
||||||
</ha-alert>
|
</ha-alert>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<ha-alert alert-type="success">
|
<ha-alert alert-type="success">
|
||||||
This is a success alert — check it out!
|
This is a success alert — check it out!
|
||||||
<ha-button slot="action">Undo</ha-button>
|
<mwc-button slot="action" label="Undo"></mwc-button>
|
||||||
</ha-alert>
|
</ha-alert>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import "../../../../src/components/ha-logo-svg";
|
import "../../../../src/components/ha-logo-svg";
|
||||||
|
|
||||||
const alerts: {
|
const alerts: {
|
||||||
@@ -78,13 +78,13 @@ const alerts: {
|
|||||||
title: "Error with action",
|
title: "Error with action",
|
||||||
description: "This is a test error alert with action",
|
description: "This is a test error alert with action",
|
||||||
type: "error",
|
type: "error",
|
||||||
actionSlot: html`<ha-button size="small" slot="action">restart</ha-button>`,
|
actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Unsaved data",
|
title: "Unsaved data",
|
||||||
description: "You have unsaved data",
|
description: "You have unsaved data",
|
||||||
type: "warning",
|
type: "warning",
|
||||||
actionSlot: html`<ha-button size="small" slot="action">save</ha-button>`,
|
actionSlot: html`<mwc-button slot="action" label="save"></mwc-button>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Slotted icon",
|
title: "Slotted icon",
|
||||||
@@ -108,7 +108,7 @@ const alerts: {
|
|||||||
title: "Slotted action",
|
title: "Slotted action",
|
||||||
description: "Alert with slotted action",
|
description: "Alert with slotted action",
|
||||||
type: "info",
|
type: "info",
|
||||||
actionSlot: html`<ha-button slot="action">action</ha-button>`,
|
actionSlot: html`<mwc-button slot="action" label="action"></mwc-button>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Dismissable information (RTL)",
|
description: "Dismissable information (RTL)",
|
||||||
@@ -120,7 +120,7 @@ const alerts: {
|
|||||||
title: "Error with action",
|
title: "Error with action",
|
||||||
description: "This is a test error alert with action (RTL)",
|
description: "This is a test error alert with action (RTL)",
|
||||||
type: "error",
|
type: "error",
|
||||||
actionSlot: html`<ha-button slot="action">restart</ha-button>`,
|
actionSlot: html`<mwc-button slot="action" label="restart"></mwc-button>`,
|
||||||
rtl: true,
|
rtl: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -211,7 +211,7 @@ export class DemoHaAlert extends LitElement {
|
|||||||
max-height: 24px;
|
max-height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
ha-button {
|
mwc-button {
|
||||||
--mdc-theme-primary: var(--primary-text-color);
|
--mdc-theme-primary: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -117,7 +117,7 @@ export class DemoHaBadge extends LitElement {
|
|||||||
}
|
}
|
||||||
.card-content {
|
.card-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--ha-space-6);
|
gap: 24px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -1,67 +0,0 @@
|
|||||||
---
|
|
||||||
title: Button
|
|
||||||
---
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.wrapper {
|
|
||||||
display: flex;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
# Button `<ha-button>`
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
<div class="wrapper">
|
|
||||||
<ha-button>
|
|
||||||
simple button
|
|
||||||
</ha-button>
|
|
||||||
<ha-button appearance="plain">
|
|
||||||
plain button
|
|
||||||
</ha-button>
|
|
||||||
<ha-button appearance="filled">
|
|
||||||
filled button
|
|
||||||
</ha-button>
|
|
||||||
|
|
||||||
<ha-button size="small">
|
|
||||||
small
|
|
||||||
</ha-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ha-button> simple button </ha-button>
|
|
||||||
|
|
||||||
<ha-button size="small"> small </ha-button>
|
|
||||||
```
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
This component is based on the webawesome button component.
|
|
||||||
Check the [webawesome documentation](https://webawesome.com/docs/components/button/) for more details.
|
|
||||||
|
|
||||||
**Slots**
|
|
||||||
|
|
||||||
- default slot: Label of the button
|
|
||||||
` - no default
|
|
||||||
- `start`: The prefix container (usually for icons).
|
|
||||||
` - no default
|
|
||||||
- `end`: The suffix container (usually for icons).
|
|
||||||
` - no default
|
|
||||||
|
|
||||||
**Properties/Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
|
||||||
| ---------- | ---------------------------------------------- | -------- | --------------------------------------------------------------------------------- |
|
|
||||||
| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. |
|
|
||||||
| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. |
|
|
||||||
| size | "small"/"medium" | "medium" | Sets the button size. |
|
|
||||||
| loading | Boolean | false | Shows a loading indicator instead of the buttons label and disable buttons click. |
|
|
||||||
| disabled | Boolean | false | Disables the button and prevents user interaction. |
|
|
||||||
|
|
||||||
**CSS Custom Properties**
|
|
||||||
|
|
||||||
- `--ha-button-height` - Height of the button.
|
|
||||||
- `--ha-button-border-radius` - Border radius of the button. Defaults to `var(--ha-border-radius-pill)`.
|
|
@@ -1,171 +0,0 @@
|
|||||||
import { mdiHome } from "@mdi/js";
|
|
||||||
import type { TemplateResult } from "lit";
|
|
||||||
import { css, html, LitElement } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
|
||||||
import { titleCase } from "../../../../src/common/string/title-case";
|
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-svg-icon";
|
|
||||||
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
|
||||||
|
|
||||||
const appearances = ["accent", "filled", "plain"];
|
|
||||||
const variants = ["brand", "danger", "neutral", "warning", "success"];
|
|
||||||
|
|
||||||
@customElement("demo-components-ha-button")
|
|
||||||
export class DemoHaButton extends LitElement {
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
${["light", "dark"].map(
|
|
||||||
(mode) => html`
|
|
||||||
<div class=${mode}>
|
|
||||||
<ha-card header="ha-button in ${mode}">
|
|
||||||
<div class="card-content">
|
|
||||||
${variants.map(
|
|
||||||
(variant) => html`
|
|
||||||
<div>
|
|
||||||
${appearances.map(
|
|
||||||
(appearance) => html`
|
|
||||||
<ha-button
|
|
||||||
.appearance=${appearance}
|
|
||||||
.variant=${variant}
|
|
||||||
>
|
|
||||||
<ha-svg-icon
|
|
||||||
.path=${mdiHomeAssistant}
|
|
||||||
slot="start"
|
|
||||||
></ha-svg-icon>
|
|
||||||
${titleCase(`${variant} ${appearance}`)}
|
|
||||||
<ha-svg-icon
|
|
||||||
.path=${mdiHome}
|
|
||||||
slot="end"
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-button>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
${appearances.map(
|
|
||||||
(appearance) => html`
|
|
||||||
<ha-button
|
|
||||||
.appearance=${appearance}
|
|
||||||
.variant=${variant}
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
${titleCase(`${variant} ${appearance}`)}
|
|
||||||
</ha-button>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
${appearances.map(
|
|
||||||
(appearance) => html`
|
|
||||||
<ha-button
|
|
||||||
.appearance=${appearance}
|
|
||||||
.variant=${variant}
|
|
||||||
loading
|
|
||||||
>
|
|
||||||
<ha-svg-icon
|
|
||||||
.path=${mdiHomeAssistant}
|
|
||||||
slot="start"
|
|
||||||
></ha-svg-icon>
|
|
||||||
${titleCase(`${variant} ${appearance}`)}
|
|
||||||
<ha-svg-icon
|
|
||||||
.path=${mdiHome}
|
|
||||||
slot="end"
|
|
||||||
></ha-svg-icon>
|
|
||||||
</ha-button>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
${variants.map(
|
|
||||||
(variant) => html`
|
|
||||||
<div>
|
|
||||||
${appearances.map(
|
|
||||||
(appearance) => html`
|
|
||||||
<ha-button
|
|
||||||
.variant=${variant}
|
|
||||||
.appearance=${appearance}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
${titleCase(`${appearance}`)}
|
|
||||||
</ha-button>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
${appearances.map(
|
|
||||||
(appearance) => html`
|
|
||||||
<ha-button
|
|
||||||
.variant=${variant}
|
|
||||||
.appearance=${appearance}
|
|
||||||
size="small"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
${titleCase(`${appearance}`)}
|
|
||||||
</ha-button>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated(changedProps) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
applyThemesOnElement(
|
|
||||||
this.shadowRoot!.querySelector(".dark"),
|
|
||||||
{
|
|
||||||
default_theme: "default",
|
|
||||||
default_dark_theme: "default",
|
|
||||||
themes: {},
|
|
||||||
darkMode: true,
|
|
||||||
theme: "default",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.dark,
|
|
||||||
.light {
|
|
||||||
display: block;
|
|
||||||
background-color: var(--primary-background-color);
|
|
||||||
padding: 0 50px;
|
|
||||||
}
|
|
||||||
.button {
|
|
||||||
padding: unset;
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--ha-space-6);
|
|
||||||
}
|
|
||||||
.card-content div {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--ha-space-2);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-components-ha-button": DemoHaButton;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -9,10 +9,10 @@ import { css, html, LitElement } from "lit";
|
|||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-control-button";
|
import "../../../../src/components/ha-control-button";
|
||||||
import "../../../../src/components/ha-control-button-group";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
import "../../../../src/components/ha-svg-icon";
|
||||||
|
import "../../../../src/components/ha-control-button-group";
|
||||||
|
|
||||||
interface Button {
|
interface Button {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -156,17 +156,17 @@ export class DemoHaBarButton extends LitElement {
|
|||||||
--control-button-icon-color: var(--primary-color);
|
--control-button-icon-color: var(--primary-color);
|
||||||
--control-button-background-color: var(--primary-color);
|
--control-button-background-color: var(--primary-color);
|
||||||
--control-button-background-opacity: 0.2;
|
--control-button-background-opacity: 0.2;
|
||||||
--control-button-border-radius: var(--ha-border-radius-xl);
|
--control-button-border-radius: 18px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
.custom-group {
|
.custom-group {
|
||||||
--control-button-group-thickness: 100px;
|
--control-button-group-thickness: 100px;
|
||||||
--control-button-group-border-radius: var(--ha-border-radius-6xl);
|
--control-button-group-border-radius: 36px;
|
||||||
--control-button-group-spacing: 20px;
|
--control-button-group-spacing: 20px;
|
||||||
}
|
}
|
||||||
.custom-group ha-control-button {
|
.custom-group ha-control-button {
|
||||||
--control-button-border-radius: var(--ha-border-radius-xl);
|
--control-button-border-radius: 18px;
|
||||||
--mdc-icon-size: 32px;
|
--mdc-icon-size: 32px;
|
||||||
}
|
}
|
||||||
.vertical-buttons {
|
.vertical-buttons {
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { LitElement, css, html } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
|
||||||
import { repeat } from "lit/directives/repeat";
|
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-control-number-buttons";
|
import "../../../../src/components/ha-control-number-buttons";
|
||||||
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
|
||||||
const buttons: {
|
const buttons: {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -94,7 +94,7 @@ export class DemoHarControlNumberButtons extends LitElement {
|
|||||||
--control-number-buttons-background-color: #2196f3;
|
--control-number-buttons-background-color: #2196f3;
|
||||||
--control-number-buttons-background-opacity: 0.1;
|
--control-number-buttons-background-opacity: 0.1;
|
||||||
--control-number-buttons-thickness: 100px;
|
--control-number-buttons-thickness: 100px;
|
||||||
--control-number-buttons-border-radius: var(--ha-border-radius-6xl);
|
--control-number-buttons-border-radius: 36px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -131,7 +131,7 @@ export class DemoHaControlSelectMenu extends LitElement {
|
|||||||
--control-button-icon-color: var(--primary-color);
|
--control-button-icon-color: var(--primary-color);
|
||||||
--control-button-background-color: var(--primary-color);
|
--control-button-background-color: var(--primary-color);
|
||||||
--control-button-background-opacity: 0.2;
|
--control-button-background-opacity: 0.2;
|
||||||
--control-button-border-radius: var(--ha-border-radius-xl);
|
--control-button-border-radius: 18px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
@@ -135,7 +135,7 @@ export class DemoHaControlSelect extends LitElement {
|
|||||||
.options=${options}
|
.options=${options}
|
||||||
class=${ifDefined(config.class)}
|
class=${ifDefined(config.class)}
|
||||||
@value-changed=${this.handleValueChanged}
|
@value-changed=${this.handleValueChanged}
|
||||||
.label=${label}
|
aria-labelledby=${id}
|
||||||
?disabled=${config.disabled}
|
?disabled=${config.disabled}
|
||||||
>
|
>
|
||||||
</ha-control-select>
|
</ha-control-select>
|
||||||
@@ -156,7 +156,7 @@ export class DemoHaControlSelect extends LitElement {
|
|||||||
vertical
|
vertical
|
||||||
class=${ifDefined(config.class)}
|
class=${ifDefined(config.class)}
|
||||||
@value-changed=${this.handleValueChanged}
|
@value-changed=${this.handleValueChanged}
|
||||||
.label=${label}
|
aria-labelledby=${id}
|
||||||
?disabled=${config.disabled}
|
?disabled=${config.disabled}
|
||||||
>
|
>
|
||||||
</ha-control-select>
|
</ha-control-select>
|
||||||
@@ -187,7 +187,7 @@ export class DemoHaControlSelect extends LitElement {
|
|||||||
--mdc-icon-size: 24px;
|
--mdc-icon-size: 24px;
|
||||||
--control-select-color: var(--state-fan-active-color);
|
--control-select-color: var(--state-fan-active-color);
|
||||||
--control-select-thickness: 130px;
|
--control-select-thickness: 130px;
|
||||||
--control-select-border-radius: var(--ha-border-radius-6xl);
|
--control-select-border-radius: 36px;
|
||||||
}
|
}
|
||||||
.vertical-selects {
|
.vertical-selects {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
|
@@ -3,8 +3,8 @@ import { css, html, LitElement } from "lit";
|
|||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-control-slider";
|
import "../../../../src/components/ha-control-slider";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
|
||||||
const sliders: {
|
const sliders: {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -97,7 +97,7 @@ export class DemoHaBarSlider extends LitElement {
|
|||||||
class=${ifDefined(config.class)}
|
class=${ifDefined(config.class)}
|
||||||
@value-changed=${this.handleValueChanged}
|
@value-changed=${this.handleValueChanged}
|
||||||
@slider-moved=${this.handleSliderMoved}
|
@slider-moved=${this.handleSliderMoved}
|
||||||
.label=${label}
|
aria-labelledby=${id}
|
||||||
.unit=${config.unit}
|
.unit=${config.unit}
|
||||||
>
|
>
|
||||||
</ha-control-slider>
|
</ha-control-slider>
|
||||||
@@ -119,7 +119,7 @@ export class DemoHaBarSlider extends LitElement {
|
|||||||
class=${ifDefined(config.class)}
|
class=${ifDefined(config.class)}
|
||||||
@value-changed=${this.handleValueChanged}
|
@value-changed=${this.handleValueChanged}
|
||||||
@slider-moved=${this.handleSliderMoved}
|
@slider-moved=${this.handleSliderMoved}
|
||||||
.label=${label}
|
aria-label=${label}
|
||||||
.unit=${config.unit}
|
.unit=${config.unit}
|
||||||
>
|
>
|
||||||
</ha-control-slider>
|
</ha-control-slider>
|
||||||
@@ -151,7 +151,7 @@ export class DemoHaBarSlider extends LitElement {
|
|||||||
--control-slider-background: #ffcf4c;
|
--control-slider-background: #ffcf4c;
|
||||||
--control-slider-background-opacity: 0.2;
|
--control-slider-background-opacity: 0.2;
|
||||||
--control-slider-thickness: 130px;
|
--control-slider-thickness: 130px;
|
||||||
--control-slider-border-radius: var(--ha-border-radius-6xl);
|
--control-slider-border-radius: 36px;
|
||||||
}
|
}
|
||||||
.vertical-sliders {
|
.vertical-sliders {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
|
@@ -9,8 +9,8 @@ import { css, html, LitElement } from "lit";
|
|||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-control-switch";
|
import "../../../../src/components/ha-control-switch";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
|
||||||
const switches: {
|
const switches: {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -63,7 +63,7 @@ export class DemoHaControlSwitch extends LitElement {
|
|||||||
@change=${this.handleValueChanged}
|
@change=${this.handleValueChanged}
|
||||||
.pathOn=${mdiLightbulb}
|
.pathOn=${mdiLightbulb}
|
||||||
.pathOff=${mdiLightbulbOff}
|
.pathOff=${mdiLightbulbOff}
|
||||||
.label=${label}
|
aria-labelledby=${id}
|
||||||
?disabled=${config.disabled}
|
?disabled=${config.disabled}
|
||||||
?reversed=${config.reversed}
|
?reversed=${config.reversed}
|
||||||
>
|
>
|
||||||
@@ -84,7 +84,7 @@ export class DemoHaControlSwitch extends LitElement {
|
|||||||
vertical
|
vertical
|
||||||
class=${ifDefined(config.class)}
|
class=${ifDefined(config.class)}
|
||||||
@change=${this.handleValueChanged}
|
@change=${this.handleValueChanged}
|
||||||
.label=${label}
|
aria-label=${label}
|
||||||
.pathOn=${mdiGarageOpen}
|
.pathOn=${mdiGarageOpen}
|
||||||
.pathOff=${mdiGarage}
|
.pathOff=${mdiGarage}
|
||||||
?disabled=${config.disabled}
|
?disabled=${config.disabled}
|
||||||
@@ -118,7 +118,7 @@ export class DemoHaControlSwitch extends LitElement {
|
|||||||
--control-switch-on-color: var(--green-color);
|
--control-switch-on-color: var(--green-color);
|
||||||
--control-switch-off-color: var(--red-color);
|
--control-switch-off-color: var(--red-color);
|
||||||
--control-switch-thickness: 130px;
|
--control-switch-thickness: 130px;
|
||||||
--control-switch-border-radius: var(--ha-border-radius-6xl);
|
--control-switch-border-radius: 36px;
|
||||||
--control-switch-padding: 6px;
|
--control-switch-padding: 6px;
|
||||||
--mdc-icon-size: 24px;
|
--mdc-icon-size: 24px;
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
/* eslint-disable lit/no-template-arrow */
|
||||||
|
import "@material/mwc-button";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
|
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
title: Marquee Text
|
|
||||||
---
|
|
||||||
|
|
||||||
# Marquee Text `<ha-marquee-text>`
|
|
||||||
|
|
||||||
Marquee text component scrolls text horizontally if it overflows its container. It supports pausing on hover and customizable speed and pause duration.
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
<ha-marquee-text style="width: 200px;">
|
|
||||||
This is a long text that will scroll horizontally if it overflows the container.
|
|
||||||
</ha-marquee-text>
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ha-marquee-text style="width: 200px;">
|
|
||||||
This is a long text that will scroll horizontally if it overflows the
|
|
||||||
container.
|
|
||||||
</ha-marquee-text>
|
|
||||||
```
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
**Slots**
|
|
||||||
|
|
||||||
- default slot: The text content to be displayed and scrolled.
|
|
||||||
- no default
|
|
||||||
|
|
||||||
**Properties/Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
|
||||||
| -------------- | ------- | ------- | ---------------------------------------------------------------------------- |
|
|
||||||
| speed | number | `15` | The speed of the scrolling animation. Higher values result in faster scroll. |
|
|
||||||
| pause-on-hover | boolean | `true` | Whether to pause the scrolling animation when |
|
|
||||||
| pause-duration | number | `1000` | The delay in milliseconds before the scrolling animation starts/restarts. |
|
|
@@ -1,25 +0,0 @@
|
|||||||
import { css, LitElement } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-marquee-text";
|
|
||||||
|
|
||||||
@customElement("demo-components-ha-marquee-text")
|
|
||||||
export class DemoHaMarqueeText extends LitElement {
|
|
||||||
static styles = css`
|
|
||||||
ha-card {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-components-ha-marquee-text": DemoHaMarqueeText;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
title: Progress Button
|
|
||||||
---
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.wrapper {
|
|
||||||
display: flex;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
# Progress Button `<ha-progress-button>`
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
This component is a wrapper around `<ha-button>` that adds support for showing progress
|
|
||||||
|
|
||||||
**Slots**
|
|
||||||
|
|
||||||
- default slot: Label of the button
|
|
||||||
` - no default
|
|
||||||
|
|
||||||
**Properties/Attributes**
|
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
|
||||||
| ---------- | ---------------------------------------------- | --------- | -------------------------------------------------- |
|
|
||||||
| label | string | "accent" | Sets the button label. |
|
|
||||||
| disabled | Boolean | false | Disables the button if true. |
|
|
||||||
| progress | Boolean | false | Shows a progress indicator on the button. |
|
|
||||||
| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. |
|
|
||||||
| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. |
|
|
||||||
| iconPath | string | undefined | Sets the icon path for the button. |
|
|
@@ -1,139 +0,0 @@
|
|||||||
import type { TemplateResult } from "lit";
|
|
||||||
import { css, html, LitElement } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-svg-icon";
|
|
||||||
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
|
||||||
|
|
||||||
@customElement("demo-components-ha-progress-button")
|
|
||||||
export class DemoHaProgressButton extends LitElement {
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
${["light", "dark"].map(
|
|
||||||
(mode) => html`
|
|
||||||
<div class=${mode}>
|
|
||||||
<ha-card header="ha-progress-button in ${mode}">
|
|
||||||
<div class="card-content">
|
|
||||||
<ha-progress-button @click=${this._clickedSuccess}>
|
|
||||||
Success
|
|
||||||
</ha-progress-button>
|
|
||||||
<ha-progress-button @click=${this._clickedFail}>
|
|
||||||
Fail
|
|
||||||
</ha-progress-button>
|
|
||||||
<ha-progress-button size="small" @click=${this._clickedSuccess}>
|
|
||||||
small
|
|
||||||
</ha-progress-button>
|
|
||||||
<ha-progress-button
|
|
||||||
appearance="filled"
|
|
||||||
@click=${this._clickedSuccess}
|
|
||||||
>
|
|
||||||
filled
|
|
||||||
</ha-progress-button>
|
|
||||||
<ha-progress-button
|
|
||||||
appearance="plain"
|
|
||||||
@click=${this._clickedSuccess}
|
|
||||||
>
|
|
||||||
plain
|
|
||||||
</ha-progress-button>
|
|
||||||
<ha-progress-button
|
|
||||||
variant="warning"
|
|
||||||
@click=${this._clickedSuccess}
|
|
||||||
>
|
|
||||||
warning
|
|
||||||
</ha-progress-button>
|
|
||||||
<ha-progress-button
|
|
||||||
variant="neutral"
|
|
||||||
@click=${this._clickedSuccess}
|
|
||||||
label="with icon"
|
|
||||||
.iconPath=${mdiHomeAssistant}
|
|
||||||
>
|
|
||||||
With Icon
|
|
||||||
</ha-progress-button>
|
|
||||||
<ha-progress-button progress @click=${this._clickedSuccess}>
|
|
||||||
progress
|
|
||||||
</ha-progress-button>
|
|
||||||
<ha-progress-button disabled @click=${this._clickedSuccess}>
|
|
||||||
disabled
|
|
||||||
</ha-progress-button>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated(changedProps) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
applyThemesOnElement(
|
|
||||||
this.shadowRoot!.querySelector(".dark"),
|
|
||||||
{
|
|
||||||
default_theme: "default",
|
|
||||||
default_dark_theme: "default",
|
|
||||||
themes: {},
|
|
||||||
darkMode: true,
|
|
||||||
theme: "default",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _clickedSuccess(ev: CustomEvent): Promise<void> {
|
|
||||||
console.log("Clicked success");
|
|
||||||
const button = ev.currentTarget as any;
|
|
||||||
button.progress = true;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
button.actionSuccess();
|
|
||||||
button.progress = false;
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _clickedFail(ev: CustomEvent): Promise<void> {
|
|
||||||
const button = ev.currentTarget as any;
|
|
||||||
button.progress = true;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
button.actionError();
|
|
||||||
button.progress = false;
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.dark,
|
|
||||||
.light {
|
|
||||||
display: block;
|
|
||||||
background-color: var(--primary-background-color);
|
|
||||||
padding: 0 50px;
|
|
||||||
}
|
|
||||||
.button {
|
|
||||||
padding: unset;
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--ha-space-6);
|
|
||||||
}
|
|
||||||
.card-content div {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--ha-space-2);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-components-ha-progress-button": DemoHaProgressButton;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -131,7 +131,7 @@ export class DemoHaSelectBox extends LitElement {
|
|||||||
--mdc-icon-size: 24px;
|
--mdc-icon-size: 24px;
|
||||||
--control-select-color: var(--state-fan-active-color);
|
--control-select-color: var(--state-fan-active-color);
|
||||||
--control-select-thickness: 130px;
|
--control-select-thickness: 130px;
|
||||||
--control-select-border-radius: var(--ha-border-radius-6xl);
|
--control-select-border-radius: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.title {
|
p.title {
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
@@ -415,34 +416,6 @@ const SCHEMAS: {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
items: {
|
|
||||||
name: "Items",
|
|
||||||
selector: {
|
|
||||||
object: {
|
|
||||||
label_field: "name",
|
|
||||||
description_field: "value",
|
|
||||||
multiple: true,
|
|
||||||
fields: {
|
|
||||||
name: {
|
|
||||||
label: "Name",
|
|
||||||
selector: { text: {} },
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
label: "Value",
|
|
||||||
selector: {
|
|
||||||
number: {
|
|
||||||
mode: "slider",
|
|
||||||
min: 0,
|
|
||||||
max: 100,
|
|
||||||
unit_of_measurement: "%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
title: Slider
|
|
||||||
subtitle: A slider component for selecting a value from a range.
|
|
||||||
---
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.wrapper {
|
|
||||||
display: flex;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
# Slider `<ha-slider>`
|
|
||||||
|
|
||||||
## Implementation
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
<div class="wrapper">
|
|
||||||
<ha-slider size="small" with-markers min="0" max="8" value="4"></ha-slider>
|
|
||||||
<ha-slider size="medium"></ha-slider>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```html
|
|
||||||
<ha-slider size="small" with-markers min="0" max="8" value="4"></ha-slider>
|
|
||||||
<ha-slider size="medium"></ha-slider>
|
|
||||||
```
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
This component is based on the webawesome slider component.
|
|
||||||
Check the [webawesome documentation](https://webawesome.com/docs/components/slider/) for more details.
|
|
||||||
|
|
||||||
**CSS Custom Properties**
|
|
||||||
|
|
||||||
- `--ha-slider-track-size` - Height of the slider track. Defaults to `4px`.
|
|
||||||
- `--ha-slider-thumb-color` - Color of the slider thumb. Defaults to `var(--primary-color)`.
|
|
||||||
- `--ha-slider-indicator-color` - Color of the filled portion of the slider track. Defaults to `var(--primary-color)`.
|
|
@@ -1,100 +0,0 @@
|
|||||||
import type { TemplateResult } from "lit";
|
|
||||||
import { css, html, LitElement } from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators";
|
|
||||||
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
|
||||||
import "../../../../src/components/ha-bar";
|
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/components/ha-spinner";
|
|
||||||
import "../../../../src/components/ha-slider";
|
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
|
||||||
|
|
||||||
@customElement("demo-components-ha-slider")
|
|
||||||
export class DemoHaSlider extends LitElement {
|
|
||||||
@property({ attribute: false }) hass!: HomeAssistant;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
${["light", "dark"].map(
|
|
||||||
(mode) => html`
|
|
||||||
<div class=${mode}>
|
|
||||||
<ha-card header="ha-slider ${mode} demo">
|
|
||||||
<div class="card-content">
|
|
||||||
<span>Default (disabled)</span>
|
|
||||||
<ha-slider
|
|
||||||
disabled
|
|
||||||
min="0"
|
|
||||||
max="8"
|
|
||||||
value="4"
|
|
||||||
with-markers
|
|
||||||
></ha-slider>
|
|
||||||
<span>Small</span>
|
|
||||||
<ha-slider
|
|
||||||
size="small"
|
|
||||||
min="0"
|
|
||||||
max="8"
|
|
||||||
value="4"
|
|
||||||
with-markers
|
|
||||||
></ha-slider>
|
|
||||||
<span>Medium</span>
|
|
||||||
<ha-slider
|
|
||||||
size="medium"
|
|
||||||
min="0"
|
|
||||||
max="8"
|
|
||||||
value="4"
|
|
||||||
with-markers
|
|
||||||
></ha-slider>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated(changedProps) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
applyThemesOnElement(
|
|
||||||
this.shadowRoot!.querySelector(".dark"),
|
|
||||||
{
|
|
||||||
default_theme: "default",
|
|
||||||
default_dark_theme: "default",
|
|
||||||
themes: {},
|
|
||||||
darkMode: true,
|
|
||||||
theme: "default",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.dark,
|
|
||||||
.light {
|
|
||||||
display: block;
|
|
||||||
background-color: var(--primary-background-color);
|
|
||||||
padding: 0 50px;
|
|
||||||
margin: 16px;
|
|
||||||
border-radius: var(--ha-border-radius-md);
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
.card-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--ha-space-6);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-components-ha-slider": DemoHaSlider;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -61,7 +61,7 @@ export class DemoHaSpinner extends LitElement {
|
|||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
padding: 0 50px;
|
padding: 0 50px;
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
border-radius: var(--ha-border-radius-md);
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
margin: 24px auto;
|
margin: 24px auto;
|
||||||
@@ -70,7 +70,7 @@ export class DemoHaSpinner extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--ha-space-6);
|
gap: 24px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -6,23 +6,21 @@ A tooltip's target is its _first child element_, so you should only wrap one ele
|
|||||||
|
|
||||||
Tooltips use `display: contents` so they won't interfere with how elements are positioned in a flex or grid layout.
|
Tooltips use `display: contents` so they won't interfere with how elements are positioned in a flex or grid layout.
|
||||||
|
|
||||||
<ha-button id="hover">Hover Me</ha-button>
|
<ha-tooltip content="This is a tooltip">
|
||||||
<ha-tooltip for="hover">
|
<ha-button>Hover Me</ha-button>
|
||||||
This is a tooltip
|
|
||||||
</ha-tooltip>
|
</ha-tooltip>
|
||||||
|
|
||||||
```
|
```
|
||||||
<ha-button id="hover">Hover Me</ha-button>
|
<ha-tooltip content="This is a tooltip">
|
||||||
<ha-tooltip for="hover">
|
<ha-button>Hover Me</ha-button>
|
||||||
This is a tooltip
|
|
||||||
</ha-tooltip>
|
</ha-tooltip>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
This element is based on webawesome `wa-tooltip` it only sets some css tokens and has a custom show/hide animation.
|
This element is based on shoelace `sl-tooltip` it only sets some css tokens and has a custom show/hide animation.
|
||||||
|
|
||||||
<a href="https://webawesome.com/docs/components/tooltip/" target="_blank" rel="noopener noreferrer">Webawesome documentation</a>
|
<a href="https://shoelace.style/components/tooltip" target="_blank" rel="noopener noreferrer">Shoelace documentation</a>
|
||||||
|
|
||||||
### HA style tokens
|
### HA style tokens
|
||||||
|
|
||||||
@@ -30,7 +28,7 @@ In your theme settings use this without the prefixed `--`.
|
|||||||
|
|
||||||
- `--ha-tooltip-border-radius` (Default: 4px)
|
- `--ha-tooltip-border-radius` (Default: 4px)
|
||||||
- `--ha-tooltip-arrow-size` (Default: 8px)
|
- `--ha-tooltip-arrow-size` (Default: 8px)
|
||||||
- `--wa-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
|
- `--sl-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
|
||||||
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
|
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
|
||||||
- `--wa-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
|
- `--sl-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
|
||||||
- `--wa-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)
|
- `--sl-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)
|
||||||
|
@@ -11,7 +11,6 @@ import { provideHass } from "../../../../src/fake_data/provide_hass";
|
|||||||
import "../../components/demo-cards";
|
import "../../components/demo-cards";
|
||||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||||
import { ClimateEntityFeature } from "../../../../src/data/climate";
|
import { ClimateEntityFeature } from "../../../../src/data/climate";
|
||||||
import { FanEntityFeature } from "../../../../src/data/fan";
|
|
||||||
|
|
||||||
const ENTITIES = [
|
const ENTITIES = [
|
||||||
getEntity("switch", "tv_outlet", "on", {
|
getEntity("switch", "tv_outlet", "on", {
|
||||||
@@ -101,15 +100,6 @@ const ENTITIES = [
|
|||||||
ClimateEntityFeature.FAN_MODE +
|
ClimateEntityFeature.FAN_MODE +
|
||||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
|
||||||
}),
|
}),
|
||||||
getEntity("fan", "fan_demo", "on", {
|
|
||||||
friendly_name: "Ceiling fan",
|
|
||||||
device_class: "fan",
|
|
||||||
direction: "reverse",
|
|
||||||
supported_features:
|
|
||||||
FanEntityFeature.DIRECTION +
|
|
||||||
FanEntityFeature.SET_SPEED +
|
|
||||||
FanEntityFeature.OSCILLATE,
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONFIGS = [
|
const CONFIGS = [
|
||||||
@@ -271,33 +261,6 @@ const CONFIGS = [
|
|||||||
- type: target-temperature
|
- type: target-temperature
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
heading: "Fan direction feature",
|
|
||||||
config: `
|
|
||||||
- type: tile
|
|
||||||
entity: fan.fan_demo
|
|
||||||
features:
|
|
||||||
- type: fan-direction
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: "Fan speed feature",
|
|
||||||
config: `
|
|
||||||
- type: tile
|
|
||||||
entity: fan.fan_demo
|
|
||||||
features:
|
|
||||||
- type: fan-speed
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
heading: "Fan oscillate feature",
|
|
||||||
config: `
|
|
||||||
- type: tile
|
|
||||||
entity: fan.fan_demo
|
|
||||||
features:
|
|
||||||
- type: fan-oscillate
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-lovelace-tile-card")
|
@customElement("demo-lovelace-tile-card")
|
||||||
|
@@ -5,13 +5,13 @@ import type {
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
|
||||||
import { computeDomain } from "../../../../src/common/entity/compute_domain";
|
import { computeDomain } from "../../../../src/common/entity/compute_domain";
|
||||||
import { computeStateDisplay } from "../../../../src/common/entity/compute_state_display";
|
import { computeStateDisplay } from "../../../../src/common/entity/compute_state_display";
|
||||||
import "../../../../src/components/data-table/ha-data-table";
|
import "../../../../src/components/data-table/ha-data-table";
|
||||||
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
||||||
import "../../../../src/components/entity/state-badge";
|
import "../../../../src/components/entity/state-badge";
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
|
import { mockIcons } from "../../../../demo/src/stubs/icons";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
|
|
||||||
const SENSOR_DEVICE_CLASSES = [
|
const SENSOR_DEVICE_CLASSES = [
|
||||||
@@ -434,7 +434,7 @@ export class DemoEntityState extends LitElement {
|
|||||||
display: block;
|
display: block;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
border-radius: var(--ha-border-radius-md);
|
border-radius: 10px;
|
||||||
background-color: rgb(--color);
|
background-color: rgb(--color);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import type { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
|
import type { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
|
||||||
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
|
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||||
@@ -13,16 +13,12 @@ export class DemoUtilLongPress extends LitElement {
|
|||||||
${[1, 2, 3].map(
|
${[1, 2, 3].map(
|
||||||
() => html`
|
() => html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<ha-button
|
<mwc-button
|
||||||
appearance="plain"
|
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
.actionHandler=${actionHandler({
|
.actionHandler=${actionHandler({})}
|
||||||
hasHold: true,
|
|
||||||
hasDoubleClick: true,
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
(long) press me!
|
(long) press me!
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
|
|
||||||
<textarea></textarea>
|
<textarea></textarea>
|
||||||
|
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: Fan
|
|
||||||
---
|
|
@@ -1,50 +0,0 @@
|
|||||||
import type { PropertyValues, TemplateResult } from "lit";
|
|
||||||
import { html, LitElement } from "lit";
|
|
||||||
import { customElement, property, query } from "lit/decorators";
|
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import "../../../../src/dialogs/more-info/more-info-content";
|
|
||||||
import { getEntity } from "../../../../src/fake_data/entity";
|
|
||||||
import type { MockHomeAssistant } from "../../../../src/fake_data/provide_hass";
|
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
|
||||||
import "../../components/demo-more-infos";
|
|
||||||
import { FanEntityFeature } from "../../../../src/data/fan";
|
|
||||||
|
|
||||||
const ENTITIES = [
|
|
||||||
getEntity("fan", "fan", "on", {
|
|
||||||
friendly_name: "Fan",
|
|
||||||
device_class: "fan",
|
|
||||||
supported_features:
|
|
||||||
FanEntityFeature.OSCILLATE +
|
|
||||||
FanEntityFeature.DIRECTION +
|
|
||||||
FanEntityFeature.SET_SPEED,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-more-info-fan")
|
|
||||||
class DemoMoreInfoFan extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
|
||||||
|
|
||||||
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<demo-more-infos
|
|
||||||
.hass=${this.hass}
|
|
||||||
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
|
||||||
></demo-more-infos>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected firstUpdated(changedProperties: PropertyValues) {
|
|
||||||
super.firstUpdated(changedProperties);
|
|
||||||
const hass = provideHass(this._demoRoot);
|
|
||||||
hass.updateTranslations(null, "en");
|
|
||||||
hass.addEntities(ENTITIES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-more-info-fan": DemoMoreInfoFan;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -11,10 +11,7 @@ import "../../../../src/components/ha-alert";
|
|||||||
import "../../../../src/components/ha-button-menu";
|
import "../../../../src/components/ha-button-menu";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
import type {
|
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||||
HaFormSchema,
|
|
||||||
HaFormDataContainer,
|
|
||||||
} from "../../../../src/components/ha-form/types";
|
|
||||||
import "../../../../src/components/ha-formfield";
|
import "../../../../src/components/ha-formfield";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import "../../../../src/components/ha-list-item";
|
import "../../../../src/components/ha-list-item";
|
||||||
@@ -36,7 +33,6 @@ import { haStyle } from "../../../../src/resources/styles";
|
|||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
import type { ObjectSelector, Selector } from "../../../../src/data/selector";
|
|
||||||
|
|
||||||
const SUPPORTED_UI_TYPES = [
|
const SUPPORTED_UI_TYPES = [
|
||||||
"string",
|
"string",
|
||||||
@@ -82,83 +78,41 @@ class HassioAddonConfig extends LitElement {
|
|||||||
|
|
||||||
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
|
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
|
||||||
|
|
||||||
private _getTranslationEntry(
|
public computeLabel = (entry: HaFormSchema): string =>
|
||||||
language: string,
|
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
||||||
entry: HaFormSchema,
|
?.name ||
|
||||||
options?: { path?: string[] }
|
this.addon.translations.en?.configuration?.[entry.name]?.name ||
|
||||||
) {
|
|
||||||
let parent = this.addon.translations[language]?.configuration;
|
|
||||||
if (!parent) return undefined;
|
|
||||||
if (options?.path) {
|
|
||||||
for (const key of options.path) {
|
|
||||||
parent = parent[key]?.fields;
|
|
||||||
if (!parent) return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parent[entry.name];
|
|
||||||
}
|
|
||||||
|
|
||||||
public computeLabel = (
|
|
||||||
entry: HaFormSchema,
|
|
||||||
_data: HaFormDataContainer,
|
|
||||||
options?: { path?: string[] }
|
|
||||||
): string =>
|
|
||||||
this._getTranslationEntry(this.hass.language, entry, options)?.name ||
|
|
||||||
this._getTranslationEntry("en", entry, options)?.name ||
|
|
||||||
entry.name;
|
entry.name;
|
||||||
|
|
||||||
public computeHelper = (
|
public computeHelper = (entry: HaFormSchema): string =>
|
||||||
entry: HaFormSchema,
|
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
||||||
options?: { path?: string[] }
|
|
||||||
): string =>
|
|
||||||
this._getTranslationEntry(this.hass.language, entry, options)
|
|
||||||
?.description ||
|
?.description ||
|
||||||
this._getTranslationEntry("en", entry, options)?.description ||
|
this.addon.translations.en?.configuration?.[entry.name]?.description ||
|
||||||
"";
|
"";
|
||||||
|
|
||||||
private _convertSchema = memoizeOne(
|
private _convertSchema = memoizeOne(
|
||||||
// Convert supervisor schema to selectors
|
// Convert supervisor schema to selectors
|
||||||
(schema: readonly HaFormSchema[]): HaFormSchema[] =>
|
(schema: Record<string, any>): HaFormSchema[] =>
|
||||||
this._convertSchemaElements(schema)
|
schema.map((entry) =>
|
||||||
);
|
entry.type === "select"
|
||||||
|
? {
|
||||||
private _convertSchemaElements(
|
|
||||||
schema: readonly HaFormSchema[]
|
|
||||||
): HaFormSchema[] {
|
|
||||||
return schema.map((entry) => this._convertSchemaElement(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _convertSchemaElement(entry: any): HaFormSchema {
|
|
||||||
if (entry.type === "schema" && !entry.multiple) {
|
|
||||||
return {
|
|
||||||
name: entry.name,
|
|
||||||
type: "expandable",
|
|
||||||
required: entry.required,
|
|
||||||
schema: this._convertSchemaElements(entry.schema),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const selector = this._convertSchemaElementToSelector(entry, false);
|
|
||||||
if (selector) {
|
|
||||||
return {
|
|
||||||
name: entry.name,
|
name: entry.name,
|
||||||
required: entry.required,
|
required: entry.required,
|
||||||
selector,
|
selector: { select: { options: entry.options } },
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return entry;
|
: entry.type === "string"
|
||||||
|
? entry.multiple
|
||||||
|
? {
|
||||||
|
name: entry.name,
|
||||||
|
required: entry.required,
|
||||||
|
selector: {
|
||||||
|
select: { options: [], multiple: true, custom_value: true },
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
private _convertSchemaElementToSelector(
|
|
||||||
entry: any,
|
|
||||||
force: boolean
|
|
||||||
): Selector | null {
|
|
||||||
if (entry.type === "select") {
|
|
||||||
return { select: { options: entry.options } };
|
|
||||||
}
|
|
||||||
if (entry.type === "string") {
|
|
||||||
return entry.multiple
|
|
||||||
? { select: { options: [], multiple: true, custom_value: true } }
|
|
||||||
: {
|
: {
|
||||||
|
name: entry.name,
|
||||||
|
required: entry.required,
|
||||||
|
selector: {
|
||||||
text: {
|
text: {
|
||||||
type: entry.format
|
type: entry.format
|
||||||
? entry.format
|
? entry.format
|
||||||
@@ -166,41 +120,36 @@ class HassioAddonConfig extends LitElement {
|
|||||||
? "password"
|
? "password"
|
||||||
: "text",
|
: "text",
|
||||||
},
|
},
|
||||||
};
|
|
||||||
}
|
|
||||||
if (entry.type === "boolean") {
|
|
||||||
return { boolean: {} };
|
|
||||||
}
|
|
||||||
if (entry.type === "schema") {
|
|
||||||
const fields: NonNullable<ObjectSelector["object"]>["fields"] = {};
|
|
||||||
for (const child_entry of entry.schema) {
|
|
||||||
fields[child_entry.name] = {
|
|
||||||
required: child_entry.required,
|
|
||||||
selector: this._convertSchemaElementToSelector(child_entry, true)!,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
object: {
|
|
||||||
multiple: entry.multiple,
|
|
||||||
fields,
|
|
||||||
},
|
},
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (entry.type === "float" || entry.type === "integer") {
|
: entry.type === "boolean"
|
||||||
return {
|
? {
|
||||||
|
name: entry.name,
|
||||||
|
required: entry.required,
|
||||||
|
selector: { boolean: {} },
|
||||||
|
}
|
||||||
|
: entry.type === "schema"
|
||||||
|
? {
|
||||||
|
name: entry.name,
|
||||||
|
required: entry.required,
|
||||||
|
selector: { object: {} },
|
||||||
|
}
|
||||||
|
: entry.type === "float" || entry.type === "integer"
|
||||||
|
? {
|
||||||
|
name: entry.name,
|
||||||
|
required: entry.required,
|
||||||
|
selector: {
|
||||||
number: {
|
number: {
|
||||||
mode: "box",
|
mode: "box",
|
||||||
step: entry.type === "float" ? "any" : undefined,
|
step: entry.type === "float" ? "any" : undefined,
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
}
|
|
||||||
if (force) {
|
|
||||||
return { object: {} };
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
: entry
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
private _filteredSchema = memoizeOne(
|
private _filteredShchema = memoizeOne(
|
||||||
(options: Record<string, unknown>, schema: HaFormSchema[]) =>
|
(options: Record<string, unknown>, schema: HaFormSchema[]) =>
|
||||||
schema.filter((entry) => entry.name in options || entry.required)
|
schema.filter((entry) => entry.name in options || entry.required)
|
||||||
);
|
);
|
||||||
@@ -212,7 +161,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
showForm &&
|
showForm &&
|
||||||
JSON.stringify(this.addon.schema) !==
|
JSON.stringify(this.addon.schema) !==
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
this._filteredSchema(this.addon.options, this.addon.schema!)
|
this._filteredShchema(this.addon.options, this.addon.schema!)
|
||||||
);
|
);
|
||||||
return html`
|
return html`
|
||||||
<h1>${this.addon.name}</h1>
|
<h1>${this.addon.name}</h1>
|
||||||
@@ -250,7 +199,6 @@ class HassioAddonConfig extends LitElement {
|
|||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${showForm
|
${showForm
|
||||||
? html`<ha-form
|
? html`<ha-form
|
||||||
.hass=${this.hass}
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.data=${this._options!}
|
.data=${this._options!}
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
@@ -259,7 +207,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
.schema=${this._convertSchema(
|
.schema=${this._convertSchema(
|
||||||
this._showOptional
|
this._showOptional
|
||||||
? this.addon.schema!
|
? this.addon.schema!
|
||||||
: this._filteredSchema(
|
: this._filteredShchema(
|
||||||
this.addon.options,
|
this.addon.options,
|
||||||
this.addon.schema!
|
this.addon.schema!
|
||||||
)
|
)
|
||||||
@@ -482,7 +430,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
font-family: var(--ha-card-header-font-family, inherit);
|
font-family: var(--ha-card-header-font-family, inherit);
|
||||||
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
||||||
letter-spacing: -0.012em;
|
letter-spacing: -0.012em;
|
||||||
line-height: var(--ha-line-height-expanded);
|
line-height: 48px;
|
||||||
padding: 12px 16px 16px;
|
padding: 12px 16px 16px;
|
||||||
display: block;
|
display: block;
|
||||||
margin-block: 0px;
|
margin-block: 0px;
|
||||||
|
@@ -99,8 +99,7 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
: nothing}
|
: nothing}
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
variant="danger"
|
class="warning"
|
||||||
appearance="plain"
|
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@click=${this._resetTapped}
|
@click=${this._resetTapped}
|
||||||
>
|
>
|
||||||
|
@@ -25,7 +25,6 @@ import type { CSSResultGroup, TemplateResult } from "lit";
|
|||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
@@ -188,13 +187,12 @@ class HassioAddonInfo extends LitElement {
|
|||||||
"addon.dashboard.protection_mode.content"
|
"addon.dashboard.protection_mode.content"
|
||||||
)}
|
)}
|
||||||
<ha-button
|
<ha-button
|
||||||
variant="danger"
|
|
||||||
slot="action"
|
slot="action"
|
||||||
@click=${this._protectionToggled}
|
.label=${this.supervisor.localize(
|
||||||
>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.protection_mode.enable"
|
"addon.dashboard.protection_mode.enable"
|
||||||
)}
|
)}
|
||||||
|
@click=${this._protectionToggled}
|
||||||
|
>
|
||||||
</ha-button>
|
</ha-button>
|
||||||
</ha-alert>
|
</ha-alert>
|
||||||
`
|
`
|
||||||
@@ -694,16 +692,14 @@ class HassioAddonInfo extends LitElement {
|
|||||||
? this._computeIsRunning
|
? this._computeIsRunning
|
||||||
? html`
|
? html`
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
variant="danger"
|
class="warning"
|
||||||
appearance="plain"
|
|
||||||
@click=${this._stopClicked}
|
@click=${this._stopClicked}
|
||||||
.disabled=${systemManaged && !this.controlEnabled}
|
.disabled=${systemManaged && !this.controlEnabled}
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("addon.dashboard.stop")}
|
${this.supervisor.localize("addon.dashboard.stop")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
variant="danger"
|
class="warning"
|
||||||
appearance="plain"
|
|
||||||
@click=${this._restartClicked}
|
@click=${this._restartClicked}
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("addon.dashboard.restart")}
|
${this.supervisor.localize("addon.dashboard.restart")}
|
||||||
@@ -713,60 +709,10 @@ class HassioAddonInfo extends LitElement {
|
|||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
@click=${this._startClicked}
|
@click=${this._startClicked}
|
||||||
.progress=${this.addon.state === "startup"}
|
.progress=${this.addon.state === "startup"}
|
||||||
appearance="plain"
|
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("addon.dashboard.start")}
|
${this.supervisor.localize("addon.dashboard.start")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
`
|
`
|
||||||
: nothing}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
${this.addon.version
|
|
||||||
? html`
|
|
||||||
<ha-progress-button
|
|
||||||
variant="danger"
|
|
||||||
appearance="plain"
|
|
||||||
@click=${this._uninstallClicked}
|
|
||||||
.disabled=${systemManaged && !this.controlEnabled}
|
|
||||||
>
|
|
||||||
${this.supervisor.localize("addon.dashboard.uninstall")}
|
|
||||||
</ha-progress-button>
|
|
||||||
${this.addon.build
|
|
||||||
? html`
|
|
||||||
<ha-progress-button
|
|
||||||
variant="danger"
|
|
||||||
appearance="plain"
|
|
||||||
@click=${this._rebuildClicked}
|
|
||||||
>
|
|
||||||
${this.supervisor.localize("addon.dashboard.rebuild")}
|
|
||||||
</ha-progress-button>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
${this._computeShowWebUI || this._computeShowIngressUI
|
|
||||||
? html`
|
|
||||||
<ha-button
|
|
||||||
href=${ifDefined(
|
|
||||||
!this._computeShowIngressUI
|
|
||||||
? this._pathWebui!
|
|
||||||
: nothing
|
|
||||||
)}
|
|
||||||
target=${ifDefined(
|
|
||||||
!this._computeShowIngressUI ? "_blank" : nothing
|
|
||||||
)}
|
|
||||||
rel=${ifDefined(
|
|
||||||
!this._computeShowIngressUI ? "noopener" : nothing
|
|
||||||
)}
|
|
||||||
@click=${!this._computeShowWebUI
|
|
||||||
? this._openIngress
|
|
||||||
: undefined}
|
|
||||||
>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.dashboard.open_web_ui"
|
|
||||||
)}
|
|
||||||
</ha-button>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
`
|
|
||||||
: html`
|
: html`
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
.disabled=${!this.addon.available}
|
.disabled=${!this.addon.available}
|
||||||
@@ -776,12 +722,58 @@ class HassioAddonInfo extends LitElement {
|
|||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
${this.addon.version
|
||||||
|
? html` ${this._computeShowWebUI
|
||||||
|
? html`
|
||||||
|
<a
|
||||||
|
href=${this._pathWebui!}
|
||||||
|
tabindex="-1"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
<ha-button>
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.open_web_ui"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${this._computeShowIngressUI
|
||||||
|
? html`
|
||||||
|
<ha-button @click=${this._openIngress}>
|
||||||
|
${this.supervisor.localize(
|
||||||
|
"addon.dashboard.open_web_ui"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
<ha-progress-button
|
||||||
|
class="warning"
|
||||||
|
@click=${this._uninstallClicked}
|
||||||
|
.disabled=${systemManaged && !this.controlEnabled}
|
||||||
|
>
|
||||||
|
${this.supervisor.localize("addon.dashboard.uninstall")}
|
||||||
|
</ha-progress-button>
|
||||||
|
${this.addon.build
|
||||||
|
? html`
|
||||||
|
<ha-progress-button
|
||||||
|
class="warning"
|
||||||
|
@click=${this._rebuildClicked}
|
||||||
|
>
|
||||||
|
${this.supervisor.localize("addon.dashboard.rebuild")}
|
||||||
|
</ha-progress-button>
|
||||||
|
`
|
||||||
|
: nothing}`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
|
||||||
${this.addon.long_description
|
${this.addon.long_description
|
||||||
? html`
|
? html`
|
||||||
<ha-card class="long-description" outlined>
|
<ha-card outlined>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
.content=${this.addon.long_description}
|
.content=${this.addon.long_description}
|
||||||
@@ -1154,17 +1146,15 @@ class HassioAddonInfo extends LitElement {
|
|||||||
),
|
),
|
||||||
dismissText: this.supervisor.localize("common.cancel"),
|
dismissText: this.supervisor.localize("common.cancel"),
|
||||||
});
|
});
|
||||||
button.actionError();
|
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
button.actionError();
|
|
||||||
button.progress = false;
|
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to validate addon configuration",
|
title: "Failed to validate addon configuration",
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
|
button.progress = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1178,15 +1168,11 @@ class HassioAddonInfo extends LitElement {
|
|||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
button.actionError();
|
|
||||||
button.progress = false;
|
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize("addon.dashboard.action_error.start"),
|
title: this.supervisor.localize("addon.dashboard.action_error.start"),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
button.actionSuccess();
|
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1242,7 +1228,6 @@ class HassioAddonInfo extends LitElement {
|
|||||||
path: "uninstall",
|
path: "uninstall",
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
button.actionSuccess();
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this.supervisor.localize(
|
title: this.supervisor.localize(
|
||||||
@@ -1250,7 +1235,6 @@ class HassioAddonInfo extends LitElement {
|
|||||||
),
|
),
|
||||||
text: extractApiErrorMessage(err),
|
text: extractApiErrorMessage(err),
|
||||||
});
|
});
|
||||||
button.actionError();
|
|
||||||
}
|
}
|
||||||
button.progress = false;
|
button.progress = false;
|
||||||
}
|
}
|
||||||
@@ -1333,9 +1317,6 @@ class HassioAddonInfo extends LitElement {
|
|||||||
.description a {
|
.description a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
.long-description {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
ha-assist-chip {
|
ha-assist-chip {
|
||||||
--md-sys-color-primary: var(--text-primary-color);
|
--md-sys-color-primary: var(--text-primary-color);
|
||||||
--md-sys-color-on-surface: var(--text-primary-color);
|
--md-sys-color-on-surface: var(--text-primary-color);
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
import type { ActionDetail } from "@material/mwc-list";
|
import type { ActionDetail } from "@material/mwc-list";
|
||||||
|
|
||||||
import { mdiBackupRestore, mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
|
import { mdiBackupRestore, mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
|
||||||
@@ -16,7 +17,6 @@ import type {
|
|||||||
} from "../../../src/components/data-table/ha-data-table";
|
} from "../../../src/components/data-table/ha-data-table";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-fab";
|
import "../../../src/components/ha-fab";
|
||||||
import "../../../src/components/ha-button";
|
|
||||||
import "../../../src/components/ha-icon-button";
|
import "../../../src/components/ha-icon-button";
|
||||||
import "../../../src/components/ha-list-item";
|
import "../../../src/components/ha-list-item";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
@@ -241,13 +241,12 @@ export class HassioBackups extends LitElement {
|
|||||||
<div class="header-btns">
|
<div class="header-btns">
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`
|
? html`
|
||||||
<ha-button
|
<mwc-button
|
||||||
appearance="plain"
|
|
||||||
variant="danger"
|
|
||||||
@click=${this._deleteSelected}
|
@click=${this._deleteSelected}
|
||||||
|
class="warning"
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("backup.delete_selected")}
|
${this.supervisor.localize("backup.delete_selected")}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@@ -409,7 +408,7 @@ export class HassioBackups extends LitElement {
|
|||||||
margin-inline-end: -12px;
|
margin-inline-end: -12px;
|
||||||
margin-inline-start: initial;
|
margin-inline-start: initial;
|
||||||
}
|
}
|
||||||
.header-btns > ha-button,
|
.header-btns > mwc-button,
|
||||||
.header-btns > ha-icon-button {
|
.header-btns > ha-icon-button {
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
}
|
}
|
||||||
|
@@ -101,7 +101,7 @@ class HassioCardContent extends LitElement {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 2.4em;
|
height: 2.4em;
|
||||||
line-height: var(--ha-line-height-condensed);
|
line-height: 1.2em;
|
||||||
}
|
}
|
||||||
.icon_image img {
|
.icon_image img {
|
||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
@@ -121,7 +121,7 @@ class HassioCardContent extends LitElement {
|
|||||||
height: 12px;
|
height: 12px;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
border-radius: var(--ha-border-radius-circle);
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
.topbar {
|
.topbar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@@ -132,9 +132,9 @@ class HassioDashboard extends LitElement {
|
|||||||
}
|
}
|
||||||
ha-fab.non-tabs {
|
ha-fab.non-tabs {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: calc(16px + var(--safe-area-inset-right));
|
right: calc(16px + env(safe-area-inset-right));
|
||||||
bottom: calc(16px + var(--safe-area-inset-bottom));
|
bottom: calc(16px + env(safe-area-inset-bottom));
|
||||||
inset-inline-end: calc(16px + var(--safe-area-inset-right));
|
inset-inline-end: calc(16px + env(safe-area-inset-right));
|
||||||
inset-inline-start: initial;
|
inset-inline-start: initial;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-button";
|
|
||||||
import "../../../src/components/ha-settings-row";
|
import "../../../src/components/ha-settings-row";
|
||||||
import "../../../src/components/ha-svg-icon";
|
import "../../../src/components/ha-svg-icon";
|
||||||
import type { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
import type { HassioHassOSInfo } from "../../../src/data/hassio/host";
|
||||||
@@ -108,9 +109,10 @@ export class HassioUpdate extends LitElement {
|
|||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-button appearance="plain" href="/hassio/update-available/${key}">
|
<a href="/hassio/update-available/${key}">
|
||||||
${this.supervisor.localize("common.show")}
|
<mwc-button .label=${this.supervisor.localize("common.show")}>
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../src/components/ha-form/types";
|
import type { SchemaUnion } from "../../../../src/components/ha-form/types";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
@@ -77,21 +77,20 @@ class HassioBackupLocationDialog extends LitElement {
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
></ha-form>
|
></ha-form>
|
||||||
<ha-button
|
<mwc-button
|
||||||
appearance="plain"
|
|
||||||
slot="secondaryAction"
|
slot="secondaryAction"
|
||||||
@click=${this.closeDialog}
|
@click=${this.closeDialog}
|
||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
>
|
>
|
||||||
${this._dialogParams.supervisor.localize("common.cancel")}
|
${this._dialogParams.supervisor.localize("common.cancel")}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
<ha-button
|
<mwc-button
|
||||||
.disabled=${this._waiting || !this._data}
|
.disabled=${this._waiting || !this._data}
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@click=${this._changeMount}
|
@click=${this._changeMount}
|
||||||
>
|
>
|
||||||
${this._dialogParams.supervisor.localize("common.save")}
|
${this._dialogParams.supervisor.localize("common.save")}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import { atLeastVersion } from "../../../../src/common/config/version";
|
|||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
|
||||||
import { slugify } from "../../../../src/common/string/slugify";
|
import { slugify } from "../../../../src/common/string/slugify";
|
||||||
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-button";
|
import "../../../../src/components/ha-button";
|
||||||
import "../../../../src/components/ha-button-menu";
|
import "../../../../src/components/ha-button-menu";
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import "../../../../src/components/ha-spinner";
|
import "../../../../src/components/ha-spinner";
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import {
|
import {
|
||||||
@@ -68,20 +69,16 @@ class HassioCreateBackupDialog extends LitElement {
|
|||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-button
|
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||||
appearance="plain"
|
|
||||||
slot="secondaryAction"
|
|
||||||
@click=${this.closeDialog}
|
|
||||||
>
|
|
||||||
${this._dialogParams.supervisor.localize("common.close")}
|
${this._dialogParams.supervisor.localize("common.close")}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
<ha-button
|
<mwc-button
|
||||||
.disabled=${this._creatingBackup}
|
.disabled=${this._creatingBackup}
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@click=${this._createBackup}
|
@click=${this._createBackup}
|
||||||
>
|
>
|
||||||
${this._dialogParams.supervisor.localize("backup.create")}
|
${this._dialogParams.supervisor.localize("backup.create")}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import "../../../../src/components/ha-list-item";
|
import "../../../../src/components/ha-list-item";
|
||||||
import "../../../../src/components/ha-select";
|
import "../../../../src/components/ha-select";
|
||||||
import "../../../../src/components/ha-spinner";
|
import "../../../../src/components/ha-spinner";
|
||||||
@@ -21,8 +20,8 @@ import type { HomeAssistant } from "../../../../src/types";
|
|||||||
import type { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
|
import type { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
|
||||||
|
|
||||||
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
|
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
|
||||||
// Assume a speed of 30 MB/s.
|
const speed = supervisor.host.disk_life_time !== "" ? 30 : 10;
|
||||||
const moveTime = (supervisor.host.disk_used * 1000) / 60 / 30;
|
const moveTime = (supervisor.host.disk_used * 1000) / 60 / speed;
|
||||||
const rebootTime = (supervisor.host.startup_time * 4) / 60;
|
const rebootTime = (supervisor.host.startup_time * 4) / 60;
|
||||||
return Math.ceil((moveTime + rebootTime) / 10) * 10;
|
return Math.ceil((moveTime + rebootTime) / 10) * 10;
|
||||||
});
|
});
|
||||||
@@ -110,18 +109,17 @@ class HassioDatadiskDialog extends LitElement {
|
|||||||
"dialog.datadisk_move.no_devices"
|
"dialog.datadisk_move.no_devices"
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ha-button
|
<mwc-button
|
||||||
appearance="plain"
|
slot="secondaryAction"
|
||||||
slot="primaryAction"
|
|
||||||
@click=${this.closeDialog}
|
@click=${this.closeDialog}
|
||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
>
|
>
|
||||||
${this.dialogParams.supervisor.localize(
|
${this.dialogParams.supervisor.localize(
|
||||||
"dialog.datadisk_move.cancel"
|
"dialog.datadisk_move.cancel"
|
||||||
)}
|
)}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
|
|
||||||
<ha-button
|
<mwc-button
|
||||||
.disabled=${!this.selectedDevice}
|
.disabled=${!this.selectedDevice}
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
@click=${this._moveDatadisk}
|
@click=${this._moveDatadisk}
|
||||||
@@ -129,7 +127,7 @@ class HassioDatadiskDialog extends LitElement {
|
|||||||
${this.dialogParams.supervisor.localize(
|
${this.dialogParams.supervisor.localize(
|
||||||
"dialog.datadisk_move.move"
|
"dialog.datadisk_move.move"
|
||||||
)}
|
)}
|
||||||
</ha-button>`}
|
</mwc-button>`}
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@@ -164,7 +164,7 @@ class HassioHardwareDialog extends LitElement {
|
|||||||
pre,
|
pre,
|
||||||
code {
|
code {
|
||||||
background-color: var(--markdown-code-background-color, none);
|
background-color: var(--markdown-code-background-color, none);
|
||||||
border-radius: var(--ha-border-radius-sm);
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import "@material/mwc-button/mwc-button";
|
||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
@@ -5,7 +6,6 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { cache } from "lit/directives/cache";
|
import { cache } from "lit/directives/cache";
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import "../../../../src/components/ha-dialog";
|
import "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-expansion-panel";
|
import "../../../../src/components/ha-expansion-panel";
|
||||||
import "../../../../src/components/ha-formfield";
|
import "../../../../src/components/ha-formfield";
|
||||||
@@ -15,8 +15,7 @@ import "../../../../src/components/ha-list";
|
|||||||
import "../../../../src/components/ha-list-item";
|
import "../../../../src/components/ha-list-item";
|
||||||
import "../../../../src/components/ha-password-field";
|
import "../../../../src/components/ha-password-field";
|
||||||
import "../../../../src/components/ha-radio";
|
import "../../../../src/components/ha-radio";
|
||||||
import "../../../../src/components/ha-tab-group";
|
import "../../../../src/components/ha-spinner";
|
||||||
import "../../../../src/components/ha-tab-group-tab";
|
|
||||||
import "../../../../src/components/ha-textfield";
|
import "../../../../src/components/ha-textfield";
|
||||||
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
@@ -38,6 +37,7 @@ import type { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
|||||||
import { haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import type { HassioNetworkDialogParams } from "./show-dialog-network";
|
import type { HassioNetworkDialogParams } from "./show-dialog-network";
|
||||||
|
import "../../../../src/components/sl-tab-group";
|
||||||
|
|
||||||
const IP_VERSIONS = ["ipv4", "ipv6"];
|
const IP_VERSIONS = ["ipv4", "ipv6"];
|
||||||
|
|
||||||
@@ -115,19 +115,19 @@ export class DialogHassioNetwork
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
${this._interfaces.length > 1
|
${this._interfaces.length > 1
|
||||||
? html`<ha-tab-group @wa-tab-show=${this._handleTabActivated}
|
? html`<sl-tab-group @sl-tab-show=${this._handleTabActivated}
|
||||||
>${this._interfaces.map(
|
>${this._interfaces.map(
|
||||||
(device, index) =>
|
(device, index) =>
|
||||||
html`<ha-tab-group-tab
|
html`<sl-tab
|
||||||
slot="nav"
|
slot="nav"
|
||||||
.id=${device.interface}
|
.id=${device.interface}
|
||||||
.panel=${index.toString()}
|
.panel=${index.toString()}
|
||||||
.active=${this._curTabIndex === index}
|
.active=${this._curTabIndex === index}
|
||||||
>
|
>
|
||||||
${device.interface}
|
${device.interface}
|
||||||
</ha-tab-group-tab>`
|
</sl-tab>`
|
||||||
)}
|
)}
|
||||||
</ha-tab-group>`
|
</sl-tab-group>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
${cache(this._renderTab())}
|
${cache(this._renderTab())}
|
||||||
@@ -154,16 +154,16 @@ export class DialogHassioNetwork
|
|||||||
)}
|
)}
|
||||||
</p>`
|
</p>`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-button
|
<mwc-button
|
||||||
appearance="plain"
|
|
||||||
size="small"
|
|
||||||
class="scan"
|
class="scan"
|
||||||
@click=${this._scanForAP}
|
@click=${this._scanForAP}
|
||||||
.disabled=${this._scanning}
|
.disabled=${this._scanning}
|
||||||
.loading=${this._scanning}
|
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("dialog.network.scan_ap")}
|
${this._scanning
|
||||||
</ha-button>
|
? html`<ha-spinner aria-label="Scanning" size="small">
|
||||||
|
</ha-spinner>`
|
||||||
|
: this.supervisor.localize("dialog.network.scan_ap")}
|
||||||
|
</mwc-button>
|
||||||
${this._accessPoints &&
|
${this._accessPoints &&
|
||||||
this._accessPoints.accesspoints &&
|
this._accessPoints.accesspoints &&
|
||||||
this._accessPoints.accesspoints.length !== 0
|
this._accessPoints.accesspoints.length !== 0
|
||||||
@@ -270,16 +270,16 @@ export class DialogHassioNetwork
|
|||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<ha-button @click=${this.closeDialog} appearance="plain">
|
<mwc-button
|
||||||
${this.supervisor.localize("common.cancel")}
|
.label=${this.supervisor.localize("common.cancel")}
|
||||||
</ha-button>
|
@click=${this.closeDialog}
|
||||||
<ha-button
|
|
||||||
@click=${this._updateNetwork}
|
|
||||||
.disabled=${!this._dirty}
|
|
||||||
.loading=${this._processing}
|
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("common.save")}
|
</mwc-button>
|
||||||
</ha-button>
|
<mwc-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
|
||||||
|
${this._processing
|
||||||
|
? html`<ha-spinner size="small"> </ha-spinner>`
|
||||||
|
: this.supervisor.localize("common.save")}
|
||||||
|
</mwc-button>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,7 +584,11 @@ export class DialogHassioNetwork
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-button.scan {
|
mwc-button.warning {
|
||||||
|
--mdc-theme-primary: var(--error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-button.scan {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-inline-start: 8px;
|
margin-inline-start: 8px;
|
||||||
margin-inline-end: initial;
|
margin-inline-end: initial;
|
||||||
@@ -605,8 +609,8 @@ export class DialogHassioNetwork
|
|||||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 16px;
|
padding: 8px;
|
||||||
padding-bottom: max(var(--safe-area-inset-bottom), 16px);
|
padding-bottom: max(env(safe-area-inset-bottom), 8px);
|
||||||
background-color: var(--mdc-theme-surface, #fff);
|
background-color: var(--mdc-theme-surface, #fff);
|
||||||
}
|
}
|
||||||
.warning {
|
.warning {
|
||||||
@@ -628,10 +632,10 @@ export class DialogHassioNetwork
|
|||||||
--mdc-list-side-padding: 10px;
|
--mdc-list-side-padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-tab-group-tab {
|
sl-tab {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
ha-tab-group-tab::part(base) {
|
sl-tab::part(base) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,13 @@
|
|||||||
import { mdiDelete, mdiPlus } from "@mdi/js";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import { mdiDelete } from "@mdi/js";
|
||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-button";
|
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
import type { SchemaUnion } from "../../../../src/components/ha-form/types";
|
import type { SchemaUnion } from "../../../../src/components/ha-form/types";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import "../../../../src/components/ha-settings-row";
|
import "../../../../src/components/ha-settings-row";
|
||||||
import "../../../../src/components/ha-svg-icon";
|
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
addHassioDockerRegistry,
|
addHassioDockerRegistry,
|
||||||
@@ -85,19 +84,16 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
></ha-form>
|
></ha-form>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<ha-button
|
<mwc-button
|
||||||
?disabled=${Boolean(
|
?disabled=${Boolean(
|
||||||
!this._input.registry ||
|
!this._input.registry ||
|
||||||
!this._input.username ||
|
!this._input.username ||
|
||||||
!this._input.password
|
!this._input.password
|
||||||
)}
|
)}
|
||||||
@click=${this._addNewRegistry}
|
@click=${this._addNewRegistry}
|
||||||
appearance="filled"
|
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
|
||||||
${this.supervisor.localize("dialog.registries.add_registry")}
|
${this.supervisor.localize("dialog.registries.add_registry")}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html`${this._registries?.length
|
: html`${this._registries?.length
|
||||||
@@ -130,17 +126,11 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
</ha-alert>
|
</ha-alert>
|
||||||
`}
|
`}
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<ha-button
|
<mwc-button @click=${this._addRegistry} dialogInitialFocus>
|
||||||
@click=${this._addRegistry}
|
|
||||||
dialogInitialFocus
|
|
||||||
appearance="filled"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"dialog.registries.add_new_registry"
|
"dialog.registries.add_new_registry"
|
||||||
)}
|
)}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</div> `}
|
</div> `}
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
@@ -228,7 +218,7 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
css`
|
css`
|
||||||
.registry {
|
.registry {
|
||||||
border: 1px solid var(--divider-color);
|
border: 1px solid var(--divider-color);
|
||||||
border-radius: var(--ha-border-radius-sm);
|
border-radius: 4px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
.action {
|
.action {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { mdiDelete, mdiDeleteOff, mdiPlus } from "@mdi/js";
|
import "@material/mwc-button/mwc-button";
|
||||||
|
import { mdiDelete, mdiDeleteOff } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
@@ -6,15 +7,10 @@ import memoizeOne from "memoize-one";
|
|||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import { caseInsensitiveStringCompare } from "../../../../src/common/string/compare";
|
import { caseInsensitiveStringCompare } from "../../../../src/common/string/compare";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-button";
|
import "../../../../src/components/ha-tooltip";
|
||||||
|
import "../../../../src/components/ha-spinner";
|
||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import "../../../../src/components/ha-md-list";
|
|
||||||
import "../../../../src/components/ha-md-list-item";
|
|
||||||
import "../../../../src/components/ha-svg-icon";
|
|
||||||
import "../../../../src/components/ha-textfield";
|
|
||||||
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
|
||||||
import "../../../../src/components/ha-tooltip";
|
|
||||||
import type {
|
import type {
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
@@ -28,6 +24,10 @@ import {
|
|||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import type { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
import type { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
||||||
|
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
||||||
|
import "../../../../src/components/ha-textfield";
|
||||||
|
import "../../../../src/components/ha-md-list";
|
||||||
|
import "../../../../src/components/ha-md-list-item";
|
||||||
|
|
||||||
@customElement("dialog-hassio-repositories")
|
@customElement("dialog-hassio-repositories")
|
||||||
class HassioRepositoriesDialog extends LitElement {
|
class HassioRepositoriesDialog extends LitElement {
|
||||||
@@ -119,17 +119,15 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
<div>${repo.url}</div>
|
<div>${repo.url}</div>
|
||||||
</div>
|
</div>
|
||||||
<ha-tooltip
|
<ha-tooltip
|
||||||
.for="icon-button-${repo.slug}"
|
|
||||||
class="delete"
|
class="delete"
|
||||||
slot="end"
|
slot="end"
|
||||||
>
|
.content=${this._dialogParams!.supervisor.localize(
|
||||||
${this._dialogParams!.supervisor.localize(
|
|
||||||
usedRepositories.includes(repo.slug)
|
usedRepositories.includes(repo.slug)
|
||||||
? "dialog.repositories.used"
|
? "dialog.repositories.used"
|
||||||
: "dialog.repositories.remove"
|
: "dialog.repositories.remove"
|
||||||
)}
|
)}
|
||||||
</ha-tooltip>
|
>
|
||||||
<div .id="icon-button-${repo.slug}">
|
<div>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.disabled=${usedRepositories.includes(repo.slug)}
|
.disabled=${usedRepositories.includes(repo.slug)}
|
||||||
.slug=${repo.slug}
|
.slug=${repo.slug}
|
||||||
@@ -140,6 +138,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
>
|
>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
</div>
|
</div>
|
||||||
|
</ha-tooltip>
|
||||||
</ha-md-list-item>
|
</ha-md-list-item>
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
@@ -160,22 +159,18 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
@keydown=${this._handleKeyAdd}
|
@keydown=${this._handleKeyAdd}
|
||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
></ha-textfield>
|
></ha-textfield>
|
||||||
<ha-button
|
<mwc-button @click=${this._addRepository}>
|
||||||
.loading=${this._processing}
|
${this._processing
|
||||||
@click=${this._addRepository}
|
? html`<ha-spinner size="small"></ha-spinner>`
|
||||||
appearance="filled"
|
: this._dialogParams!.supervisor.localize(
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
|
||||||
${this._dialogParams!.supervisor.localize(
|
|
||||||
"dialog.repositories.add"
|
"dialog.repositories.add"
|
||||||
)}
|
)}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ha-button slot="primaryAction" @click=${this.closeDialog}>
|
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
|
||||||
${this._dialogParams?.supervisor.localize("common.close")}
|
${this._dialogParams?.supervisor.localize("common.close")}
|
||||||
</ha-button>
|
</mwc-button>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -193,14 +188,19 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
.option {
|
.option {
|
||||||
border: 1px solid var(--divider-color);
|
border: 1px solid var(--divider-color);
|
||||||
border-radius: var(--ha-border-radius-sm);
|
border-radius: 4px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
ha-button {
|
mwc-button {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-inline-start: 8px;
|
margin-inline-start: 8px;
|
||||||
margin-inline-end: initial;
|
margin-inline-end: initial;
|
||||||
}
|
}
|
||||||
|
ha-spinner {
|
||||||
|
display: block;
|
||||||
|
margin: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
div.delete ha-icon-button {
|
div.delete ha-icon-button {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
@@ -249,8 +249,6 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
await addStoreRepository(this.hass, input.value);
|
await addStoreRepository(this.hass, input.value);
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
|
|
||||||
fireEvent(this, "supervisor-collection-refresh", { collection: "store" });
|
|
||||||
|
|
||||||
input.value = "";
|
input.value = "";
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
@@ -263,8 +261,6 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
try {
|
try {
|
||||||
await removeStoreRepository(this.hass, slug);
|
await removeStoreRepository(this.hass, slug);
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
|
|
||||||
fireEvent(this, "supervisor-collection-refresh", { collection: "store" });
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
}
|
}
|
||||||
|
@@ -159,7 +159,7 @@ class HassioSystemManagedDialog extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--ha-space-4);
|
gap: 16px;
|
||||||
--mdc-icon-size: 48px;
|
--mdc-icon-size: 48px;
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ import type { PropertyValues, TemplateResult } from "lit";
|
|||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import { goBack, navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
import { nextRender } from "../../../src/common/util/render-status";
|
import { nextRender } from "../../../src/common/util/render-status";
|
||||||
import "../../../src/components/ha-icon-button";
|
import "../../../src/components/ha-icon-button";
|
||||||
@@ -193,7 +193,7 @@ class HassioIngressView extends LitElement {
|
|||||||
title: addon.name,
|
title: addon.name,
|
||||||
});
|
});
|
||||||
await nextRender();
|
await nextRender();
|
||||||
goBack();
|
history.back();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +275,7 @@ class HassioIngressView extends LitElement {
|
|||||||
title: addon.name,
|
title: addon.name,
|
||||||
});
|
});
|
||||||
await nextRender();
|
await nextRender();
|
||||||
goBack();
|
history.back();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ class HassioIngressView extends LitElement {
|
|||||||
|
|
||||||
.main-title {
|
.main-title {
|
||||||
margin: var(--margin-title);
|
margin: var(--margin-title);
|
||||||
line-height: var(--ha-line-height-condensed);
|
line-height: 20px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ export const hassioStyle = css`
|
|||||||
.card-group {
|
.card-group {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
grid-gap: var(--ha-space-2);
|
grid-gap: 8px;
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 640px) {
|
@media screen and (min-width: 640px) {
|
||||||
.card-group {
|
.card-group {
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
|
||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-button";
|
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-settings-row";
|
import "../../../src/components/ha-settings-row";
|
||||||
@@ -69,12 +70,12 @@ class HassioCoreInfo extends LitElement {
|
|||||||
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
||||||
this.supervisor.core.update_available
|
this.supervisor.core.update_available
|
||||||
? html`
|
? html`
|
||||||
<ha-button
|
<a href="/hassio/update-available/core">
|
||||||
appearance="plain"
|
<mwc-button
|
||||||
href="/hassio/update-available/core"
|
.label=${this.supervisor.localize("common.show")}
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("common.show")}
|
</mwc-button>
|
||||||
</ha-button>
|
</a>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
@@ -94,7 +95,7 @@ class HassioCoreInfo extends LitElement {
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
slot="primaryAction"
|
slot="primaryAction"
|
||||||
variant="danger"
|
class="warning"
|
||||||
@click=${this._coreRestart}
|
@click=${this._coreRestart}
|
||||||
.title=${this.supervisor.localize("common.restart_name", {
|
.title=${this.supervisor.localize("common.restart_name", {
|
||||||
name: "Core",
|
name: "Core",
|
||||||
@@ -187,6 +188,11 @@ class HassioCoreInfo extends LitElement {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
--mdc-theme-primary: var(--error-color);
|
||||||
|
}
|
||||||
|
|
||||||
ha-button-menu {
|
ha-button-menu {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
--mdc-menu-min-width: 200px;
|
--mdc-menu-min-width: 200px;
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
|
||||||
import { mdiDotsVertical } from "@mdi/js";
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
@@ -6,11 +8,10 @@ import memoizeOne from "memoize-one";
|
|||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-button";
|
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-icon-button";
|
|
||||||
import "../../../src/components/ha-list-item";
|
import "../../../src/components/ha-list-item";
|
||||||
|
import "../../../src/components/ha-icon-button";
|
||||||
import "../../../src/components/ha-settings-row";
|
import "../../../src/components/ha-settings-row";
|
||||||
import {
|
import {
|
||||||
extractApiErrorMessage,
|
extractApiErrorMessage,
|
||||||
@@ -76,28 +77,24 @@ class HassioHostInfo extends LitElement {
|
|||||||
<span slot="description">
|
<span slot="description">
|
||||||
${this.supervisor.host.hostname}
|
${this.supervisor.host.hostname}
|
||||||
</span>
|
</span>
|
||||||
<ha-button
|
<mwc-button
|
||||||
|
.label=${this.supervisor.localize("system.host.change")}
|
||||||
@click=${this._changeHostnameClicked}
|
@click=${this._changeHostnameClicked}
|
||||||
appearance="plain"
|
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("system.host.change")}
|
</mwc-button>
|
||||||
</ha-button>
|
|
||||||
</ha-settings-row>`
|
</ha-settings-row>`
|
||||||
: ""}
|
: ""}
|
||||||
${this.supervisor.host.features.includes("network")
|
${this.supervisor.host.features.includes("network")
|
||||||
? html`<ha-settings-row>
|
? html` <ha-settings-row>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
${this.supervisor.localize("system.host.ip_address")}
|
${this.supervisor.localize("system.host.ip_address")}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description"> ${primaryIpAddress} </span>
|
<span slot="description"> ${primaryIpAddress} </span>
|
||||||
<ha-button
|
<mwc-button
|
||||||
|
.label=${this.supervisor.localize("system.host.change")}
|
||||||
@click=${this._changeNetworkClicked}
|
@click=${this._changeNetworkClicked}
|
||||||
appearance="plain"
|
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("system.host.change")}
|
</mwc-button>
|
||||||
</ha-button>
|
|
||||||
</ha-settings-row>`
|
</ha-settings-row>`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
@@ -111,13 +108,12 @@ class HassioHostInfo extends LitElement {
|
|||||||
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
||||||
this.supervisor.os.update_available
|
this.supervisor.os.update_available
|
||||||
? html`
|
? html`
|
||||||
<ha-button
|
<a href="/hassio/update-available/os">
|
||||||
appearance="plain"
|
<mwc-button
|
||||||
size="small"
|
.label=${this.supervisor.localize("common.show")}
|
||||||
href="/hassio/update-available/os"
|
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("common.show")}
|
</mwc-button>
|
||||||
</ha-button>
|
</a>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
@@ -143,12 +139,16 @@ class HassioHostInfo extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
${this.supervisor.host.disk_life_time !== null
|
${this.supervisor.host.disk_life_time !== "" &&
|
||||||
|
this.supervisor.host.disk_life_time >= 10
|
||||||
? html` <ha-settings-row>
|
? html` <ha-settings-row>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
${this.supervisor.localize("system.host.lifetime_used")}
|
${this.supervisor.localize(
|
||||||
|
"system.host.emmc_lifetime_used"
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="description">
|
<span slot="description">
|
||||||
|
${this.supervisor.host.disk_life_time - 10} % -
|
||||||
${this.supervisor.host.disk_life_time} %
|
${this.supervisor.host.disk_life_time} %
|
||||||
</span>
|
</span>
|
||||||
</ha-settings-row>`
|
</ha-settings-row>`
|
||||||
@@ -167,7 +167,7 @@ class HassioHostInfo extends LitElement {
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
${this.supervisor.host.features.includes("reboot")
|
${this.supervisor.host.features.includes("reboot")
|
||||||
? html`
|
? html`
|
||||||
<ha-progress-button variant="danger" @click=${this._hostReboot}>
|
<ha-progress-button class="warning" @click=${this._hostReboot}>
|
||||||
${this.supervisor.localize("system.host.reboot_host")}
|
${this.supervisor.localize("system.host.reboot_host")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
`
|
`
|
||||||
@@ -175,7 +175,7 @@ class HassioHostInfo extends LitElement {
|
|||||||
${this.supervisor.host.features.includes("shutdown")
|
${this.supervisor.host.features.includes("shutdown")
|
||||||
? html`
|
? html`
|
||||||
<ha-progress-button
|
<ha-progress-button
|
||||||
variant="danger"
|
class="warning"
|
||||||
@click=${this._hostShutdown}
|
@click=${this._hostShutdown}
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("system.host.shutdown_host")}
|
${this.supervisor.localize("system.host.shutdown_host")}
|
||||||
@@ -431,6 +431,10 @@ class HassioHostInfo extends LitElement {
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
--mdc-theme-primary: var(--error-color);
|
||||||
|
}
|
||||||
|
|
||||||
ha-button-menu {
|
ha-button-menu {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
--mdc-menu-min-width: 200px;
|
--mdc-menu-min-width: 200px;
|
||||||
|
@@ -5,7 +5,6 @@ import { atLeastVersion } from "../../../src/common/config/version";
|
|||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import "../../../src/components/buttons/ha-progress-button";
|
import "../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../src/components/ha-alert";
|
import "../../../src/components/ha-alert";
|
||||||
import "../../../src/components/ha-button";
|
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import "../../../src/components/ha-settings-row";
|
import "../../../src/components/ha-settings-row";
|
||||||
import "../../../src/components/ha-switch";
|
import "../../../src/components/ha-switch";
|
||||||
@@ -81,13 +80,12 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
${!atLeastVersion(this.hass.config.version, 2021, 12) &&
|
||||||
this.supervisor.supervisor.update_available
|
this.supervisor.supervisor.update_available
|
||||||
? html`
|
? html`
|
||||||
<ha-button
|
<a href="/hassio/update-available/supervisor">
|
||||||
appearance="plain"
|
<mwc-button
|
||||||
size="small"
|
.label=${this.supervisor.localize("common.show")}
|
||||||
href="/hassio/update-available/supervisor"
|
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("common.show")}
|
</mwc-button>
|
||||||
</ha-button>
|
</a>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
@@ -158,28 +156,24 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"system.supervisor.unsupported_title"
|
"system.supervisor.unsupported_title"
|
||||||
)}
|
)}
|
||||||
<ha-button
|
<mwc-button
|
||||||
slot="action"
|
slot="action"
|
||||||
|
.label=${this.supervisor.localize("common.learn_more")}
|
||||||
@click=${this._unsupportedDialog}
|
@click=${this._unsupportedDialog}
|
||||||
variant="warning"
|
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("common.learn_more")}
|
</mwc-button>
|
||||||
</ha-button>
|
|
||||||
</ha-alert>`}
|
</ha-alert>`}
|
||||||
${!this.supervisor.supervisor.healthy
|
${!this.supervisor.supervisor.healthy
|
||||||
? html`<ha-alert alert-type="error">
|
? html`<ha-alert alert-type="error">
|
||||||
${this.supervisor.localize(
|
${this.supervisor.localize(
|
||||||
"system.supervisor.unhealthy_title"
|
"system.supervisor.unhealthy_title"
|
||||||
)}
|
)}
|
||||||
<ha-button
|
<mwc-button
|
||||||
variant="danger"
|
|
||||||
size="small"
|
|
||||||
slot="action"
|
slot="action"
|
||||||
|
.label=${this.supervisor.localize("common.learn_more")}
|
||||||
@click=${this._unhealthyDialog}
|
@click=${this._unhealthyDialog}
|
||||||
>
|
>
|
||||||
${this.supervisor.localize("common.learn_more")}
|
</mwc-button>
|
||||||
</ha-button>
|
|
||||||
</ha-alert>`
|
</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
@@ -454,6 +448,9 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
ha-alert mwc-button {
|
||||||
|
--mdc-theme-primary: var(--primary-text-color);
|
||||||
|
}
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
|
||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
@@ -208,16 +208,14 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
${changelog
|
${changelog
|
||||||
? html`
|
? html`
|
||||||
|
<a href=${changelog} target="_blank" rel="noreferrer">
|
||||||
<ha-button
|
<ha-button
|
||||||
href=${changelog}
|
.label=${this.supervisor.localize(
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
appearance="plain"
|
|
||||||
>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"update_available.open_release_notes"
|
"update_available.open_release_notes"
|
||||||
)}
|
)}
|
||||||
|
>
|
||||||
</ha-button>
|
</ha-button>
|
||||||
|
</a>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<span></span>
|
<span></span>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user