# HStack

[🔰 CSS](../..) ⟩ [Layout](..) ⟩ [System]() ⟩

{% tabs %}
{% tab title="👥 相關" %}
* [VStack](vstack), [Spacer](spacer)
* [flex](../display/flex/flex/flex)
{% endtab %}

{% tab title="📘 手冊" %}
* MDN ⟩&#x20;
{% endtab %}
{% endtabs %}

{% hint style="info" %}
在排版時，如果 **HStack** 內部某個元件需要「**橫向擴展**」，可以使用 [**flex**](../display/flex/flex/flex)**: 1**。
{% endhint %}

## versions

{% tabs %}
{% tab title="0.0.3" %}
{% embed url="https://codesandbox.io/s/layout-hstack-v-0-0-3-dpveb?file=/styles.css" %}
layout v.0.0.3
{% endembed %}
{% endtab %}

{% tab title="hstack.mjs" %}
```javascript
// 0.0.5 + helpers

import {attr} from './helpers.mjs';

// HStack
export default class HStack extends HTMLElement {

  // this.render()
  render() {

    const {spacing, alignment} = this;
    const attrs = [spacing, alignment].filter(attr => !!attr);   // remove empty attributes

    // if no specific settings, use default values and return.
    if (attrs.length === 0) return;

    // style ID for this element
    const styleID = `hstack-${attrs.join('_')}`;

    // put HStack style ID in "data-xxx" attribute
    this.dataset.style = styleID;

    // if no such <style> with this ID, create it.
    if (!document.getElementById(styleID)) {

        // create <style>
        let style = document.createElement('style');
        style.id = styleID;

        // concate css rules
        const rules = [
            // spacing between items
            `${spacing ? `[data-style="${styleID}"] > * + :not(i-spacer) {margin-left: ${spacing};}` : ''}`,
            // stack alignmentment
            `${alignment ? `[data-style="${styleID}"] {align-items: ${alignment};}` : ''}`,
        ]
            .filter(rule => rule !== '')    // remove empty rules
            .join('\n');

        style.innerHTML = `${rules}`;
        document.head.appendChild(style);
    }
  }

  // -------------- getters/setters -----------------

  // this.spacing (= '30px')
  get spacing() { return attr(this, 'spacing') }
  set spacing(val) { return attr(this, 'spacing', val) }

  // this.alignment (= 'flex-start' | 'center' | 'flex-end')
  get alignment() { return attr(this, 'alignment') }
  set alignment(val) { return attr(this, 'alignment', val) }

  // -------------- lifecycle callbacks ----------------

  // ⭐ connected to document
  connectedCallback() {
    this.render();
  }

  // ⭐ observed attributes
  static get observedAttributes() {
    return ['spacing', 'alignment'];
  }

  // re-renders whenever one of attributes changes.
  attributeChangedCallback() {
    this.render();
  }
}

// ⭐️ register <hstack->
customElements.get('hstack-') || customElements.define('hstack-', HStack);

```
{% endtab %}

{% tab title="hstack.css" %}
```css
/* 0.1.0 - align-items: center -> stretch */

/* hstack */
hstack- {
  display: flex;
  flex-direction: row;
  align-items: stretch;
}

/* hstack 內非 spacer 的間隔 */
hstack- > * + :not(spacer-) {
  margin-left: var(--spacing);
}

/* 
    如果只有唯一一個 hstack，就讓它的寬度為 100%，
    這樣裡面的 spacer 才能發生作用。
 */
hstack-:only-child {
    width: 100%;
}
```
{% endtab %}

{% tab title="⬆️ 需要" %}
* [helper functions](../../../appendix/custom/helper-functions)
{% endtab %}

{% tab title="👥 相關" %}
* [VStack](vstack), [Spacer](spacer)
{% endtab %}

{% tab title="💾 程式" %}
* replit ⟩ [layout 0.1.0](https://replit.com/@pegasusroe/layout-010-refactor#layout/hstack.css) - align-items: center ➜ stretch
{% endtab %}
{% endtabs %}
