# @qaecy/cue-ui

Framework-agnostic web components for the [QAECY Cue platform](https://qaecy.com).  
Drop them into any HTML page — no build step required.

## Components

| Custom element                  | Description                                                                       |
| ------------------------------- | --------------------------------------------------------------------------------- |
| `<cue-logo>`                    | Animated QAECY logo                                                               |
| `<cue-document-viewer>`         | Document viewer with Cue SDK integration (PDF, BIM, CAD, images, spreadsheets, …) |
| `<cue-document-list>`           | Lazy document list that fetches metadata from the Cue SDK                         |
| `<cue-entity-list>`             | Lazy entity list that fetches entity data from the Cue SDK                        |
| `<cue-entity-viewer>`           | Full entity detail view — info, relationships, documents, and optional map        |
| `<cue-id-builder>`              | Interactive IFC/SPARQL ID builder                                                 |
| `<cue-block-project-documents>` | Project documents content block with charts and statistics                        |

---

## CDN quickstart

The library ships as a **single script** that lazy-loads only the components you request — no `npm install` needed.

```html
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>My Cue App</title>
        <!-- Optional: component styles -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@qaecy/cue-ui/styles.css" />
        <style>
            body {
                margin: 0;
                height: 100vh;
                display: flex;
                flex-direction: column;
            }
            cue-logo {
                width: 160px;
                padding: 1rem;
            }
            cue-document-viewer {
                flex: 1;
            }
        </style>
    </head>
    <body>
        <!-- Static elements are detected automatically -->
        <cue-logo size="m"></cue-logo>

        <!-- Document viewer (populated via JS below) -->
        <div id="viewer-host" style="flex:1; display:contents;"></div>

        <!--
          1. Single loader script. Angular boots once; only requested components
             are downloaded as separate lazy chunks.

          data-components lists components that will be created dynamically
          (i.e. not present in the HTML at load time). Statically placed tags
          are detected automatically and do not need to be listed here.
        -->
        <script
            src="https://cdn.jsdelivr.net/npm/@qaecy/cue-ui/index.js"
            type="module"
            data-components="cue-document-viewer"
        ></script>

        <!-- 2. Cue SDK + app logic -->
        <script type="module">
            import { Cue } from 'https://cdn.jsdelivr.net/npm/@qaecy/cue-sdk/dist/index.esm.js';

            // 3. Authenticate
            const cue = new Cue();
            await cue.auth.signIn('google');

            // 4. Mount the document viewer once the custom element is registered
            await customElements.whenDefined('cue-document-viewer');

            const viewer = document.createElement('cue-document-viewer');

            // Required: authenticated SDK instance (must be set as a property, not an attribute)
            viewer.cue = cue;
            viewer.uuid = 'your-document-uuid'; // or 'uuid?page=3' to open at a page
            viewer.projectId = 'your-project-id';

            document.getElementById('viewer-host').appendChild(viewer);
        </script>
    </body>
</html>
```

### Installing via npm

```bash
npm install @qaecy/cue-sdk @qaecy/cue-ui
```

```js
// Import the loader — registers the smart loader that boots Angular once
import '@qaecy/cue-ui';

// Optionally import styles
import '@qaecy/cue-ui/styles';
```

---

## `<cue-logo>`

Renders the animated QAECY logo. Adapts automatically to light/dark mode.

| Property / attribute | Type                | Default | Description                      |
| -------------------- | ------------------- | ------- | -------------------------------- |
| `size`               | `'s' \| 'm' \| 'l'` | `'m'`   | Size preset                      |
| `active`             | `boolean`           | `true`  | Triggers the draw animation once |
| `continuous`         | `boolean`           | `false` | Loops the draw animation         |

```html
<!-- Static logo -->
<cue-logo size="l"></cue-logo>

<!-- Animated on load -->
<cue-logo size="m" active></cue-logo>

<!-- Continuously looping -->
<cue-logo continuous></cue-logo>
```

> **Loader** — add `cue-logo` to `data-components` if you create it dynamically; otherwise it is detected automatically. See [CDN quickstart](#cdn-quickstart).

---

## `<cue-entity-list>`

Renders a lazy-loaded entity table. Pass `iris`, a `sdkState` containing a `CueProjectEntities` instance, and optionally a Mapbox token for location views.

| Property             | Type                  | Required | Description                                                                          |
| -------------------- | --------------------- | -------- | ------------------------------------------------------------------------------------ |
| `iris`               | `string[]`            | ✅       | Entity IRIs to display in the list.                                                  |
| `sdkState`           | `{ cue?; entities? }` | ✅       | SDK context. Provide at least `entities` (`CueProjectEntities` instance).            |
| `mapboxToken`        | `string`              | ❌       | Mapbox access token. Required only if entity location data should be shown on a map. |
| `paginationPageSize` | `number`              | ❌       | Number of rows shown per page in the grid paginator. Default: `10`.                  |

> **Mapbox token** — the map view is only active when a valid `mapboxToken` is set. Without it, location fetching is skipped and the map tab is hidden. Obtain a token from [mapbox.com](https://www.mapbox.com/).

The component emits two events:

| Event         | Payload     | Description                                 |
| ------------- | ----------- | ------------------------------------------- |
| `rowClicked`  | `EntityRow` | Fires when the user clicks a row.           |
| `clickedOpen` | `EntityRow` | Fires when the user opens an entity detail. |

```js
await customElements.whenDefined('cue-entity-list');

const list = document.createElement('cue-entity-list');

// sdkState must be set as a JS property, not an HTML attribute
list.sdkState = { entities: cue.project('your-project-id').entities };
list.iris = ['https://example.com/entity/uuid-1', 'https://example.com/entity/uuid-2'];
list.paginationPageSize = 25;

list.addEventListener('rowClicked', (e) => console.log('Row clicked:', e.detail));
list.addEventListener('clickedOpen', (e) => console.log('Open entity:', e.detail));

document.getElementById('list-host').appendChild(list);
```

> **Loader** — add `cue-entity-list` to `data-components` if you create it dynamically; otherwise it is detected automatically. See [CDN quickstart](#cdn-quickstart).

---

## `<cue-id-builder>`

A self-contained interactive tool that converts a text string into a context-based GUID (used for IFC/SPARQL identifiers). No external inputs required — drop it in and it works standalone.

```html
<cue-id-builder></cue-id-builder>
```

> **Loader** — add `cue-id-builder` to `data-components` if you create it dynamically; otherwise it is detected automatically. See [CDN quickstart](#cdn-quickstart).

---

## `<cue-document-viewer>`

Fetches and renders a Cue document. Automatically picks the best viewer based on the file type. Requires an authenticated `Cue` SDK instance.

| Property    | Type                 | Required | Description                                                                          |
| ----------- | -------------------- | -------- | ------------------------------------------------------------------------------------ |
| `cue`       | `Cue` (SDK instance) | ✅       | Authenticated Cue SDK instance. **Set as a JS property**, not an HTML attribute.     |
| `uuid`      | `string`             | ✅       | Document UUID. Append `?page=N` to open at a specific page, e.g. `'abc-123?page=5'`. |
| `projectId` | `string`             | ✅       | Cue project (space) ID the document belongs to.                                      |

Language is managed globally by the SDK via `cue.api.language` / `cue.api.setLanguage(lang)` and applies to all language-sensitive requests.  
Document metadata caching is also managed by the SDK; the component reuses SDK-owned per-project document state automatically.

Supported file types: PDF, IFC/BIM, DXF/CAD, images (PNG, JPG, SVG, …), Markdown, plain text, CSV/XLSX spreadsheets.

> **Loader** — add `cue-document-viewer` to `data-components` if you create it dynamically; otherwise it is detected automatically. See [CDN quickstart](#cdn-quickstart).

---

## `<cue-document-list>`

Renders a lazy-loaded document table. Pass a `projectId`, `uuids`, and `sdkState`; the component requests metadata and categories internally.

| Property                         | Type                                                                  | Required | Description                                                                            |
| -------------------------------- | --------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------- |
| `projectId`                      | `string`                                                              | ✅       | Cue project (space) ID.                                                                |
| `uuids`                          | `string[]`                                                            | ✅       | Document UUIDs to display.                                                             |
| `sdkState`                       | `{ cue?; view?; documents?; language?; availableContentCategories? }` | ✅       | SDK context. Provide at least one of `cue`, `view`, or `documents`.                    |
| `simple`                         | `boolean`                                                             | ❌       | Renders a compact list + paginator instead of ag-grid/flip/settings. Default: `false`. |
| `pageSize`                       | `number`                                                              | ❌       | Host-controlled page size used by both simple and non-simple modes. Default: `10`.     |
| `prefetchPages`                  | `number`                                                              | ❌       | Lazy-load buffer size multiplier (`pageSize * prefetchPages`). Default: `3`.           |
| `showOpenInDocumentViewerAction` | `boolean`                                                             | ❌       | Shows "Open in document viewer" menu item. Default: `false`.                           |
| `showOpenInFileManagerAction`    | `boolean`                                                             | ❌       | Shows "Open in file manager" menu item. Default: `false`.                              |
| `customMenuItems`                | `Array<{ label; icon?; action }>`                                     | ❌       | Custom context-menu entries appended after the standard menu items.                    |

By default, only the `Download` action is shown in the row context menu.

Set `simple = true` for a narrow sidebar-style document list (no ag-grid, no column settings, no flip card).

The component emits the same row click outputs as the internal document list (`clickedDocument`, `clickedDownloadDocument`, `clickedOpen`, `clickedOpenInDir`).

Custom menu items are appended after the standard ones:

```js
const list = document.querySelector('cue-document-list');

list.customMenuItems = [
    {
        label: 'Copy UUID',
        icon: 'copy',
        action: (document) => {
            navigator.clipboard.writeText(document.id);
        },
    },
    {
        label: 'Log Row',
        icon: 'document',
        action: (document, cell) => {
            console.log('Document:', document, 'Cell data:', cell);
        },
    },
];
```

> **Loader** — add `cue-document-list` to `data-components` if you create it dynamically; otherwise it is detected automatically. See [CDN quickstart](#cdn-quickstart).

---

## `<cue-block-project-documents>`

A content block that visualises project document statistics — documents by content category, by file type, total size, and duplicates — using interactive pie charts.

| Property | Type                   | Required | Description                                                                                      |
| -------- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `data`   | `ProjectDocumentsData` | ❌       | Document statistics payload. The component renders empty until set.                              |
| `config` | `BlockConfig`          | ✅       | Block configuration including `projectId`, `handlers`, and privileges. **Set as a JS property.** |

```js
await customElements.whenDefined('cue-block-project-documents');

const block = document.createElement('cue-block-project-documents');

block.config = {
    projectId: 'your-project-id',
    graphIsSaturated: false,
    privileges: { canEdit: false },
    portalBaseURL: 'https://app.qaecy.com',
    resourceBaseURL: 'https://cue.qaecy.com/r/',
};

block.data = {
    documentsBySuffix: { pdf: { count: 12, size: 4096000 } },
    documentsByContentCategory: { drawings: { count: 5, size: 1024000 } },
    duplicateCount: 2,
};

document.getElementById('block-host').appendChild(block);
```

> **Loader** — add `cue-block-project-documents` to `data-components` if you create it dynamically; otherwise it is detected automatically. See [CDN quickstart](#cdn-quickstart).

---

## `<cue-entity-viewer>`

Renders the full detail view for a single entity: an info panel (label + categories), a relationship graph, linked documents, and an optional location map.

Requires an authenticated `Cue` SDK instance. Internally creates `CueProjectEntities` and `CueProjectDocuments` instances and wires all data-fetching automatically.

| Property      | Type                 | Required | Description                                                                               |
| ------------- | -------------------- | -------- | ----------------------------------------------------------------------------------------- |
| `cue`         | `Cue` (SDK instance) | ✅       | Authenticated Cue SDK instance. **Set as a JS property**, not an HTML attribute.          |
| `projectId`   | `string`             | ✅       | Cue project (space) ID that the entity belongs to.                                        |
| `iri`         | `string`             | ✅       | Full RDF IRI of the entity to display, e.g. `'https://cue.qaecy.com/r/{project}/{uuid}'`. |
| `mapboxToken` | `string`             | ❌       | Mapbox access token. Required to show entity location on a map. Omit to hide the map tab. |

> **Mapbox token** — the map tab is only shown when a valid `mapboxToken` is provided. Without it, no map-related data is fetched and the tab does not appear. Obtain a token from [mapbox.com](https://www.mapbox.com/).

The component emits one event:

| Event        | Payload  | Description                                             |
| ------------ | -------- | ------------------------------------------------------- |
| `openEntity` | `string` | Entity UUID when the user navigates to a linked entity. |

```js
await customElements.whenDefined('cue-entity-viewer');

const viewer = document.createElement('cue-entity-viewer');

// Required: set as JS properties, not HTML attributes
viewer.cue = cue;
viewer.projectId = 'your-project-id';
viewer.iri = 'https://cue.qaecy.com/r/your-project-id/entity-uuid';

// Optional: enable the location map tab
viewer.mapboxToken = 'pk.eyJ1…';

// Navigate within your app when the user opens a related entity
viewer.addEventListener('openEntity', (e) => {
    viewer.iri = `https://cue.qaecy.com/r/your-project-id/${e.detail}`;
});

document.getElementById('viewer-host').appendChild(viewer);
```

> **Loader** — add `cue-entity-viewer` to `data-components` if you create it dynamically; otherwise it is detected automatically. See [CDN quickstart](#cdn-quickstart).

---

## Dependency: `@qaecy/cue-sdk`

The `cue-document-viewer`, `cue-document-list`, `cue-entity-list`, and `cue-entity-viewer` components require an authenticated [`Cue`](https://www.npmjs.com/package/@qaecy/cue-sdk) instance. The SDK handles authentication and all data fetching — the components never make direct network calls.

```js
import { Cue } from 'https://cdn.jsdelivr.net/npm/@qaecy/cue-sdk/dist/index.esm.js';

const cue = new Cue();
await cue.auth.signIn('google'); // or 'microsoft', 'email', etc.
```

---

## Theming

All visual properties are driven by CSS custom properties defined on `:root`. Because the components do **not** use Shadow DOM, you can override any variable from your own stylesheet — no `::part()` or `::slotted()` selectors needed.

### Colors

```css
:root {
    /* Brand palette */
    --cue-color-blue: #2859e1;
    --cue-color-green: #e2f552; /* accent */

    /* Semantic color keys — override these to retheme without touching raw values */
    --cue-primary: var(--cue-color-blue);
    --cue-primaryContrast: #ffffff;

    --cue-accent: var(--cue-color-green);
    --cue-accentContrast: #121c2b;

    --cue-main-background: #f5f6f9;
    --cue-main-foreground: #121c2b;

    --cue-border-color: #d9d9d9;
}
```

Apply a dark theme by adding the `dark` class to `<body>` (or any ancestor element):

```css
body.dark {
    --cue-main-background: #121c2b;
    --cue-main-foreground: #ffffff;
    --cue-default: #0e1827;
    --cue-defaultContrast: #f5f6f9;
    --cue-border-color: #404955;
}
```

### Typography

```css
:root {
    --cue-font-family: 'Inter', sans-serif; /* swap out Poppins */

    --cue-font-weight-regular: 400;
    --cue-font-weight-medium: 500;
    --cue-font-weight-semibold: 600;
}
```

> The default font is **Poppins**, loaded via the bundled stylesheet. If you override `--cue-font-family`, load your chosen font yourself (e.g. via Google Fonts).

### Spacing & element sizes

```css
:root {
    /* Gap / padding scale */
    --cue-dim-gap-xs: 0.25rem;
    --cue-dim-gap-s: 0.5rem;
    --cue-dim-gap-m: 1rem;
    --cue-dim-gap-l: 1.875rem;

    /* Interactive element heights */
    --cue-dim-elem-s: 2rem; /* small buttons, inputs */
    --cue-dim-elem-m: 2.75rem; /* default */
    --cue-dim-elem-l: 3.5rem; /* large */
}
```

### Minimal brand override example

```html
<style>
    :root {
        --cue-primary: #7c3aed; /* violet */
        --cue-primaryContrast: #ffffff;
        --cue-accent: #f59e0b; /* amber */
        --cue-accentContrast: #1c1917;
        --cue-font-family: 'Inter', sans-serif;
    }
</style>
```
