name: API
purpose: Bind HTTP request configuration and response state to a declarative page element.

attributes:
  auto:
    type: boolean
    behavior: Request after request changes when request.url exists.

properties:
  request:
    default:
      url: ''
      method: "GET when data is null; POST when data is present, unless explicitly set."
      headers: {}
      data: null
      timeout: 10000
      responseType: ''
  response:
    fields: [loading, ok, status, error, headers, responseType, result]
  result:
    behavior: Latest response result.

methods:
  do:
    signature: api.do(options)
    behavior: Merge options with request and perform HTTP.request.
    options:
      noui: Suppress automatic error toast.

events:
  response:
    detail: Complete HTTP response object.
  error:
    detail: Error object.

rules:
  - request.url is required before calling do.
  - Without request.method, API uses POST when request.data is present and GET otherwise.
  - Bind request with $.request when external state owns request configuration.
  - Bind asynchronous result fields directly to component state, for example `$.state.list="usersApi.result.list"`.

examples:
  auto: |
    <script>
      const usersRequest = { url: '/api/users', method: 'GET' }
    </script>
    <API id="usersApi" auto $.request="usersRequest"></API>
    <List $.state.list="usersApi.result.list"></List>
  do: |
    <script>
      const loadUsers = async () => {
        const response = await usersApi.do({ url: '/api/users', method: 'GET', noui: true })
        if (response.ok) console.log(response.result)
      }
    </script>
    <API id="usersApi"></API>
    <button $onclick="loadUsers()">Load users</button>
  post_default: |
    <API auto .request.url="/admin/table" .request.data.action="tables"></API>
    <!-- data is present, so this request uses POST unless method is explicitly set -->

tests:
  - API.test.html
