Skip to content
36 changes: 36 additions & 0 deletions src/ak-hint/ak-hint.builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { ElementRest } from "../types.js";
import { Hint, type HintVariant } from "./ak-hint.component.js";

import { spread } from "@open-wc/lit-helpers";

import { html, nothing, TemplateResult } from "lit";
import { ifDefined } from "lit/directives/if-defined.js";

export type HintProps = ElementRest & {
variant?: HintVariant;
// Must be named "hint"; although it's called "title" everywhere else, "title" as a field name
// conflicts with HTMLElement.title by type.
hint?: string | TemplateResult;
footer?: string | TemplateResult;
body?: string | TemplateResult;
};

/**
* @summary Helper function to create a Hint component programmatically
*
* @returns {TemplateResult} A Lit template result containing the configured ak-hint element
*
* @see {@link Hint} - The underlying web component
*/

export function akHint(options: HintProps = {}) {
const { hint, body, footer, variant, ...rest } = options;

return html`
<ak-hint ${spread(rest)} variant=${ifDefined(variant)}>
${hint ? html`<h3 slot="title">${hint}</h3>` : nothing}
${body ? html`<span>${body}</span>` : nothing}
${footer ? html`<footer slot="footer">${footer}</footer>` : nothing}
</ak-hint>
`;
}
80 changes: 80 additions & 0 deletions src/ak-hint/ak-hint.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { AkLitElement } from "../component-base.js";
import styles from "./ak-hint.css";

import { html, nothing } from "lit";

// Used only by the builder, as these have only visual effects
export const hintVariant = ["info", "success", "warning", "danger"] as const;
export type HintVariant = (typeof hintVariant)[number];

/**
* @element ak-hint
*
* @summary A container for displaying informative content with optional title and footer sections
*
* @attr {string} variant - Visual theme variant: "info" (default), "success", "warning", "danger"
*
* @slot - Main content body of the hint
* @slot title - Optional title header for the hint
* @slot footer - Optional footer content with additional information or actions
*
* @csspart hint - The hint component as a whole
* @csspart title - The title section container
* @csspart body - The main content body container
* @csspart footer - The footer section container
*
* @cssprop --pf-v5-c-hint--GridRowGap - Gap between grid rows
* @cssprop --pf-v5-c-hint--PaddingTop - Top padding of the hint container
* @cssprop --pf-v5-c-hint--PaddingRight - Right padding of the hint container
* @cssprop --pf-v5-c-hint--PaddingBottom - Bottom padding of the hint container
* @cssprop --pf-v5-c-hint--PaddingLeft - Left padding of the hint container
* @cssprop --pf-v5-c-hint--BackgroundColor - Background color of the hint
* @cssprop --pf-v5-c-hint--BorderColor - Border color of the hint
* @cssprop --pf-v5-c-hint--BorderWidth - Border width of the hint
* @cssprop --pf-v5-c-hint--BoxShadow - Box shadow of the hint container
* @cssprop --pf-v5-c-hint--Color - Text color of the hint content
* @cssprop --pf-v5-c-hint__title--FontSize - Font size of the title text
* @cssprop --pf-v5-c-hint__body--FontSize - Font size of the body text
* @cssprop --pf-v5-c-hint__footer--child--MarginRight - Right margin of footer child elements
* @cssprop --pf-v5-c-hint__actions--MarginLeft - Left margin of action elements
* @cssprop --pf-v5-c-hint__actions--MarginLeft - Left margin of action elements
* @cssprop --pf-v5-c-hint__actions--MarginLeft - Left margin of action elements
* @cssprop --pf-v5-c-hint__actions--MarginLeft - Left margin of action elements
* @cssprop --pf-v5-c-hint__actions--MarginLeft - Left margin of action elements
* @cssprop --pf-v5-c-hint__actions--MarginLeft - Left margin of action elements
* @cssprop --pf-v5-c-hint__actions--MarginLeft - Left margin of action elements
* @cssprop --ak-v1-c-hint--m-success--BackgroundColor - Background color for "success" variant
* @cssprop --ak-v1-c-hint--m-success--BorderColor - Border color for "success" variant
* @cssprop --ak-v1-c-hint--m-success__title--Color - Title color for "success" variant
* @cssprop --ak-v1-c-hint--m-warning--BackgroundColor - Background color for "warning" variant
* @cssprop --ak-v1-c-hint--m-warning--BorderColor - Border color for "warning" variant
* @cssprop --ak-v1-c-hint--m-warning__title--Color - Title color for "warning" variant
* @cssprop --ak-v1-c-hint--m-danger--BackgroundColor - Background color for "danger" variant
* @cssprop --ak-v1-c-hint--m-danger--BorderColor - Border color for "danger" variant
* @cssprop --ak-v1-c-hint--m-danger__title--Color - Title color for "danger" variant
* @cssprop
*/
export class Hint extends AkLitElement {
static override readonly styles = [styles];

public override connectedCallback() {
super.connectedCallback();
if (!this.hasAttribute("role")) {
this.setAttribute("role", "note");
}
}

public override render() {
const [hasTitle, hasBody, hasFooter] = ["title", null, "footer"].map((item) =>
this.hasSlotted(item),
);

return html`
<div part="hint">
${hasTitle ? html`<div part="title"><slot name="title"></slot></div>` : nothing}
${hasBody ? html`<div part="body"><slot></slot></div>` : nothing}
${hasFooter ? html`<div part="footer"><slot name="footer"></slot></div>` : nothing}
</div>
`;
}
}
89 changes: 89 additions & 0 deletions src/ak-hint/ak-hint.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
:host {
--hint--GridRowGap: var(--pf-v5-c-hint--GridRowGap, 1rem);
--hint--PaddingTop: var(--pf-v5-c-hint--PaddingTop, 1.5rem);
--hint--PaddingRight: var(--pf-v5-c-hint--PaddingRight, 1.5rem);
--hint--PaddingBottom: var(--pf-v5-c-hint--PaddingBottom, 1.5rem);
--hint--PaddingLeft: var(--pf-v5-c-hint--PaddingLeft, 1.5rem);
--hint--BackgroundColor: var(--pf-v5-c-hint--BackgroundColor, #e7f1fa);
--hint--BorderColor: var(--pf-v5-c-hint--BorderColor, #bee1f4);
--hint--BorderWidth: var(--pf-v5-c-hint--BorderWidth, 1px);
--hint--BoxShadow: var(
--pf-v5-c-hint--BoxShadow,
0 0.0625rem 0.125rem 0 rgb(3 3 3 / 12%),
0 0 0.125rem 0 rgb(3 3 3 / 6%)
);
--hint--Color: var(--pf-v5-c-hint--Color, #151515);
--hint__title--Color: var(--pf-v5-c-hint--Color, #151515);
--hint__title--FontSize: var(--pf-v5-c-hint__title--FontSize, 1.125rem);
--hint__body--FontSize: var(--pf-v5-c-hint__body--FontSize, 1rem);
--hint__footer--child--MarginRight: var(--pf-v5-c-hint__footer--child--MarginRight, 1rem);
--hint__actions--MarginLeft: var(--pf-v5-c-hint__actions--MarginLeft, 3rem);
--hint__actions--c-dropdown--MarginTop: var(
--pf-v5-c-hint__actions--c-dropdown--MarginTop,
calc(0.375rem * -1)
);
--hint--m-success--BackgroundColor: var(--ak-v1-c-hint--m-success--BackgroundColor, #f3faf2);
--hint--m-success--BorderColor: var(--ak-v1-c-hint--m-success--BorderColor, #3e8635);
--hint--m-success__title--Color: var(--ak-v1-c-hint--m-success__title--Color, #3e8635);
--hint--m-warning--BackgroundColor: var(--ak-v1-c-hint--m-warning--BackgroundColor, #fdf7e7);
--hint--m-warning--BorderColor: var(--ak-v1-c-hint--m-warning--BorderColor, #f4c145);
--hint--m-warning__title--Color: var(--ak-v1-c-hint--m-warning__title--Color, #795600);
--hint--m-danger--BackgroundColor: var(--ak-v1-c-hint--m-danger--BackgroundColor, #faeae8);
--hint--m-danger--BorderColor: var(--ak-v1-c-hint--m-danger--BorderColor, #c9190b);
--hint--m-danger__title--Color: var(--ak-v1-c-hint--m-danger__title--Color, #470000);
}

:host(:not([hidden])) {
display: block;
}

[part="hint"] {
display: grid;
grid-template-columns: 1fr auto;
row-gap: var(--hint--GridRowGap);
padding-block-start: var(--hint--PaddingTop);
padding-block-end: var(--hint--PaddingBottom);
padding-inline-start: var(--hint--PaddingLeft);
padding-inline-end: var(--hint--PaddingRight);
color: var(--hint--Color);
background-color: var(--hint--BackgroundColor);
border: var(--hint--BorderWidth) solid var(--hint--BorderColor);
box-shadow: var(--hint--BoxShadow);
}

:host([variant="success"]) {
--hint--BackgroundColor: var(--hint--m-success--BackgroundColor);
--hint--BorderColor: var(--hint--m-success--BorderColor);
--hint__title--Color: var(--hint--m-success__title--Color);
}

:host([variant="warning"]) {
--hint--BackgroundColor: var(--hint--m-warning--BackgroundColor);
--hint--BorderColor: var(--hint--m-warning--BorderColor);
--hint__title--Color: var(--hint--m-warning__title--Color);
}

:host([variant="danger"]) {
--hint--BackgroundColor: var(--hint--m-danger--BackgroundColor);
--hint--BorderColor: var(--hint--m-danger--BorderColor);
--hint__title--Color: var(--hint--m-danger__title--Color);
}

[part="hint"] > [part="title"] ::slotted(*) {
font-size: var(--hint__title--FontSize);
color: var(--hint__title--Color);
}

[part="hint"] > [part="body"] ::slotted(*) {
font-size: var(--hint__body--FontSize);
}

[part="hint"] > [part="footer"] ::slotted(*) {
font-size: var(--hint__footer--FontSize);
}

[part="title"],
[part="body"],
[part="footer"] {
grid-column: 1/-1;
}
15 changes: 15 additions & 0 deletions src/ak-hint/ak-hint.overridden.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import: hint.css
base: .pf-v5-c-hint
host:
'[part="hint"]':
$from: $base
$include: display, grid-template-columns, grid-row-gap, color, background-color, border, box-shadow, padding-block-start, padding-block-end, padding-inline-start, padding-inline-end

'[part="title"] ::slotted(*)':
font-size: var(--pf-v5-c-hint__title--FontSize) !important;
'[part="body"] ::slotted(*)':
font-size: var(--pf-v5-c-hint__body--FontSize) !important;
'[name="footer"] ::slotted(*)':
font-size: var(--pf-v5-c-hint__footer--FontSize) !important;
'[part="title"], [part="body"], [part="footer"]':
$from: __footer
38 changes: 38 additions & 0 deletions src/ak-hint/ak-hint.root.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
:root {
--pf-v5-c-hint--GridRowGap: var(--pf-v5-global--spacer--md);
--pf-v5-c-hint--PaddingTop: var(--pf-v5-global--spacer--lg);
--pf-v5-c-hint--PaddingRight: var(--pf-v5-global--spacer--lg);
--pf-v5-c-hint--PaddingBottom: var(--pf-v5-global--spacer--lg);
--pf-v5-c-hint--PaddingLeft: var(--pf-v5-global--spacer--lg);
--pf-v5-c-hint--BackgroundColor: var(--pf-v5-global--palette--blue-50);
--pf-v5-c-hint--BorderColor: var(--pf-v5-global--palette--blue-100);
--pf-v5-c-hint--BorderWidth: var(--pf-v5-global--BorderWidth--sm);
--pf-v5-c-hint--BoxShadow: var(--pf-v5-global--BoxShadow--sm);
--pf-v5-c-hint--Color: var(--pf-v5-global--Color--100);
--pf-v5-c-hint__title--FontSize: var(--pf-v5-global--FontSize--lg);
--pf-v5-c-hint__body--FontSize: var(--pf-v5-global--FontSize--md);
--pf-v5-c-hint__footer--child--MarginRight: var(--pf-v5-global--spacer--md);

/* TODO: Incorporate the "actions" block. */
--pf-v5-c-hint__actions--MarginLeft: var(--pf-v5-global--spacer--2xl);
--pf-v5-c-hint__actions--c-dropdown--MarginTop: calc(
var(--pf-v5-global--spacer--form-element) * -1
);
--ak-v1-c-hint__title--Color: var(--pf-v5-global--Color--100);
--ak-v1-c-hint--m-success--BackgroundColor: var(--pf-v5-global--palette--green-50);
--ak-v1-c-hint--m-success--BorderColor: var(--pf-v5-global--success-color--100);
--ak-v1-c-hint--m-success__title--Color: var(--pf-v5-global--success-color--100);
--ak-v1-c-hint--m-warning--BackgroundColor: var(--pf-v5-global--palette--gold-50);
--ak-v1-c-hint--m-warning--BorderColor: var(--pf-v5-global--warning-color--100);
--ak-v1-c-hint--m-warning__title--Color: var(--pf-v5-global--warning-color--200);
--ak-v1-c-hint--m-danger--BackgroundColor: var(--pf-v5-global--palette--red-50);
--ak-v1-c-hint--m-danger--BorderColor: var(--pf-v5-global--danger-color--100);
--ak-v1-c-hint--m-danger__title--Color: var(--pf-v5-global--danger-color--300);
}

.pf-v5-theme-dark,
html[theme="dark"],
html[data-theme="dark"] {
--pf-v5-c-hint--BackgroundColor: var(--pf-v5-global--BackgroundColor--400);
--pf-v5-c-hint--BorderColor: var(--pf-v5-global--BorderColor--300);
}
Loading