Skip to content

Instantly share code, notes, and snippets.

@mrclay
Created December 9, 2017 16:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrclay/64b6301fb7adcea0ff0716f60639bcd2 to your computer and use it in GitHub Desktop.
Save mrclay/64b6301fb7adcea0ff0716f60639bcd2 to your computer and use it in GitHub Desktop.
HTML class list based on DOMTokenList
<?php
namespace MrClay;
/**
* HTML class list based on DOMTokenList
*
* @link https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList
*/
class ClassList
{
/**
* Classnames (each a key mapping to true)
*
* @var int[]
*/
protected $namesAsKeys = array();
/**
* Constructor
*
* @param string|string[]|ClassList $names Name(s) for list
*/
public function __construct($names = null)
{
$this->add($names);
}
/**
* Get as an HTML class string
*
* @return string
*/
public function value()
{
return implode(' ', $this->values());
}
/**
* Alias of values()
*
* @return string
*/
public function toString()
{
return $this->value();
}
/**
* Count the names
*
* @return int
*/
public function length()
{
return count($this->namesAsKeys);
}
/**
* Get a name by index
*
* @param int $index Index
* @return string|null
*/
public function item($index)
{
$names = $this->values();
return isset($names[$index]) ? $names[$index] : null;
}
/**
* Does the list contain a name?
*
* @param string $name Name
* @return bool
*/
public function contains($name)
{
return isset($this->namesAsKeys[trim($name)]);
}
/**
* Add name(s)
*
* @param string|string[]|ClassList $names Name(s)
* @return self
*/
public function add($names)
{
foreach ($this->normalizeInput($names) as $name) {
$this->namesAsKeys[$name] = true;
}
return $this;
}
/**
* Remove name(s)
*
* @param string|string[]|ClassList $names Name(s)
* @return self
*/
public function remove($names)
{
foreach ($this->normalizeInput($names) as $name) {
unset($this->namesAsKeys[$name]);
}
return $this;
}
/**
* Replace a name with another
*
* @param string $old Name to replace
* @param string $new Name to replace with
* @return self
*/
public function replace($old, $new)
{
$old = trim($old);
$new = trim($new);
// preserve order of names
$tmp = $this->namesAsKeys;
$this->namesAsKeys = array();
foreach ($tmp as $name => $true) {
$this->namesAsKeys[$name === $old ? $new : $name] = true;
}
return $this;
}
/**
* Toggle a name
*
* @param string $name Name
* @param bool $force If set, class will be added/removed based on truth/false
* @return bool
*/
public function toggle($name, $force = null)
{
if (isset($force)) {
$force ? $this->add($name) : $this->remove($name);
return (bool) $force;
}
if ($this->contains($name)) {
$this->remove($name);
return false;
}
$this->add($name);
return true;
}
/**
* Reset the contents
*
* @param string|string[]|ClassList $names Name(s)
* @return self
*/
public function reset($names = null)
{
$this->namesAsKeys = array();
return $this->add($names);
}
/**
* Get all names
*
* @return string[]
*/
public function values()
{
return array_keys($this->namesAsKeys);
}
/**
* Factory accepting attribute array
*
* @param array $attrs Attributes
* @param string $key Key under which to find the classes
* @return self
*/
public static function fromAttrs($attrs, $key = 'class')
{
$names = isset($attrs[$key]) ? $attrs[$key] : null;
return new static($names);
}
/**
* Factory accepting values
*
* @param string|string[]|ClassList $names Name(s)
* @return self
*/
public static function fromValues($names = null)
{
return new static($names);
}
/**
* Normalize an input value to array of non-empty strings
*
* @param string|string[]|ClassList $names Name(s)
* @return string[]
*/
protected function normalizeInput($names)
{
if ($names === null) {
return array();
}
if ($names instanceof ClassList) {
return $names->values();
}
if (is_string($names)) {
$names = explode(' ', $names);
}
$ret = array();
foreach ($names as $name) {
$name = trim($name);
if ($name !== '') {
$ret[] = $name;
}
}
return $ret;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment