Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions asset/css/callout.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Layout
.callout {
display: flex;
justify-content: center;
column-gap: 1em;

width: fit-content;
margin: 0 auto 1em;

&.callout-full-width {
width: 100%;
justify-content: start;
}

&.callout-form-element {
/*
* 14em is the width of the `control-label-group` element. This property value assumes that there are no
* special cases for which the label width differs. There is no way to get the width of the form label
* programmatically.
*/
margin-left: 14em;
Comment thread
jrauh01 marked this conversation as resolved.
width: auto;
justify-content: start;
}

i.icon::before {
margin-right: 0;
}

p {
margin: 0;
}

.callout-title {
margin-bottom: .5em;
}

.callout-text {
display: flex;
flex-direction: column;
}
}

// Style
.callout {
padding: .5em 1em;
border: 1px solid var(--callout-color);
background-color: color-mix(in srgb, var(--callout-color) 10%, transparent);
border-radius: .25em;

i.icon {
color: var(--callout-color);
font-size: 1.5em;
}

&.callout-type-info {
--callout-color: @color-pending;
}

&.callout-type-success {
--callout-color: @color-ok;
}

&.callout-type-warning {
--callout-color: @color-warning;
}

&.callout-type-error {
--callout-color: @color-critical;
}
}
31 changes: 31 additions & 0 deletions src/Common/CalloutType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace ipl\Web\Common;

use ipl\Web\Widget\Icon;

/**
* An enum containing all possible callout types for the {@see Callout} widget
*/
enum CalloutType: string
{
case Info = 'callout-type-info';
case Success = 'callout-type-success';
case Warning = 'callout-type-warning';
case Error = 'callout-type-error';

/**
* Get the icon element for use in the callout
*
* @return Icon
*/
public function getIcon(): Icon
{
return new Icon(match ($this) {
self::Info => 'circle-info',
self::Success => 'circle-check',
self::Warning => 'warning',
self::Error => 'circle-xmark',
});
}
}
104 changes: 104 additions & 0 deletions src/Widget/Callout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace ipl\Web\Widget;

use ipl\Html\Attributes;
use ipl\Html\BaseHtmlElement;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Html\ValidHtml;
use ipl\Web\Common\CalloutType;

/**
* Information box with a type specific color and icon
*
* The type controls both the color scheme and the icon. An optional title
* is displayed above the content.
*/
Comment thread
TheSyscall marked this conversation as resolved.
class Callout extends BaseHtmlElement
Comment thread
jrauh01 marked this conversation as resolved.
{
/** @var string Class name for form element callouts */
protected const CLASS_FORM_ELEMENT = 'callout-form-element';

/** @var string Class name for full width callouts */
protected const CLASS_FULL_WIDTH = 'callout-full-width';

protected $tag = 'div';

protected $defaultAttributes = ['class' => 'callout'];

/**
* Create a new callout
*
* The $type parameter determines the color and icon of the callout.
*
* @param CalloutType $type The type of the callout
* @param ValidHtml|string $content The content of the callout
* @param ?string $title An optional title, displayed above the content
*/
public function __construct(
protected CalloutType $type,
protected ValidHtml|string $content,
protected ?string $title = null
) {
$this->addAttributes(Attributes::create(['class' => $type->value]));
}

protected function assemble(): void
{
$this->addHtml($this->type->getIcon());

$this->addHtml(HtmlElement::create(
'div',
['class' => 'callout-text'],
[
$this->title
? HtmlElement::create('strong', ['class' => 'callout-title'], Text::create($this->title))
: null,
is_string($this->content) ? Text::create($this->content) : $this->content,
],
));
}

/**
* Set the callout width to 100% of its parent container
*
* Callouts are normally only as wide as their content.
* Setting it to full width will force the callout to be as wide as its container.
*
* @param bool $isFullWidth Whether the callout should be full width
*
* @return $this
*/
public function setFullWidth(bool $isFullWidth = true): static
{
if ($isFullWidth) {
$this->addAttributes(Attributes::create(['class' => static::CLASS_FULL_WIDTH]));
} else {
$this->removeAttribute('class', static::CLASS_FULL_WIDTH);
}

return $this;
}

/**
* Set up the callout to be used inside a form
*
* Setting this to true will allow the callout to be used for a single form element.
* This is used to visually align the callout to the content of the form element.
*
* @param bool $isFormElement Whether the callout should be used for a form element
*
* @return $this
*/
public function setFormElement(bool $isFormElement = true): static
Comment thread
jrauh01 marked this conversation as resolved.
{
if ($isFormElement) {
$this->addAttributes(Attributes::create(['class' => static::CLASS_FORM_ELEMENT]));
} else {
$this->removeAttribute('class', static::CLASS_FORM_ELEMENT);
}

return $this;
}
}
Loading