@signalwire/web-components 4.0.0-beta.10 → 4.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/components/audio-level.d.ts +8 -0
  2. package/dist/components/audio-level.d.ts.map +1 -1
  3. package/dist/components/audio-level.js.map +1 -1
  4. package/dist/components/call-controls.d.ts +28 -8
  5. package/dist/components/call-controls.d.ts.map +1 -1
  6. package/dist/components/call-controls.js +4 -4
  7. package/dist/components/call-controls.js.map +1 -1
  8. package/dist/components/call-media.d.ts +4 -0
  9. package/dist/components/call-media.d.ts.map +1 -1
  10. package/dist/components/call-media.js.map +1 -1
  11. package/dist/components/call-status.d.ts +15 -0
  12. package/dist/components/call-status.d.ts.map +1 -1
  13. package/dist/components/call-status.js +12 -11
  14. package/dist/components/call-status.js.map +1 -1
  15. package/dist/components/click-to-call.d.ts +29 -1
  16. package/dist/components/click-to-call.d.ts.map +1 -1
  17. package/dist/components/click-to-call.js +1 -1
  18. package/dist/components/click-to-call.js.map +1 -1
  19. package/dist/components/device-selector.d.ts +14 -0
  20. package/dist/components/device-selector.d.ts.map +1 -1
  21. package/dist/components/device-selector.js.map +1 -1
  22. package/dist/components/dialpad.d.ts +23 -9
  23. package/dist/components/dialpad.d.ts.map +1 -1
  24. package/dist/components/dialpad.js.map +1 -1
  25. package/dist/components/directory.d.ts +22 -0
  26. package/dist/components/directory.d.ts.map +1 -1
  27. package/dist/components/directory.js.map +1 -1
  28. package/dist/components/example-button.d.ts +1 -0
  29. package/dist/components/example-button.d.ts.map +1 -1
  30. package/dist/components/example-button.js.map +1 -1
  31. package/dist/components/participant-controls.d.ts +32 -3
  32. package/dist/components/participant-controls.d.ts.map +1 -1
  33. package/dist/components/participant-controls.js +3 -3
  34. package/dist/components/participant-controls.js.map +1 -1
  35. package/dist/components/participants.d.ts +4 -0
  36. package/dist/components/participants.d.ts.map +1 -1
  37. package/dist/components/participants.js.map +1 -1
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js.map +1 -1
  41. package/dist/react.d.ts +49 -28
  42. package/dist/types/index.d.ts +9 -33
  43. package/dist/types/index.d.ts.map +1 -1
  44. package/dist/types/index.js +6 -6
  45. package/dist/types/index.js.map +1 -1
  46. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"dialpad.js","sources":["../../src/components/dialpad.ts"],"sourcesContent":["/**\n * Dialpad Component\n *\n * A 12-key telephone keypad (0-9, *, #) for entering phone numbers\n * and sending DTMF tones during active calls.\n *\n * @example\n * ```html\n * <sw-dialpad .call=${call}></sw-dialpad>\n * ```\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { consume } from '@lit/context';\nimport type { Observable } from 'rxjs';\nimport { callContext } from '../context/index.js';\n\n/**\n * Call interface for dialpad component\n */\nexport interface DialpadCall {\n sendDigits?(dtmf: string): Promise<void>;\n status$?: Observable<string>;\n}\n\n/**\n * Key layout for standard telephone keypad\n */\nconst KEYS = [\n { digit: '1', letters: '' },\n { digit: '2', letters: 'ABC' },\n { digit: '3', letters: 'DEF' },\n { digit: '4', letters: 'GHI' },\n { digit: '5', letters: 'JKL' },\n { digit: '6', letters: 'MNO' },\n { digit: '7', letters: 'PQRS' },\n { digit: '8', letters: 'TUV' },\n { digit: '9', letters: 'WXYZ' },\n { digit: '*', letters: '' },\n { digit: '0', letters: '+' },\n { digit: '#', letters: '' }\n];\n\n@customElement('sw-dialpad')\nexport class DialpadComponent extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-primary-hover: #0339c4;\n --sw-color-primary-active: #022b92;\n --sw-color-success: #10b981;\n --sw-color-danger: #ef4444;\n --sw-color-text: #1f2937;\n --sw-color-text-muted: #6b7280;\n --sw-color-border: #e5e7eb;\n --sw-color-background: #ffffff;\n --sw-color-background-hover: #f3f4f6;\n --sw-color-background-active: #e5e7eb;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-font-size-sm: 12px;\n --sw-font-size-base: 16px;\n --sw-font-size-lg: 24px;\n --sw-font-size-xl: 32px;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-space-4: 16px;\n --sw-border-radius: 8px;\n --sw-border-radius-full: 9999px;\n\n display: block;\n font-family: var(--sw-font-family);\n }\n\n /* Dark mode support */\n :host([data-theme='dark']) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-border: #374151;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([data-theme='light'])) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-border: #374151;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n }\n }\n\n .container {\n display: flex;\n flex-direction: column;\n gap: var(--sw-space-3);\n padding: var(--sw-space-4);\n max-width: 280px;\n background: var(--sw-color-background);\n border-radius: var(--sw-border-radius);\n }\n\n .display {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--sw-space-3) var(--sw-space-4);\n background: var(--sw-color-background-hover);\n border-radius: var(--sw-border-radius);\n min-height: 48px;\n }\n\n .display-input {\n flex: 1;\n font-size: var(--sw-font-size-xl);\n font-weight: 500;\n font-family: var(--sw-font-family);\n color: var(--sw-color-text);\n background: transparent;\n border: none;\n outline: none;\n letter-spacing: 2px;\n text-align: center;\n }\n\n .display-input::placeholder {\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-base);\n letter-spacing: normal;\n }\n\n .backspace-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background: transparent;\n border: none;\n border-radius: var(--sw-border-radius);\n cursor: pointer;\n color: var(--sw-color-text-muted);\n transition:\n background-color 0.15s ease,\n color 0.15s ease;\n }\n\n .backspace-button:hover {\n background: var(--sw-color-background-active);\n color: var(--sw-color-text);\n }\n\n .backspace-button:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n\n .keypad {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--sw-space-2);\n }\n\n .key {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n width: 72px;\n height: 72px;\n background: var(--sw-color-background);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-border-radius-full);\n cursor: pointer;\n transition:\n background-color 0.1s ease,\n transform 0.1s ease;\n user-select: none;\n -webkit-user-select: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n .key:hover {\n background: var(--sw-color-background-hover);\n }\n\n .key:active,\n .key.pressed {\n background: var(--sw-color-background-active);\n transform: scale(0.95);\n }\n\n .key-digit {\n font-size: var(--sw-font-size-lg);\n font-weight: 500;\n color: var(--sw-color-text);\n line-height: 1;\n }\n\n .key-letters {\n font-size: var(--sw-font-size-sm);\n color: var(--sw-color-text-muted);\n text-transform: uppercase;\n letter-spacing: 1px;\n margin-top: 2px;\n min-height: 14px;\n }\n\n .call-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 56px;\n background: var(--sw-color-success);\n border: none;\n border-radius: var(--sw-border-radius);\n cursor: pointer;\n color: white;\n font-size: var(--sw-font-size-base);\n font-weight: 600;\n font-family: var(--sw-font-family);\n transition: background-color 0.15s ease;\n gap: var(--sw-space-2);\n }\n\n .call-button:hover {\n background: #0ea472;\n }\n\n .call-button:active {\n background: #0d9668;\n }\n\n .call-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .call-button svg {\n width: 20px;\n height: 20px;\n }\n `;\n\n /**\n * Call object with sendDigits method\n */\n @property({ attribute: false })\n call: DialpadCall | null = null;\n\n /**\n * Call from context (if nested in sw-call-media)\n */\n @consume({ context: callContext, subscribe: true })\n @state()\n private contextCall?: DialpadCall;\n\n /**\n * Current digits entered\n */\n @state()\n private digits: string = '';\n\n /**\n * Currently pressed key (for visual feedback)\n */\n @state()\n private pressedKey: string | null = null;\n\n /**\n * Whether to show the call button\n */\n @property({ type: Boolean, attribute: 'show-call-button' })\n showCallButton: boolean = false;\n\n /**\n * Placeholder text for display input\n */\n @property({ type: String })\n placeholder: string = 'Enter number';\n\n private get activeCall(): DialpadCall | null {\n return this.call || this.contextCall || null;\n }\n\n private handleKeyPress(digit: string) {\n this.digits += digit;\n this.pressedKey = digit;\n\n // Send DTMF if call is active\n if (this.activeCall?.sendDigits) {\n this.activeCall.sendDigits(digit).catch((err) => {\n console.error('Failed to send DTMF:', err);\n });\n }\n\n // Dispatch event\n this.dispatchEvent(\n new CustomEvent('sw-digit-press', {\n detail: { digit, digits: this.digits },\n bubbles: true,\n composed: true\n })\n );\n\n // Reset pressed state after animation\n setTimeout(() => {\n this.pressedKey = null;\n }, 100);\n }\n\n private handleBackspace() {\n if (this.digits.length > 0) {\n this.digits = this.digits.slice(0, -1);\n this.dispatchEvent(\n new CustomEvent('sw-backspace', {\n detail: { digits: this.digits },\n bubbles: true,\n composed: true\n })\n );\n }\n }\n\n private handleCall() {\n if (this.digits.length > 0) {\n this.dispatchEvent(\n new CustomEvent('sw-dial', {\n detail: { digits: this.digits },\n bubbles: true,\n composed: true\n })\n );\n }\n }\n\n private handleInputChange(e: Event) {\n const input = e.target as HTMLInputElement;\n // Only allow valid DTMF characters\n this.digits = input.value.replace(/[^0-9*#]/g, '');\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n const key = e.key;\n // Handle keyboard input for digits\n if (/^[0-9*#]$/.test(key)) {\n this.handleKeyPress(key);\n } else if (key === 'Backspace') {\n this.handleBackspace();\n } else if (key === 'Enter' && this.showCallButton) {\n this.handleCall();\n }\n }\n\n render() {\n return html`\n <div class=\"container\" part=\"container\">\n <div class=\"display\" part=\"display\">\n <input\n type=\"tel\"\n class=\"display-input\"\n .value=${this.digits}\n placeholder=${this.placeholder}\n @input=${this.handleInputChange}\n @keydown=${this.handleKeyDown}\n aria-label=\"Phone number input\"\n />\n <button\n class=\"backspace-button\"\n @click=${this.handleBackspace}\n ?disabled=${this.digits.length === 0}\n aria-label=\"Delete last digit\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M21 4H8l-7 8 7 8h13a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z\"></path>\n <line x1=\"18\" y1=\"9\" x2=\"12\" y2=\"15\"></line>\n <line x1=\"12\" y1=\"9\" x2=\"18\" y2=\"15\"></line>\n </svg>\n </button>\n </div>\n\n <div class=\"keypad\" part=\"keypad\" role=\"group\" aria-label=\"Telephone keypad\">\n ${KEYS.map(\n (key) => html`\n <button\n class=\"key ${this.pressedKey === key.digit ? 'pressed' : ''}\"\n part=\"key ${this.pressedKey === key.digit ? 'key-pressed' : ''}\"\n @click=${() => this.handleKeyPress(key.digit)}\n aria-label=\"${key.digit}${key.letters ? `, ${key.letters}` : ''}\"\n >\n <span class=\"key-digit\">${key.digit}</span>\n <span class=\"key-letters\">${key.letters}</span>\n </button>\n `\n )}\n </div>\n\n ${this.showCallButton\n ? html`\n <button\n class=\"call-button\"\n part=\"call-button\"\n @click=${this.handleCall}\n ?disabled=${this.digits.length === 0}\n aria-label=\"Call ${this.digits}\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M6.62 10.79c1.44 2.83 3.76 5.15 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z\"\n />\n </svg>\n Call\n </button>\n `\n : null}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-dialpad': DialpadComponent;\n }\n}\n"],"names":["KEYS","DialpadComponent","LitElement","digit","_a","err","input","key","html","css","__decorateClass","property","consume","callContext","state","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"dialpad.js","sources":["../../src/components/dialpad.ts"],"sourcesContent":["/**\n * Dialpad Component\n *\n * A 12-key telephone keypad (0-9, *, #) for entering phone numbers\n * and sending DTMF tones during active calls.\n *\n * @example\n * ```html\n * <sw-dialpad .call=${call}></sw-dialpad>\n * ```\n *\n * @fires sw-digit-press - Fired when a digit button is pressed. Detail: `{ digit: string }`\n * @fires sw-backspace - Fired when the backspace button is pressed.\n * @fires sw-dial - Fired when the call button is pressed. Detail: `{ number: string }`\n *\n * @cssprop [--sw-color-primary=#044cf6] - Primary accent color.\n * @cssprop [--sw-color-primary-hover=#0339c4] - Primary color on hover.\n * @cssprop [--sw-color-success=#10b981] - Success/call button color.\n * @cssprop [--sw-color-danger=#ef4444] - Danger/hangup button color.\n * @cssprop [--sw-color-background=#1a1a1a] - Component background.\n * @cssprop [--sw-color-surface=#2a2a2a] - Button surface color.\n * @cssprop [--sw-color-surface-hover=#3a3a3a] - Button surface on hover.\n * @cssprop [--sw-color-text=#ffffff] - Text color.\n * @cssprop [--sw-color-text-muted=#a0a0a0] - Muted text color.\n * @cssprop [--sw-color-border=#404040] - Border color.\n * @cssprop [--sw-border-radius=8px] - Border radius.\n * @cssprop [--sw-font-size-sm=12px] - Small font size.\n * @cssprop [--sw-font-size-base=14px] - Base font size.\n * @cssprop [--sw-font-size-lg=16px] - Large font size.\n * @cssprop [--sw-font-size-xl=20px] - Extra-large font size for digit display.\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { consume } from '@lit/context';\nimport type { Observable } from 'rxjs';\nimport { callContext } from '../context/index.js';\n\n/**\n * Call interface for dialpad component\n */\nexport interface DialpadCall {\n sendDigits?(dtmf: string): Promise<void>;\n status$?: Observable<string>;\n}\n\n/**\n * Key layout for standard telephone keypad\n */\nconst KEYS = [\n { digit: '1', letters: '' },\n { digit: '2', letters: 'ABC' },\n { digit: '3', letters: 'DEF' },\n { digit: '4', letters: 'GHI' },\n { digit: '5', letters: 'JKL' },\n { digit: '6', letters: 'MNO' },\n { digit: '7', letters: 'PQRS' },\n { digit: '8', letters: 'TUV' },\n { digit: '9', letters: 'WXYZ' },\n { digit: '*', letters: '' },\n { digit: '0', letters: '+' },\n { digit: '#', letters: '' }\n];\n\n@customElement('sw-dialpad')\nexport class DialpadComponent extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-primary-hover: #0339c4;\n --sw-color-primary-active: #022b92;\n --sw-color-success: #10b981;\n --sw-color-danger: #ef4444;\n --sw-color-text: #1f2937;\n --sw-color-text-muted: #6b7280;\n --sw-color-border: #e5e7eb;\n --sw-color-background: #ffffff;\n --sw-color-background-hover: #f3f4f6;\n --sw-color-background-active: #e5e7eb;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-font-size-sm: 12px;\n --sw-font-size-base: 16px;\n --sw-font-size-lg: 24px;\n --sw-font-size-xl: 32px;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-space-4: 16px;\n --sw-border-radius: 8px;\n --sw-border-radius-full: 9999px;\n\n display: block;\n font-family: var(--sw-font-family);\n }\n\n /* Dark mode support */\n :host([data-theme='dark']) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-border: #374151;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([data-theme='light'])) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-border: #374151;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n }\n }\n\n .container {\n display: flex;\n flex-direction: column;\n gap: var(--sw-space-3);\n padding: var(--sw-space-4);\n max-width: 280px;\n background: var(--sw-color-background);\n border-radius: var(--sw-border-radius);\n }\n\n .display {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--sw-space-3) var(--sw-space-4);\n background: var(--sw-color-background-hover);\n border-radius: var(--sw-border-radius);\n min-height: 48px;\n }\n\n .display-input {\n flex: 1;\n font-size: var(--sw-font-size-xl);\n font-weight: 500;\n font-family: var(--sw-font-family);\n color: var(--sw-color-text);\n background: transparent;\n border: none;\n outline: none;\n letter-spacing: 2px;\n text-align: center;\n }\n\n .display-input::placeholder {\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-base);\n letter-spacing: normal;\n }\n\n .backspace-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n background: transparent;\n border: none;\n border-radius: var(--sw-border-radius);\n cursor: pointer;\n color: var(--sw-color-text-muted);\n transition:\n background-color 0.15s ease,\n color 0.15s ease;\n }\n\n .backspace-button:hover {\n background: var(--sw-color-background-active);\n color: var(--sw-color-text);\n }\n\n .backspace-button:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n }\n\n .keypad {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--sw-space-2);\n }\n\n .key {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n width: 72px;\n height: 72px;\n background: var(--sw-color-background);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-border-radius-full);\n cursor: pointer;\n transition:\n background-color 0.1s ease,\n transform 0.1s ease;\n user-select: none;\n -webkit-user-select: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n .key:hover {\n background: var(--sw-color-background-hover);\n }\n\n .key:active,\n .key.pressed {\n background: var(--sw-color-background-active);\n transform: scale(0.95);\n }\n\n .key-digit {\n font-size: var(--sw-font-size-lg);\n font-weight: 500;\n color: var(--sw-color-text);\n line-height: 1;\n }\n\n .key-letters {\n font-size: var(--sw-font-size-sm);\n color: var(--sw-color-text-muted);\n text-transform: uppercase;\n letter-spacing: 1px;\n margin-top: 2px;\n min-height: 14px;\n }\n\n .call-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 56px;\n background: var(--sw-color-success);\n border: none;\n border-radius: var(--sw-border-radius);\n cursor: pointer;\n color: white;\n font-size: var(--sw-font-size-base);\n font-weight: 600;\n font-family: var(--sw-font-family);\n transition: background-color 0.15s ease;\n gap: var(--sw-space-2);\n }\n\n .call-button:hover {\n background: #0ea472;\n }\n\n .call-button:active {\n background: #0d9668;\n }\n\n .call-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .call-button svg {\n width: 20px;\n height: 20px;\n }\n `;\n\n /** Call object providing the sendDigits method for DTMF tones. */\n @property({ attribute: false })\n call: DialpadCall | null = null;\n\n /**\n * Call from context (if nested in sw-call-media)\n */\n @consume({ context: callContext, subscribe: true })\n @state()\n private contextCall?: DialpadCall;\n\n /**\n * Current digits entered\n */\n @state()\n private digits: string = '';\n\n /**\n * Currently pressed key (for visual feedback)\n */\n @state()\n private pressedKey: string | null = null;\n\n /** Whether to display the call button below the keypad. */\n @property({ type: Boolean, attribute: 'show-call-button' })\n showCallButton: boolean = false;\n\n /** Placeholder text shown in the digit display input. */\n @property({ type: String })\n placeholder: string = 'Enter number';\n\n private get activeCall(): DialpadCall | null {\n return this.call || this.contextCall || null;\n }\n\n private handleKeyPress(digit: string) {\n this.digits += digit;\n this.pressedKey = digit;\n\n // Send DTMF if call is active\n if (this.activeCall?.sendDigits) {\n this.activeCall.sendDigits(digit).catch((err) => {\n console.error('Failed to send DTMF:', err);\n });\n }\n\n // Dispatch event\n this.dispatchEvent(\n new CustomEvent('sw-digit-press', {\n detail: { digit, digits: this.digits },\n bubbles: true,\n composed: true\n })\n );\n\n // Reset pressed state after animation\n setTimeout(() => {\n this.pressedKey = null;\n }, 100);\n }\n\n private handleBackspace() {\n if (this.digits.length > 0) {\n this.digits = this.digits.slice(0, -1);\n this.dispatchEvent(\n new CustomEvent('sw-backspace', {\n detail: { digits: this.digits },\n bubbles: true,\n composed: true\n })\n );\n }\n }\n\n private handleCall() {\n if (this.digits.length > 0) {\n this.dispatchEvent(\n new CustomEvent('sw-dial', {\n detail: { digits: this.digits },\n bubbles: true,\n composed: true\n })\n );\n }\n }\n\n private handleInputChange(e: Event) {\n const input = e.target as HTMLInputElement;\n // Only allow valid DTMF characters\n this.digits = input.value.replace(/[^0-9*#]/g, '');\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n const key = e.key;\n // Handle keyboard input for digits\n if (/^[0-9*#]$/.test(key)) {\n this.handleKeyPress(key);\n } else if (key === 'Backspace') {\n this.handleBackspace();\n } else if (key === 'Enter' && this.showCallButton) {\n this.handleCall();\n }\n }\n\n render() {\n return html`\n <div class=\"container\" part=\"container\">\n <div class=\"display\" part=\"display\">\n <input\n type=\"tel\"\n class=\"display-input\"\n .value=${this.digits}\n placeholder=${this.placeholder}\n @input=${this.handleInputChange}\n @keydown=${this.handleKeyDown}\n aria-label=\"Phone number input\"\n />\n <button\n class=\"backspace-button\"\n @click=${this.handleBackspace}\n ?disabled=${this.digits.length === 0}\n aria-label=\"Delete last digit\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M21 4H8l-7 8 7 8h13a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z\"></path>\n <line x1=\"18\" y1=\"9\" x2=\"12\" y2=\"15\"></line>\n <line x1=\"12\" y1=\"9\" x2=\"18\" y2=\"15\"></line>\n </svg>\n </button>\n </div>\n\n <div class=\"keypad\" part=\"keypad\" role=\"group\" aria-label=\"Telephone keypad\">\n ${KEYS.map(\n (key) => html`\n <button\n class=\"key ${this.pressedKey === key.digit ? 'pressed' : ''}\"\n part=\"key ${this.pressedKey === key.digit ? 'key-pressed' : ''}\"\n @click=${() => this.handleKeyPress(key.digit)}\n aria-label=\"${key.digit}${key.letters ? `, ${key.letters}` : ''}\"\n >\n <span class=\"key-digit\">${key.digit}</span>\n <span class=\"key-letters\">${key.letters}</span>\n </button>\n `\n )}\n </div>\n\n ${this.showCallButton\n ? html`\n <button\n class=\"call-button\"\n part=\"call-button\"\n @click=${this.handleCall}\n ?disabled=${this.digits.length === 0}\n aria-label=\"Call ${this.digits}\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M6.62 10.79c1.44 2.83 3.76 5.15 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z\"\n />\n </svg>\n Call\n </button>\n `\n : null}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-dialpad': DialpadComponent;\n }\n}\n"],"names":["KEYS","DialpadComponent","LitElement","digit","_a","err","input","key","html","css","__decorateClass","property","consume","callContext","state","customElement"],"mappings":"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"}
@@ -8,6 +8,28 @@
8
8
  * ```html
9
9
  * <sw-directory .directory=${directory}></sw-directory>
10
10
  * ```
11
+ *
12
+ * @fires sw-address-select - Fired when an address is selected. Detail: `{ address: Address }`
13
+ * @fires sw-dial - Fired when the call button on an address is clicked. Detail: `{ address: Address, channel: string }`
14
+ *
15
+ * @cssprop [--sw-color-primary=#044cf6] - Primary brand color
16
+ * @cssprop [--sw-color-primary-hover=#0339c4] - Primary color on hover
17
+ * @cssprop [--sw-color-success=#10b981] - Success/positive color
18
+ * @cssprop [--sw-color-text=#1f2937] - Primary text color
19
+ * @cssprop [--sw-color-text-muted=#6b7280] - Secondary/muted text color
20
+ * @cssprop [--sw-color-background=#ffffff] - Component background color
21
+ * @cssprop [--sw-color-background-hover=#f3f4f6] - Background color on hover
22
+ * @cssprop [--sw-color-background-active=#e5e7eb] - Background color on active/press
23
+ * @cssprop [--sw-color-border=#e5e7eb] - Border color
24
+ * @cssprop [--sw-font-family=-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif] - Font family
25
+ * @cssprop [--sw-font-size-sm=12px] - Small font size
26
+ * @cssprop [--sw-font-size-base=14px] - Base font size
27
+ * @cssprop [--sw-font-size-lg=16px] - Large font size
28
+ * @cssprop [--sw-space-1=4px] - Smallest spacing unit
29
+ * @cssprop [--sw-space-2=8px] - Small spacing unit
30
+ * @cssprop [--sw-space-3=12px] - Medium spacing unit
31
+ * @cssprop [--sw-space-4=16px] - Large spacing unit
32
+ * @cssprop [--sw-border-radius=8px] - Border radius for containers and inputs
11
33
  */
12
34
  import { LitElement } from 'lit';
13
35
  import type { Observable } from 'rxjs';
@@ -1 +1 @@
1
- {"version":3,"file":"directory.d.ts","sourceRoot":"","sources":["../../src/components/directory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC/B,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,qBACa,kBAAmB,SAAQ,UAAU;IAChD,MAAM,CAAC,MAAM,0BAuPX;IAEF;;OAEG;IAEH,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IAE1C;;OAEG;IAEH,OAAO,CAAC,eAAe,CAAwB;IAE/C;;OAEG;IAEH,OAAO,CAAC,WAAW,CAAc;IAEjC;;OAEG;IAEH,OAAO,CAAC,SAAS,CAAiB;IAElC;;OAEG;IAEH,OAAO,CAAC,OAAO,CAAkB;IAEjC;;OAEG;IAEH,OAAO,CAAC,OAAO,CAAkB;IAEjC;;OAEG;IACH,OAAO,CAAC,aAAa,CAAsB;IAE3C;;OAEG;IACH,OAAO,CAAC,mBAAmB,CAAuB;IAElD;;OAEG;IACH,OAAO,CAAC,oBAAoB,CAAqC;IAEjE;;OAEG;IAEH,OAAO,CAAC,sBAAsB,CAAkB;IAEhD,iBAAiB;IAKjB,oBAAoB;IAKpB,OAAO,CAAC,oBAAoB;IAiC5B,OAAO,CAAC,OAAO;IAYf,SAAS,CAAC,YAAY;IAItB,OAAO,CAAC,mBAAmB;IAqB3B,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;YAyB3C,sBAAsB;IAiBpC,OAAO,KAAK,iBAAiB,GAU5B;IAED,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,UAAU;YAUJ,cAAc;IAM5B,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,sBAAsB;IAS9B,MAAM;CA+EP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,cAAc,EAAE,kBAAkB,CAAC;KACpC;CACF"}
1
+ {"version":3,"file":"directory.d.ts","sourceRoot":"","sources":["../../src/components/directory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzB,QAAQ,CAAC,EAAE;QACT,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC/B,QAAQ,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,qBACa,kBAAmB,SAAQ,UAAU;IAChD,MAAM,CAAC,MAAM,0BAuPX;IAEF;;OAEG;IAEH,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IAE1C;;OAEG;IAEH,OAAO,CAAC,eAAe,CAAwB;IAE/C;;OAEG;IAEH,OAAO,CAAC,WAAW,CAAc;IAEjC;;OAEG;IAEH,OAAO,CAAC,SAAS,CAAiB;IAElC;;OAEG;IAEH,OAAO,CAAC,OAAO,CAAkB;IAEjC;;OAEG;IAEH,OAAO,CAAC,OAAO,CAAkB;IAEjC;;OAEG;IACH,OAAO,CAAC,aAAa,CAAsB;IAE3C;;OAEG;IACH,OAAO,CAAC,mBAAmB,CAAuB;IAElD;;OAEG;IACH,OAAO,CAAC,oBAAoB,CAAqC;IAEjE;;OAEG;IAEH,OAAO,CAAC,sBAAsB,CAAkB;IAEhD,iBAAiB;IAKjB,oBAAoB;IAKpB,OAAO,CAAC,oBAAoB;IAiC5B,OAAO,CAAC,OAAO;IAYf,SAAS,CAAC,YAAY;IAItB,OAAO,CAAC,mBAAmB;IAqB3B,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;YAyB3C,sBAAsB;IAiBpC,OAAO,KAAK,iBAAiB,GAU5B;IAED,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,UAAU;YAUJ,cAAc;IAM5B,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,sBAAsB;IAS9B,MAAM;CA+EP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,cAAc,EAAE,kBAAkB,CAAC;KACpC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"directory.js","sources":["../../src/components/directory.ts"],"sourcesContent":["/**\n * Directory Component\n *\n * A searchable list of addresses from the directory service.\n * Supports filtering, selection, and pagination.\n *\n * @example\n * ```html\n * <sw-directory .directory=${directory}></sw-directory>\n * ```\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { Subscription } from 'rxjs';\nimport type { Observable } from 'rxjs';\n\n/**\n * Address type from SDK\n */\nexport interface Address {\n id: string;\n name: string;\n displayName?: string;\n type?: 'room' | 'person';\n channels?: {\n audio?: boolean;\n video?: boolean;\n messaging?: boolean;\n };\n}\n\n/**\n * Directory interface for component\n */\nexport interface DirectoryService {\n addresses$: Observable<Address[]>;\n loading$?: Observable<boolean>;\n hasMore$?: Observable<boolean>;\n loadMore?(): Promise<void>;\n}\n\n@customElement('sw-directory')\nexport class DirectoryComponent extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-primary-hover: #0339c4;\n --sw-color-success: #10b981;\n --sw-color-text: #1f2937;\n --sw-color-text-muted: #6b7280;\n --sw-color-background: #ffffff;\n --sw-color-background-hover: #f3f4f6;\n --sw-color-background-active: #e5e7eb;\n --sw-color-border: #e5e7eb;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-font-size-sm: 12px;\n --sw-font-size-base: 14px;\n --sw-font-size-lg: 16px;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-space-4: 16px;\n --sw-border-radius: 8px;\n\n display: block;\n font-family: var(--sw-font-family);\n }\n\n /* Dark mode support */\n :host([data-theme='dark']) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n --sw-color-border: #374151;\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([data-theme='light'])) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n --sw-color-border: #374151;\n }\n }\n\n .container {\n display: flex;\n flex-direction: column;\n background: var(--sw-color-background);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-border-radius);\n overflow: hidden;\n }\n\n .search {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n padding: var(--sw-space-3);\n border-bottom: 1px solid var(--sw-color-border);\n }\n\n .search-input {\n flex: 1;\n padding: var(--sw-space-2) var(--sw-space-3);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-border-radius);\n background: var(--sw-color-background);\n color: var(--sw-color-text);\n font-family: var(--sw-font-family);\n font-size: var(--sw-font-size-base);\n outline: none;\n transition: border-color 0.15s ease;\n }\n\n .search-input:focus {\n border-color: var(--sw-color-primary);\n }\n\n .search-input::placeholder {\n color: var(--sw-color-text-muted);\n }\n\n .list {\n display: flex;\n flex-direction: column;\n max-height: 400px;\n overflow-y: auto;\n }\n\n .item {\n display: flex;\n align-items: center;\n gap: var(--sw-space-3);\n padding: var(--sw-space-3) var(--sw-space-4);\n border-bottom: 1px solid var(--sw-color-border);\n cursor: pointer;\n transition: background-color 0.15s ease;\n }\n\n .item:last-child {\n border-bottom: none;\n }\n\n .item:hover {\n background: var(--sw-color-background-hover);\n }\n\n .item:active {\n background: var(--sw-color-background-active);\n }\n\n .item.selected {\n background: var(--sw-color-primary);\n color: white;\n }\n\n .item.selected .item-type,\n .item.selected .item-channels {\n color: rgba(255, 255, 255, 0.8);\n }\n\n .item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: var(--sw-color-background-hover);\n color: var(--sw-color-text-muted);\n flex-shrink: 0;\n }\n\n .item.selected .item-icon {\n background: rgba(255, 255, 255, 0.2);\n color: white;\n }\n\n .item-icon svg {\n width: 20px;\n height: 20px;\n }\n\n .item-content {\n flex: 1;\n min-width: 0;\n }\n\n .item-name {\n font-size: var(--sw-font-size-base);\n font-weight: 500;\n color: inherit;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .item-type {\n font-size: var(--sw-font-size-sm);\n color: var(--sw-color-text-muted);\n text-transform: capitalize;\n }\n\n .item-channels {\n display: flex;\n gap: var(--sw-space-1);\n color: var(--sw-color-text-muted);\n }\n\n .item-channels svg {\n width: 16px;\n height: 16px;\n }\n\n .item-channels .active {\n color: var(--sw-color-success);\n }\n\n .loading,\n .empty {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--sw-space-4);\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-base);\n }\n\n .scroll-sentinel {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--sw-space-3);\n min-height: 1px;\n }\n\n .scroll-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--sw-space-2);\n padding: var(--sw-space-3);\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-sm);\n }\n\n .spinner {\n width: 16px;\n height: 16px;\n border: 2px solid var(--sw-color-border);\n border-top-color: var(--sw-color-primary);\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n .dial-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: var(--sw-color-success);\n border: none;\n border-radius: 50%;\n color: white;\n cursor: pointer;\n flex-shrink: 0;\n transition: background-color 0.15s ease;\n }\n\n .dial-button:hover {\n background: #0ea472;\n }\n\n .dial-button svg {\n width: 18px;\n height: 18px;\n }\n `;\n\n /**\n * Directory service with addresses$ observable\n */\n @property({ type: Object })\n directory: DirectoryService | null = null;\n\n /**\n * Currently selected address\n */\n @state()\n private selectedAddress: Address | null = null;\n\n /**\n * Search filter query\n */\n @state()\n private searchQuery: string = '';\n\n /**\n * List of addresses from directory\n */\n @state()\n private addresses: Address[] = [];\n\n /**\n * Loading state\n */\n @state()\n private loading: boolean = false;\n\n /**\n * Has more addresses to load\n */\n @state()\n private hasMore: boolean = false;\n\n /**\n * RxJS subscriptions for cleanup\n */\n private subscriptions: Subscription[] = [];\n\n /**\n * Debounce timer for search\n */\n private searchDebounceTimer: number | null = null;\n\n /**\n * IntersectionObserver for infinite scroll\n */\n private intersectionObserver: IntersectionObserver | null = null;\n\n /**\n * Flag to track if we're auto-loading for search\n */\n @state()\n private isAutoLoadingForSearch: boolean = false;\n\n connectedCallback() {\n super.connectedCallback();\n this.subscribeToDirectory();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.cleanup();\n }\n\n private subscribeToDirectory() {\n if (!this.directory) return;\n\n // Subscribe to addresses\n if (this.directory.addresses$) {\n const addressesSub = this.directory.addresses$.subscribe((addresses) => {\n this.addresses = addresses;\n });\n this.subscriptions.push(addressesSub);\n }\n\n // Subscribe to loading state\n if (this.directory.loading$) {\n const loadingSub = this.directory.loading$.subscribe((loading) => {\n this.loading = loading;\n });\n this.subscriptions.push(loadingSub);\n }\n\n // Subscribe to hasMore\n if (this.directory.hasMore$) {\n const hasMoreSub = this.directory.hasMore$.subscribe((hasMore) => {\n this.hasMore = hasMore;\n });\n this.subscriptions.push(hasMoreSub);\n }\n\n // Load initial data\n if (this.directory.loadMore) {\n this.directory.loadMore();\n }\n }\n\n private cleanup() {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subscriptions = [];\n if (this.searchDebounceTimer) {\n clearTimeout(this.searchDebounceTimer);\n }\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n }\n\n protected firstUpdated() {\n this.setupInfiniteScroll();\n }\n\n private setupInfiniteScroll() {\n const sentinel = this.shadowRoot?.querySelector('.scroll-sentinel');\n if (!sentinel) return;\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n const entry = entries[0];\n if (entry?.isIntersecting && this.hasMore && !this.loading) {\n this.handleLoadMore();\n }\n },\n {\n root: this.shadowRoot?.querySelector('.list'),\n rootMargin: '100px',\n threshold: 0\n }\n );\n\n this.intersectionObserver.observe(sentinel);\n }\n\n protected updated(changedProperties: Map<string, unknown>) {\n super.updated(changedProperties);\n\n // Handle directory property changes\n if (changedProperties.has('directory')) {\n this.cleanup();\n this.subscribeToDirectory();\n }\n\n // Re-setup infinite scroll if needed\n if (!this.intersectionObserver) {\n this.setupInfiniteScroll();\n }\n\n // Auto-load more when searching and no results found but more available\n // Also check when loading state changes so we can trigger subsequent loads\n if (\n changedProperties.has('addresses') ||\n changedProperties.has('searchQuery') ||\n changedProperties.has('loading')\n ) {\n this.checkAutoLoadForSearch();\n }\n }\n\n private async checkAutoLoadForSearch() {\n // If we have a search query, no filtered results, but more to load, keep loading\n // Note: !this.loading prevents concurrent loads, isAutoLoadingForSearch is for UI state only\n if (\n this.searchQuery.trim() &&\n this.filteredAddresses.length === 0 &&\n this.hasMore &&\n !this.loading\n ) {\n this.isAutoLoadingForSearch = true;\n await this.handleLoadMore();\n // The updated() will be called again when addresses/loading change, continuing the loop\n } else if (this.filteredAddresses.length > 0 || !this.hasMore) {\n this.isAutoLoadingForSearch = false;\n }\n }\n\n private get filteredAddresses(): Address[] {\n if (!this.searchQuery.trim()) {\n return this.addresses;\n }\n const query = this.searchQuery.toLowerCase();\n return this.addresses.filter(\n (addr) =>\n addr.name.toLowerCase().includes(query) ||\n (addr.displayName && addr.displayName.toLowerCase().includes(query))\n );\n }\n\n private handleSearchInput(e: Event) {\n const input = e.target as HTMLInputElement;\n\n // Debounce search\n if (this.searchDebounceTimer) {\n clearTimeout(this.searchDebounceTimer);\n }\n\n this.searchDebounceTimer = window.setTimeout(() => {\n this.searchQuery = input.value;\n }, 200);\n }\n\n private handleItemClick(address: Address) {\n this.selectedAddress = address;\n\n this.dispatchEvent(\n new CustomEvent('sw-address-select', {\n detail: { address },\n bubbles: true,\n composed: true\n })\n );\n }\n\n private handleItemDoubleClick(address: Address) {\n this.handleDial(address);\n }\n\n private handleDial(address: Address) {\n this.dispatchEvent(\n new CustomEvent('sw-dial', {\n detail: { address },\n bubbles: true,\n composed: true\n })\n );\n }\n\n private async handleLoadMore() {\n if (this.directory?.loadMore) {\n await this.directory.loadMore();\n }\n }\n\n private renderAddressIcon(address: Address) {\n if (address.type === 'room') {\n return html`\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 3c1.93 0 3.5 1.57 3.5 3.5S13.93 13 12 13s-3.5-1.57-3.5-3.5S10.07 6 12 6zm7 13H5v-.23c0-.62.28-1.2.76-1.58C7.47 15.82 9.64 15 12 15s4.53.82 6.24 2.19c.48.38.76.97.76 1.58V19z\"\n />\n </svg>\n `;\n }\n return html`\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\"\n />\n </svg>\n `;\n }\n\n private renderChannelIcons(address: Address) {\n const channels = address.channels || {};\n return html`\n <div class=\"item-channels\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n class=\"${channels.audio ? 'active' : ''}\"\n title=\"Audio\"\n >\n <path\n d=\"M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z\"\n />\n </svg>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n class=\"${channels.video ? 'active' : ''}\"\n title=\"Video\"\n >\n <path\n d=\"M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z\"\n />\n </svg>\n </div>\n `;\n }\n\n private renderLoadingIndicator() {\n return html`\n <div class=\"scroll-loading\">\n <div class=\"spinner\"></div>\n <span>${this.isAutoLoadingForSearch ? 'Searching...' : 'Loading more...'}</span>\n </div>\n `;\n }\n\n render() {\n const showInitialLoading = this.loading && this.addresses.length === 0;\n const showEmptyState = !this.loading && this.filteredAddresses.length === 0 && !this.hasMore;\n const showSearchingState = this.isAutoLoadingForSearch && this.filteredAddresses.length === 0;\n\n return html`\n <div class=\"container\" part=\"container\">\n <div class=\"search\" part=\"search\">\n <input\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search addresses...\"\n @input=${this.handleSearchInput}\n aria-label=\"Search addresses\"\n />\n </div>\n\n <div class=\"list\" part=\"list\" role=\"listbox\">\n ${showInitialLoading\n ? html`<div class=\"loading\">Loading...</div>`\n : showSearchingState\n ? this.renderLoadingIndicator()\n : showEmptyState\n ? html`<div class=\"empty\">No addresses found</div>`\n : html`\n ${this.filteredAddresses.map(\n (address) => html`\n <div\n class=\"item ${this.selectedAddress?.id === address.id ? 'selected' : ''}\"\n part=\"item ${this.selectedAddress?.id === address.id\n ? 'item-selected'\n : ''}\"\n role=\"option\"\n aria-selected=\"${this.selectedAddress?.id === address.id}\"\n @click=${() => this.handleItemClick(address)}\n @dblclick=${() => this.handleItemDoubleClick(address)}\n >\n <div class=\"item-icon\">${this.renderAddressIcon(address)}</div>\n <div class=\"item-content\">\n <div class=\"item-name\" part=\"item-name\">\n ${address.displayName || address.name}\n </div>\n <div class=\"item-type\" part=\"item-type\">\n ${address.type || 'address'}\n </div>\n </div>\n ${this.renderChannelIcons(address)}\n <button\n class=\"dial-button\"\n @click=${(e: Event) => {\n e.stopPropagation();\n this.handleDial(address);\n }}\n aria-label=\"Call ${address.displayName || address.name}\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M6.62 10.79c1.44 2.83 3.76 5.15 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z\"\n />\n </svg>\n </button>\n </div>\n `\n )}\n <!-- Scroll sentinel for infinite scroll -->\n <div class=\"scroll-sentinel\">\n ${this.loading && !this.isAutoLoadingForSearch\n ? this.renderLoadingIndicator()\n : null}\n </div>\n `}\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-directory': DirectoryComponent;\n }\n}\n"],"names":["DirectoryComponent","LitElement","addressesSub","addresses","loadingSub","loading","hasMoreSub","hasMore","sub","sentinel","_a","entries","entry","_b","changedProperties","query","addr","input","address","html","channels","showInitialLoading","showEmptyState","showSearchingState","_c","e","css","__decorateClass","property","state","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"directory.js","sources":["../../src/components/directory.ts"],"sourcesContent":["/**\n * Directory Component\n *\n * A searchable list of addresses from the directory service.\n * Supports filtering, selection, and pagination.\n *\n * @example\n * ```html\n * <sw-directory .directory=${directory}></sw-directory>\n * ```\n *\n * @fires sw-address-select - Fired when an address is selected. Detail: `{ address: Address }`\n * @fires sw-dial - Fired when the call button on an address is clicked. Detail: `{ address: Address, channel: string }`\n *\n * @cssprop [--sw-color-primary=#044cf6] - Primary brand color\n * @cssprop [--sw-color-primary-hover=#0339c4] - Primary color on hover\n * @cssprop [--sw-color-success=#10b981] - Success/positive color\n * @cssprop [--sw-color-text=#1f2937] - Primary text color\n * @cssprop [--sw-color-text-muted=#6b7280] - Secondary/muted text color\n * @cssprop [--sw-color-background=#ffffff] - Component background color\n * @cssprop [--sw-color-background-hover=#f3f4f6] - Background color on hover\n * @cssprop [--sw-color-background-active=#e5e7eb] - Background color on active/press\n * @cssprop [--sw-color-border=#e5e7eb] - Border color\n * @cssprop [--sw-font-family=-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif] - Font family\n * @cssprop [--sw-font-size-sm=12px] - Small font size\n * @cssprop [--sw-font-size-base=14px] - Base font size\n * @cssprop [--sw-font-size-lg=16px] - Large font size\n * @cssprop [--sw-space-1=4px] - Smallest spacing unit\n * @cssprop [--sw-space-2=8px] - Small spacing unit\n * @cssprop [--sw-space-3=12px] - Medium spacing unit\n * @cssprop [--sw-space-4=16px] - Large spacing unit\n * @cssprop [--sw-border-radius=8px] - Border radius for containers and inputs\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { Subscription } from 'rxjs';\nimport type { Observable } from 'rxjs';\n\n/**\n * Address type from SDK\n */\nexport interface Address {\n id: string;\n name: string;\n displayName?: string;\n type?: 'room' | 'person';\n channels?: {\n audio?: boolean;\n video?: boolean;\n messaging?: boolean;\n };\n}\n\n/**\n * Directory interface for component\n */\nexport interface DirectoryService {\n addresses$: Observable<Address[]>;\n loading$?: Observable<boolean>;\n hasMore$?: Observable<boolean>;\n loadMore?(): Promise<void>;\n}\n\n@customElement('sw-directory')\nexport class DirectoryComponent extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-primary-hover: #0339c4;\n --sw-color-success: #10b981;\n --sw-color-text: #1f2937;\n --sw-color-text-muted: #6b7280;\n --sw-color-background: #ffffff;\n --sw-color-background-hover: #f3f4f6;\n --sw-color-background-active: #e5e7eb;\n --sw-color-border: #e5e7eb;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-font-size-sm: 12px;\n --sw-font-size-base: 14px;\n --sw-font-size-lg: 16px;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-space-4: 16px;\n --sw-border-radius: 8px;\n\n display: block;\n font-family: var(--sw-font-family);\n }\n\n /* Dark mode support */\n :host([data-theme='dark']) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n --sw-color-border: #374151;\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([data-theme='light'])) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n --sw-color-border: #374151;\n }\n }\n\n .container {\n display: flex;\n flex-direction: column;\n background: var(--sw-color-background);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-border-radius);\n overflow: hidden;\n }\n\n .search {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n padding: var(--sw-space-3);\n border-bottom: 1px solid var(--sw-color-border);\n }\n\n .search-input {\n flex: 1;\n padding: var(--sw-space-2) var(--sw-space-3);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-border-radius);\n background: var(--sw-color-background);\n color: var(--sw-color-text);\n font-family: var(--sw-font-family);\n font-size: var(--sw-font-size-base);\n outline: none;\n transition: border-color 0.15s ease;\n }\n\n .search-input:focus {\n border-color: var(--sw-color-primary);\n }\n\n .search-input::placeholder {\n color: var(--sw-color-text-muted);\n }\n\n .list {\n display: flex;\n flex-direction: column;\n max-height: 400px;\n overflow-y: auto;\n }\n\n .item {\n display: flex;\n align-items: center;\n gap: var(--sw-space-3);\n padding: var(--sw-space-3) var(--sw-space-4);\n border-bottom: 1px solid var(--sw-color-border);\n cursor: pointer;\n transition: background-color 0.15s ease;\n }\n\n .item:last-child {\n border-bottom: none;\n }\n\n .item:hover {\n background: var(--sw-color-background-hover);\n }\n\n .item:active {\n background: var(--sw-color-background-active);\n }\n\n .item.selected {\n background: var(--sw-color-primary);\n color: white;\n }\n\n .item.selected .item-type,\n .item.selected .item-channels {\n color: rgba(255, 255, 255, 0.8);\n }\n\n .item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: var(--sw-color-background-hover);\n color: var(--sw-color-text-muted);\n flex-shrink: 0;\n }\n\n .item.selected .item-icon {\n background: rgba(255, 255, 255, 0.2);\n color: white;\n }\n\n .item-icon svg {\n width: 20px;\n height: 20px;\n }\n\n .item-content {\n flex: 1;\n min-width: 0;\n }\n\n .item-name {\n font-size: var(--sw-font-size-base);\n font-weight: 500;\n color: inherit;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .item-type {\n font-size: var(--sw-font-size-sm);\n color: var(--sw-color-text-muted);\n text-transform: capitalize;\n }\n\n .item-channels {\n display: flex;\n gap: var(--sw-space-1);\n color: var(--sw-color-text-muted);\n }\n\n .item-channels svg {\n width: 16px;\n height: 16px;\n }\n\n .item-channels .active {\n color: var(--sw-color-success);\n }\n\n .loading,\n .empty {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--sw-space-4);\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-base);\n }\n\n .scroll-sentinel {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--sw-space-3);\n min-height: 1px;\n }\n\n .scroll-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--sw-space-2);\n padding: var(--sw-space-3);\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-sm);\n }\n\n .spinner {\n width: 16px;\n height: 16px;\n border: 2px solid var(--sw-color-border);\n border-top-color: var(--sw-color-primary);\n border-radius: 50%;\n animation: spin 0.8s linear infinite;\n }\n\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n .dial-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: var(--sw-color-success);\n border: none;\n border-radius: 50%;\n color: white;\n cursor: pointer;\n flex-shrink: 0;\n transition: background-color 0.15s ease;\n }\n\n .dial-button:hover {\n background: #0ea472;\n }\n\n .dial-button svg {\n width: 18px;\n height: 18px;\n }\n `;\n\n /**\n * Directory service with addresses$ observable\n */\n @property({ type: Object })\n directory: DirectoryService | null = null;\n\n /**\n * Currently selected address\n */\n @state()\n private selectedAddress: Address | null = null;\n\n /**\n * Search filter query\n */\n @state()\n private searchQuery: string = '';\n\n /**\n * List of addresses from directory\n */\n @state()\n private addresses: Address[] = [];\n\n /**\n * Loading state\n */\n @state()\n private loading: boolean = false;\n\n /**\n * Has more addresses to load\n */\n @state()\n private hasMore: boolean = false;\n\n /**\n * RxJS subscriptions for cleanup\n */\n private subscriptions: Subscription[] = [];\n\n /**\n * Debounce timer for search\n */\n private searchDebounceTimer: number | null = null;\n\n /**\n * IntersectionObserver for infinite scroll\n */\n private intersectionObserver: IntersectionObserver | null = null;\n\n /**\n * Flag to track if we're auto-loading for search\n */\n @state()\n private isAutoLoadingForSearch: boolean = false;\n\n connectedCallback() {\n super.connectedCallback();\n this.subscribeToDirectory();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.cleanup();\n }\n\n private subscribeToDirectory() {\n if (!this.directory) return;\n\n // Subscribe to addresses\n if (this.directory.addresses$) {\n const addressesSub = this.directory.addresses$.subscribe((addresses) => {\n this.addresses = addresses;\n });\n this.subscriptions.push(addressesSub);\n }\n\n // Subscribe to loading state\n if (this.directory.loading$) {\n const loadingSub = this.directory.loading$.subscribe((loading) => {\n this.loading = loading;\n });\n this.subscriptions.push(loadingSub);\n }\n\n // Subscribe to hasMore\n if (this.directory.hasMore$) {\n const hasMoreSub = this.directory.hasMore$.subscribe((hasMore) => {\n this.hasMore = hasMore;\n });\n this.subscriptions.push(hasMoreSub);\n }\n\n // Load initial data\n if (this.directory.loadMore) {\n this.directory.loadMore();\n }\n }\n\n private cleanup() {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subscriptions = [];\n if (this.searchDebounceTimer) {\n clearTimeout(this.searchDebounceTimer);\n }\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.intersectionObserver = null;\n }\n }\n\n protected firstUpdated() {\n this.setupInfiniteScroll();\n }\n\n private setupInfiniteScroll() {\n const sentinel = this.shadowRoot?.querySelector('.scroll-sentinel');\n if (!sentinel) return;\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n const entry = entries[0];\n if (entry?.isIntersecting && this.hasMore && !this.loading) {\n this.handleLoadMore();\n }\n },\n {\n root: this.shadowRoot?.querySelector('.list'),\n rootMargin: '100px',\n threshold: 0\n }\n );\n\n this.intersectionObserver.observe(sentinel);\n }\n\n protected updated(changedProperties: Map<string, unknown>) {\n super.updated(changedProperties);\n\n // Handle directory property changes\n if (changedProperties.has('directory')) {\n this.cleanup();\n this.subscribeToDirectory();\n }\n\n // Re-setup infinite scroll if needed\n if (!this.intersectionObserver) {\n this.setupInfiniteScroll();\n }\n\n // Auto-load more when searching and no results found but more available\n // Also check when loading state changes so we can trigger subsequent loads\n if (\n changedProperties.has('addresses') ||\n changedProperties.has('searchQuery') ||\n changedProperties.has('loading')\n ) {\n this.checkAutoLoadForSearch();\n }\n }\n\n private async checkAutoLoadForSearch() {\n // If we have a search query, no filtered results, but more to load, keep loading\n // Note: !this.loading prevents concurrent loads, isAutoLoadingForSearch is for UI state only\n if (\n this.searchQuery.trim() &&\n this.filteredAddresses.length === 0 &&\n this.hasMore &&\n !this.loading\n ) {\n this.isAutoLoadingForSearch = true;\n await this.handleLoadMore();\n // The updated() will be called again when addresses/loading change, continuing the loop\n } else if (this.filteredAddresses.length > 0 || !this.hasMore) {\n this.isAutoLoadingForSearch = false;\n }\n }\n\n private get filteredAddresses(): Address[] {\n if (!this.searchQuery.trim()) {\n return this.addresses;\n }\n const query = this.searchQuery.toLowerCase();\n return this.addresses.filter(\n (addr) =>\n addr.name.toLowerCase().includes(query) ||\n (addr.displayName && addr.displayName.toLowerCase().includes(query))\n );\n }\n\n private handleSearchInput(e: Event) {\n const input = e.target as HTMLInputElement;\n\n // Debounce search\n if (this.searchDebounceTimer) {\n clearTimeout(this.searchDebounceTimer);\n }\n\n this.searchDebounceTimer = window.setTimeout(() => {\n this.searchQuery = input.value;\n }, 200);\n }\n\n private handleItemClick(address: Address) {\n this.selectedAddress = address;\n\n this.dispatchEvent(\n new CustomEvent('sw-address-select', {\n detail: { address },\n bubbles: true,\n composed: true\n })\n );\n }\n\n private handleItemDoubleClick(address: Address) {\n this.handleDial(address);\n }\n\n private handleDial(address: Address) {\n this.dispatchEvent(\n new CustomEvent('sw-dial', {\n detail: { address },\n bubbles: true,\n composed: true\n })\n );\n }\n\n private async handleLoadMore() {\n if (this.directory?.loadMore) {\n await this.directory.loadMore();\n }\n }\n\n private renderAddressIcon(address: Address) {\n if (address.type === 'room') {\n return html`\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 3c1.93 0 3.5 1.57 3.5 3.5S13.93 13 12 13s-3.5-1.57-3.5-3.5S10.07 6 12 6zm7 13H5v-.23c0-.62.28-1.2.76-1.58C7.47 15.82 9.64 15 12 15s4.53.82 6.24 2.19c.48.38.76.97.76 1.58V19z\"\n />\n </svg>\n `;\n }\n return html`\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\"\n />\n </svg>\n `;\n }\n\n private renderChannelIcons(address: Address) {\n const channels = address.channels || {};\n return html`\n <div class=\"item-channels\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n class=\"${channels.audio ? 'active' : ''}\"\n title=\"Audio\"\n >\n <path\n d=\"M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z\"\n />\n </svg>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n class=\"${channels.video ? 'active' : ''}\"\n title=\"Video\"\n >\n <path\n d=\"M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z\"\n />\n </svg>\n </div>\n `;\n }\n\n private renderLoadingIndicator() {\n return html`\n <div class=\"scroll-loading\">\n <div class=\"spinner\"></div>\n <span>${this.isAutoLoadingForSearch ? 'Searching...' : 'Loading more...'}</span>\n </div>\n `;\n }\n\n render() {\n const showInitialLoading = this.loading && this.addresses.length === 0;\n const showEmptyState = !this.loading && this.filteredAddresses.length === 0 && !this.hasMore;\n const showSearchingState = this.isAutoLoadingForSearch && this.filteredAddresses.length === 0;\n\n return html`\n <div class=\"container\" part=\"container\">\n <div class=\"search\" part=\"search\">\n <input\n type=\"text\"\n class=\"search-input\"\n placeholder=\"Search addresses...\"\n @input=${this.handleSearchInput}\n aria-label=\"Search addresses\"\n />\n </div>\n\n <div class=\"list\" part=\"list\" role=\"listbox\">\n ${showInitialLoading\n ? html`<div class=\"loading\">Loading...</div>`\n : showSearchingState\n ? this.renderLoadingIndicator()\n : showEmptyState\n ? html`<div class=\"empty\">No addresses found</div>`\n : html`\n ${this.filteredAddresses.map(\n (address) => html`\n <div\n class=\"item ${this.selectedAddress?.id === address.id ? 'selected' : ''}\"\n part=\"item ${this.selectedAddress?.id === address.id\n ? 'item-selected'\n : ''}\"\n role=\"option\"\n aria-selected=\"${this.selectedAddress?.id === address.id}\"\n @click=${() => this.handleItemClick(address)}\n @dblclick=${() => this.handleItemDoubleClick(address)}\n >\n <div class=\"item-icon\">${this.renderAddressIcon(address)}</div>\n <div class=\"item-content\">\n <div class=\"item-name\" part=\"item-name\">\n ${address.displayName || address.name}\n </div>\n <div class=\"item-type\" part=\"item-type\">\n ${address.type || 'address'}\n </div>\n </div>\n ${this.renderChannelIcons(address)}\n <button\n class=\"dial-button\"\n @click=${(e: Event) => {\n e.stopPropagation();\n this.handleDial(address);\n }}\n aria-label=\"Call ${address.displayName || address.name}\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M6.62 10.79c1.44 2.83 3.76 5.15 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z\"\n />\n </svg>\n </button>\n </div>\n `\n )}\n <!-- Scroll sentinel for infinite scroll -->\n <div class=\"scroll-sentinel\">\n ${this.loading && !this.isAutoLoadingForSearch\n ? this.renderLoadingIndicator()\n : null}\n </div>\n `}\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-directory': DirectoryComponent;\n }\n}\n"],"names":["DirectoryComponent","LitElement","addressesSub","addresses","loadingSub","loading","hasMoreSub","hasMore","sub","sentinel","_a","entries","entry","_b","changedProperties","query","addr","input","address","html","channels","showInitialLoading","showEmptyState","showSearchingState","_c","e","css","__decorateClass","property","state","customElement"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { LitElement } from 'lit';
2
2
  /**
3
3
  * An example button component built with Lit
4
4
  *
5
+ * @fires sw-click - Fired when the button is clicked (and not disabled). Detail: `{ originalEvent: MouseEvent }`
5
6
  * @slot - Default slot for button content
6
7
  * @csspart button - The button element
7
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"example-button.d.ts","sourceRoot":"","sources":["../../src/components/example-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C;;;;;GAKG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,MAAM,CAAC,MAAM,0BA4BX;IAGF,QAAQ,UAAS;IAGjB,OAAO,EAAE,SAAS,GAAG,WAAW,CAAa;IAE7C,OAAO,CAAC,YAAY;IAgBpB,MAAM;CAOP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,mBAAmB,EAAE,aAAa,CAAC;KACpC;CACF"}
1
+ {"version":3,"file":"example-button.d.ts","sourceRoot":"","sources":["../../src/components/example-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C;;;;;;GAMG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,MAAM,CAAC,MAAM,0BA4BX;IAGF,QAAQ,UAAS;IAGjB,OAAO,EAAE,SAAS,GAAG,WAAW,CAAa;IAE7C,OAAO,CAAC,YAAY;IAgBpB,MAAM;CAOP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,mBAAmB,EAAE,aAAa,CAAC;KACpC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"example-button.js","sources":["../../src/components/example-button.ts"],"sourcesContent":["import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n/**\n * An example button component built with Lit\n *\n * @slot - Default slot for button content\n * @csspart button - The button element\n */\n@customElement('sw-example-button')\nexport class ExampleButton extends LitElement {\n static styles = css`\n :host {\n display: inline-block;\n }\n\n button {\n padding: 0.5rem 1rem;\n font-size: 1rem;\n border: none;\n border-radius: 4px;\n background-color: #0066cc;\n color: white;\n cursor: pointer;\n transition: background-color 0.2s;\n }\n\n button:hover {\n background-color: #0052a3;\n }\n\n button:active {\n background-color: #003d7a;\n }\n\n button:disabled {\n background-color: #cccccc;\n cursor: not-allowed;\n }\n `;\n\n @property({ type: Boolean })\n disabled = false;\n\n @property({ type: String })\n variant: 'primary' | 'secondary' = 'primary';\n\n private _handleClick(e: MouseEvent) {\n if (this.disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent('sw-click', {\n detail: { originalEvent: e },\n bubbles: true,\n composed: true\n })\n );\n }\n\n render() {\n return html`\n <button part=\"button\" ?disabled=${this.disabled} @click=${this._handleClick}>\n <slot></slot>\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-example-button': ExampleButton;\n }\n}\n"],"names":["ExampleButton","LitElement","e","html","css","__decorateClass","property","customElement"],"mappings":";;;;;;;AAUO,IAAMA,IAAN,cAA4BC,EAAW;AAAA,EAAvC,cAAA;AAAA,UAAA,GAAA,SAAA,GAgCL,KAAA,WAAW,IAGX,KAAA,UAAmC;AAAA,EAAA;AAAA,EAE3B,aAAaC,GAAe;AAClC,QAAI,KAAK,UAAU;AACjB,MAAAA,EAAE,eAAA,GACFA,EAAE,gBAAA;AACF;AAAA,IACF;AAEA,SAAK;AAAA,MACH,IAAI,YAAY,YAAY;AAAA,QAC1B,QAAQ,EAAE,eAAeA,EAAA;AAAA,QACzB,SAAS;AAAA,QACT,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAAA,EAEL;AAAA,EAEA,SAAS;AACP,WAAOC;AAAA,wCAC6B,KAAK,QAAQ,WAAW,KAAK,YAAY;AAAA;AAAA;AAAA;AAAA,EAI/E;AACF;AA5DaH,EACJ,SAASI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BhBC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAA,CAAS;AAAA,GA/BhBN,EAgCX,WAAA,YAAA,CAAA;AAGAK,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAlCfN,EAmCX,WAAA,WAAA,CAAA;AAnCWA,IAANK,EAAA;AAAA,EADNE,EAAc,mBAAmB;AAAA,GACrBP,CAAA;"}
1
+ {"version":3,"file":"example-button.js","sources":["../../src/components/example-button.ts"],"sourcesContent":["import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n/**\n * An example button component built with Lit\n *\n * @fires sw-click - Fired when the button is clicked (and not disabled). Detail: `{ originalEvent: MouseEvent }`\n * @slot - Default slot for button content\n * @csspart button - The button element\n */\n@customElement('sw-example-button')\nexport class ExampleButton extends LitElement {\n static styles = css`\n :host {\n display: inline-block;\n }\n\n button {\n padding: 0.5rem 1rem;\n font-size: 1rem;\n border: none;\n border-radius: 4px;\n background-color: #0066cc;\n color: white;\n cursor: pointer;\n transition: background-color 0.2s;\n }\n\n button:hover {\n background-color: #0052a3;\n }\n\n button:active {\n background-color: #003d7a;\n }\n\n button:disabled {\n background-color: #cccccc;\n cursor: not-allowed;\n }\n `;\n\n @property({ type: Boolean })\n disabled = false;\n\n @property({ type: String })\n variant: 'primary' | 'secondary' = 'primary';\n\n private _handleClick(e: MouseEvent) {\n if (this.disabled) {\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n this.dispatchEvent(\n new CustomEvent('sw-click', {\n detail: { originalEvent: e },\n bubbles: true,\n composed: true\n })\n );\n }\n\n render() {\n return html`\n <button part=\"button\" ?disabled=${this.disabled} @click=${this._handleClick}>\n <slot></slot>\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-example-button': ExampleButton;\n }\n}\n"],"names":["ExampleButton","LitElement","e","html","css","__decorateClass","property","customElement"],"mappings":";;;;;;;AAWO,IAAMA,IAAN,cAA4BC,EAAW;AAAA,EAAvC,cAAA;AAAA,UAAA,GAAA,SAAA,GAgCL,KAAA,WAAW,IAGX,KAAA,UAAmC;AAAA,EAAA;AAAA,EAE3B,aAAaC,GAAe;AAClC,QAAI,KAAK,UAAU;AACjB,MAAAA,EAAE,eAAA,GACFA,EAAE,gBAAA;AACF;AAAA,IACF;AAEA,SAAK;AAAA,MACH,IAAI,YAAY,YAAY;AAAA,QAC1B,QAAQ,EAAE,eAAeA,EAAA;AAAA,QACzB,SAAS;AAAA,QACT,UAAU;AAAA,MAAA,CACX;AAAA,IAAA;AAAA,EAEL;AAAA,EAEA,SAAS;AACP,WAAOC;AAAA,wCAC6B,KAAK,QAAQ,WAAW,KAAK,YAAY;AAAA;AAAA;AAAA;AAAA,EAI/E;AACF;AA5DaH,EACJ,SAASI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BhBC,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,QAAA,CAAS;AAAA,GA/BhBN,EAgCX,WAAA,YAAA,CAAA;AAGAK,EAAA;AAAA,EADCC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAlCfN,EAmCX,WAAA,WAAA,CAAA;AAnCWA,IAANK,EAAA;AAAA,EADNE,EAAc,mBAAmB;AAAA,GACrBP,CAAA;"}
@@ -11,6 +11,35 @@
11
11
  * .capabilities=${['memberMuteAudio', 'memberRemove']}
12
12
  * ></sw-participant-controls>
13
13
  * ```
14
+ *
15
+ * @fires sw-participant-mute-audio - Fired to toggle a participant's audio. Detail: `{ participantId: string, muted: boolean }`
16
+ * @fires sw-participant-mute-video - Fired to toggle a participant's video. Detail: `{ participantId: string, muted: boolean }`
17
+ * @fires sw-participant-remove - Fired to remove a participant. Detail: `{ participantId: string }`
18
+ * @fires sw-participant-volume - Fired when a participant's volume changes. Detail: `{ participantId: string, volume: number }`
19
+ * @fires sw-participant-pin - Fired to pin/unpin a participant. Detail: `{ participantId: string, pinned: boolean }`
20
+ *
21
+ * @cssprop [--sw-color-primary=#044cf6] - Primary brand color
22
+ * @cssprop [--sw-color-primary-hover=#0339c4] - Primary color on hover
23
+ * @cssprop [--sw-color-success=#10b981] - Success/positive color
24
+ * @cssprop [--sw-color-danger=#ef4444] - Danger/destructive color
25
+ * @cssprop [--sw-color-danger-hover=#dc2626] - Danger color on hover
26
+ * @cssprop [--sw-color-warning=#f59e0b] - Warning color
27
+ * @cssprop [--sw-color-text=#1f2937] - Primary text color
28
+ * @cssprop [--sw-color-text-muted=#6b7280] - Secondary/muted text color
29
+ * @cssprop [--sw-color-text-inverse=#ffffff] - Inverse text color for active buttons
30
+ * @cssprop [--sw-color-background=#ffffff] - Component background color
31
+ * @cssprop [--sw-color-background-hover=#f3f4f6] - Background color on hover
32
+ * @cssprop [--sw-color-background-active=#e5e7eb] - Background color on active/press
33
+ * @cssprop [--sw-color-border=#e5e7eb] - Border color
34
+ * @cssprop [--sw-font-family=-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif] - Font family
35
+ * @cssprop [--sw-font-size-xs=11px] - Extra-small font size
36
+ * @cssprop [--sw-font-size-sm=12px] - Small font size
37
+ * @cssprop [--sw-font-size-base=14px] - Base font size
38
+ * @cssprop [--sw-space-1=4px] - Smallest spacing unit
39
+ * @cssprop [--sw-space-2=8px] - Small spacing unit
40
+ * @cssprop [--sw-space-3=12px] - Medium spacing unit
41
+ * @cssprop [--sw-space-4=16px] - Large spacing unit
42
+ * @cssprop [--sw-border-radius=8px] - Border radius for containers and buttons
14
43
  */
15
44
  import { LitElement } from 'lit';
16
45
  import type { Observable } from 'rxjs';
@@ -19,9 +48,9 @@ import type { Observable } from 'rxjs';
19
48
  */
20
49
  export interface ControlParticipant {
21
50
  id: string;
22
- name$?: Observable<string>;
23
- audioMuted$?: Observable<boolean>;
24
- videoMuted$?: Observable<boolean>;
51
+ name$?: Observable<string | undefined>;
52
+ audioMuted$?: Observable<boolean | undefined>;
53
+ videoMuted$?: Observable<boolean | undefined>;
25
54
  mute?(): Promise<void>;
26
55
  unmute?(): Promise<void>;
27
56
  muteVideo?(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"participant-controls.d.ts","sourceRoot":"","sources":["../../src/components/participant-controls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3B,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,SAAS,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,qBACa,4BAA6B,SAAQ,UAAU;IAC1D,MAAM,CAAC,MAAM,0BAoMX;IAEF;;OAEG;IAEH,WAAW,EAAE,kBAAkB,GAAG,IAAI,CAAQ;IAE9C;;OAEG;IAEH,YAAY,EAAE,MAAM,EAAE,CAAM;IAE5B;;OAEG;IAEH,UAAU,EAAE,OAAO,CAAS;IAE5B;;OAEG;IAEH,OAAO,EAAE,OAAO,CAAS;IAEzB;;OAEG;IAEH,OAAO,CAAC,MAAM,CAAe;IAE7B;;OAEG;IAEH,OAAO,CAAC,eAAe,CAAyB;IAEhD;;OAEG;IAEH,OAAO,CAAC,UAAU,CAAkB;IAEpC;;OAEG;IAEH,OAAO,CAAC,UAAU,CAAkB;IAEpC;;OAEG;IAEH,OAAO,CAAC,QAAQ,CAAkB;IAElC;;OAEG;IACH,OAAO,CAAC,aAAa,CAAsB;IAE3C,iBAAiB;IAKjB,oBAAoB;IAKpB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAO/C,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,OAAO;IAKf,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,SAAS,GAEpB;IAED,OAAO,KAAK,gBAAgB,GAI3B;YAEa,qBAAqB;YAsBrB,qBAAqB;YAsBrB,YAAY;IAkB1B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,eAAe;IAYvB,MAAM;CAqIP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,yBAAyB,EAAE,4BAA4B,CAAC;KACzD;CACF"}
1
+ {"version":3,"file":"participant-controls.d.ts","sourceRoot":"","sources":["../../src/components/participant-controls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,UAAU,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC9C,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,SAAS,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,qBACa,4BAA6B,SAAQ,UAAU;IAC1D,MAAM,CAAC,MAAM,0BAoMX;IAEF;;OAEG;IAEH,WAAW,EAAE,kBAAkB,GAAG,IAAI,CAAQ;IAE9C;;OAEG;IAEH,YAAY,EAAE,MAAM,EAAE,CAAM;IAE5B;;OAEG;IAEH,UAAU,EAAE,OAAO,CAAS;IAE5B;;OAEG;IAEH,OAAO,EAAE,OAAO,CAAS;IAEzB;;OAEG;IAEH,OAAO,CAAC,MAAM,CAAe;IAE7B;;OAEG;IAEH,OAAO,CAAC,eAAe,CAAyB;IAEhD;;OAEG;IAEH,OAAO,CAAC,UAAU,CAAkB;IAEpC;;OAEG;IAEH,OAAO,CAAC,UAAU,CAAkB;IAEpC;;OAEG;IAEH,OAAO,CAAC,QAAQ,CAAkB;IAElC;;OAEG;IACH,OAAO,CAAC,aAAa,CAAsB;IAE3C,iBAAiB;IAKjB,oBAAoB;IAKpB,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAO/C,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,OAAO;IAKf,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,SAAS,GAEpB;IAED,OAAO,KAAK,gBAAgB,GAI3B;YAEa,qBAAqB;YAsBrB,qBAAqB;YAsBrB,YAAY;IAkB1B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,eAAe;IAYvB,MAAM;CAqIP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,yBAAyB,EAAE,4BAA4B,CAAC;KACzD;CACF"}
@@ -22,19 +22,19 @@ let i = class extends d {
22
22
  if (this.participant) {
23
23
  if (this.participant.name$) {
24
24
  const t = this.participant.name$.subscribe((e) => {
25
- this.participantName = e;
25
+ e !== void 0 && (this.participantName = e);
26
26
  });
27
27
  this.subscriptions.push(t);
28
28
  }
29
29
  if (this.participant.audioMuted$) {
30
30
  const t = this.participant.audioMuted$.subscribe((e) => {
31
- this.audioMuted = e;
31
+ e !== void 0 && (this.audioMuted = e);
32
32
  });
33
33
  this.subscriptions.push(t);
34
34
  }
35
35
  if (this.participant.videoMuted$) {
36
36
  const t = this.participant.videoMuted$.subscribe((e) => {
37
- this.videoMuted = e;
37
+ e !== void 0 && (this.videoMuted = e);
38
38
  });
39
39
  this.subscriptions.push(t);
40
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"participant-controls.js","sources":["../../src/components/participant-controls.ts"],"sourcesContent":["/**\n * Participant Controls Component\n *\n * Individual participant control panel with actions like mute, remove,\n * volume control, and pin/spotlight.\n *\n * @example\n * ```html\n * <sw-participant-controls\n * .participant=${participant}\n * .capabilities=${['memberMuteAudio', 'memberRemove']}\n * ></sw-participant-controls>\n * ```\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { Subscription } from 'rxjs';\nimport type { Observable } from 'rxjs';\n\n/**\n * Participant interface for controls component\n */\nexport interface ControlParticipant {\n id: string;\n name$?: Observable<string>;\n audioMuted$?: Observable<boolean>;\n videoMuted$?: Observable<boolean>;\n mute?(): Promise<void>;\n unmute?(): Promise<void>;\n muteVideo?(): Promise<void>;\n unmuteVideo?(): Promise<void>;\n remove?(): Promise<void>;\n}\n\n@customElement('sw-participant-controls')\nexport class ParticipantControlsComponent extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-primary-hover: #0339c4;\n --sw-color-success: #10b981;\n --sw-color-danger: #ef4444;\n --sw-color-danger-hover: #dc2626;\n --sw-color-warning: #f59e0b;\n --sw-color-text: #1f2937;\n --sw-color-text-muted: #6b7280;\n --sw-color-text-inverse: #ffffff;\n --sw-color-background: #ffffff;\n --sw-color-background-hover: #f3f4f6;\n --sw-color-background-active: #e5e7eb;\n --sw-color-border: #e5e7eb;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-font-size-xs: 11px;\n --sw-font-size-sm: 12px;\n --sw-font-size-base: 14px;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-space-4: 16px;\n --sw-border-radius: 8px;\n\n display: block;\n font-family: var(--sw-font-family);\n }\n\n /* Dark mode support */\n :host([data-theme='dark']) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n --sw-color-border: #374151;\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([data-theme='light'])) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n --sw-color-border: #374151;\n }\n }\n\n .container {\n display: flex;\n flex-direction: column;\n gap: var(--sw-space-2);\n padding: var(--sw-space-3);\n background: var(--sw-color-background);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-border-radius);\n min-width: 200px;\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n }\n\n .header {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n padding-bottom: var(--sw-space-2);\n border-bottom: 1px solid var(--sw-color-border);\n }\n\n .participant-name {\n flex: 1;\n font-size: var(--sw-font-size-base);\n font-weight: 500;\n color: var(--sw-color-text);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .actions {\n display: flex;\n flex-direction: column;\n gap: var(--sw-space-2);\n }\n\n .action-button {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n padding: var(--sw-space-2) var(--sw-space-3);\n background: var(--sw-color-background-hover);\n border: none;\n border-radius: var(--sw-border-radius);\n color: var(--sw-color-text);\n font-family: var(--sw-font-family);\n font-size: var(--sw-font-size-sm);\n cursor: pointer;\n transition: background-color 0.15s ease;\n text-align: left;\n }\n\n .action-button:hover {\n background: var(--sw-color-background-active);\n }\n\n .action-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .action-button.active {\n background: var(--sw-color-danger);\n color: var(--sw-color-text-inverse);\n }\n\n .action-button.active:hover {\n background: var(--sw-color-danger-hover);\n }\n\n .action-button.danger {\n color: var(--sw-color-danger);\n }\n\n .action-button.danger:hover {\n background: var(--sw-color-danger);\n color: var(--sw-color-text-inverse);\n }\n\n .action-button svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n }\n\n .volume-control {\n display: flex;\n flex-direction: column;\n gap: var(--sw-space-1);\n }\n\n .volume-label {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n font-size: var(--sw-font-size-sm);\n color: var(--sw-color-text-muted);\n }\n\n .volume-label svg {\n width: 14px;\n height: 14px;\n }\n\n .volume-slider {\n width: 100%;\n height: 4px;\n -webkit-appearance: none;\n appearance: none;\n background: var(--sw-color-border);\n border-radius: 2px;\n outline: none;\n cursor: pointer;\n }\n\n .volume-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 14px;\n height: 14px;\n background: var(--sw-color-primary);\n border-radius: 50%;\n cursor: pointer;\n transition: transform 0.1s ease;\n }\n\n .volume-slider::-webkit-slider-thumb:hover {\n transform: scale(1.2);\n }\n\n .volume-slider::-moz-range-thumb {\n width: 14px;\n height: 14px;\n background: var(--sw-color-primary);\n border-radius: 50%;\n border: none;\n cursor: pointer;\n }\n\n .no-actions {\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-sm);\n text-align: center;\n padding: var(--sw-space-2);\n }\n `;\n\n /**\n * Participant object to control\n */\n @property({ attribute: false })\n participant: ControlParticipant | null = null;\n\n /**\n * Available capabilities (actions user can perform)\n */\n @property({ attribute: false })\n capabilities: string[] = [];\n\n /**\n * Whether to show volume slider\n */\n @property({ type: Boolean, attribute: 'show-volume' })\n showVolume: boolean = false;\n\n /**\n * Whether to show pin/spotlight button\n */\n @property({ type: Boolean, attribute: 'show-pin' })\n showPin: boolean = false;\n\n /**\n * Current volume (0-100)\n */\n @state()\n private volume: number = 100;\n\n /**\n * Participant name\n */\n @state()\n private participantName: string = 'Participant';\n\n /**\n * Audio mute state\n */\n @state()\n private audioMuted: boolean = false;\n\n /**\n * Video mute state\n */\n @state()\n private videoMuted: boolean = false;\n\n /**\n * Is pinned/spotlighted\n */\n @state()\n private isPinned: boolean = false;\n\n /**\n * RxJS subscriptions for cleanup\n */\n private subscriptions: Subscription[] = [];\n\n connectedCallback() {\n super.connectedCallback();\n this.subscribeToParticipant();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.cleanup();\n }\n\n updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('participant')) {\n this.cleanup();\n this.subscribeToParticipant();\n }\n }\n\n private subscribeToParticipant() {\n if (!this.participant) return;\n\n if (this.participant.name$) {\n const nameSub = this.participant.name$.subscribe((name) => {\n this.participantName = name;\n });\n this.subscriptions.push(nameSub);\n }\n\n if (this.participant.audioMuted$) {\n const audioSub = this.participant.audioMuted$.subscribe((muted) => {\n this.audioMuted = muted;\n });\n this.subscriptions.push(audioSub);\n }\n\n if (this.participant.videoMuted$) {\n const videoSub = this.participant.videoMuted$.subscribe((muted) => {\n this.videoMuted = muted;\n });\n this.subscriptions.push(videoSub);\n }\n }\n\n private cleanup() {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subscriptions = [];\n }\n\n private get canMuteAudio(): boolean {\n return this.capabilities.includes('memberMuteAudio');\n }\n\n private get canMuteVideo(): boolean {\n return this.capabilities.includes('memberMuteVideo');\n }\n\n private get canRemove(): boolean {\n return this.capabilities.includes('memberRemove');\n }\n\n private get hasAnyCapability(): boolean {\n return (\n this.canMuteAudio || this.canMuteVideo || this.canRemove || this.showVolume || this.showPin\n );\n }\n\n private async handleToggleAudioMute() {\n if (!this.participant) return;\n\n try {\n if (this.audioMuted && this.participant.unmute) {\n await this.participant.unmute();\n } else if (!this.audioMuted && this.participant.mute) {\n await this.participant.mute();\n }\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-mute-audio', {\n detail: { participant: this.participant, muted: !this.audioMuted },\n bubbles: true,\n composed: true\n })\n );\n } catch (error) {\n console.error('Failed to toggle audio mute:', error);\n }\n }\n\n private async handleToggleVideoMute() {\n if (!this.participant) return;\n\n try {\n if (this.videoMuted && this.participant.unmuteVideo) {\n await this.participant.unmuteVideo();\n } else if (!this.videoMuted && this.participant.muteVideo) {\n await this.participant.muteVideo();\n }\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-mute-video', {\n detail: { participant: this.participant, muted: !this.videoMuted },\n bubbles: true,\n composed: true\n })\n );\n } catch (error) {\n console.error('Failed to toggle video mute:', error);\n }\n }\n\n private async handleRemove() {\n if (!this.participant?.remove) return;\n\n try {\n await this.participant.remove();\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-remove', {\n detail: { participant: this.participant },\n bubbles: true,\n composed: true\n })\n );\n } catch (error) {\n console.error('Failed to remove participant:', error);\n }\n }\n\n private handleVolumeChange(e: Event) {\n const input = e.target as HTMLInputElement;\n this.volume = parseInt(input.value, 10);\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-volume', {\n detail: { participant: this.participant, volume: this.volume },\n bubbles: true,\n composed: true\n })\n );\n }\n\n private handleTogglePin() {\n this.isPinned = !this.isPinned;\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-pin', {\n detail: { participant: this.participant, pinned: this.isPinned },\n bubbles: true,\n composed: true\n })\n );\n }\n\n render() {\n return html`\n <div class=\"container\" part=\"container\">\n <div class=\"header\">\n <span class=\"participant-name\">${this.participantName}</span>\n </div>\n\n <div class=\"actions\">\n ${!this.hasAnyCapability\n ? html`<div class=\"no-actions\">No actions available</div>`\n : null}\n ${this.canMuteAudio\n ? html`\n <button\n class=\"action-button ${this.audioMuted ? 'active' : ''}\"\n part=\"action-button\"\n @click=${this.handleToggleAudioMute}\n aria-label=\"${this.audioMuted ? 'Unmute audio' : 'Mute audio'}\"\n >\n ${this.audioMuted\n ? html`<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z\"\n />\n </svg>`\n : html`<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"\n />\n </svg>`}\n ${this.audioMuted ? 'Unmute' : 'Mute'}\n </button>\n `\n : null}\n ${this.canMuteVideo\n ? html`\n <button\n class=\"action-button ${this.videoMuted ? 'active' : ''}\"\n part=\"action-button\"\n @click=${this.handleToggleVideoMute}\n aria-label=\"${this.videoMuted ? 'Unmute video' : 'Mute video'}\"\n >\n ${this.videoMuted\n ? html`<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M21 6.5l-4 4V7c0-.55-.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.21 0 .39-.08.54-.18L19.73 21 21 19.73 3.27 2z\"\n />\n </svg>`\n : html`<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z\"\n />\n </svg>`}\n ${this.videoMuted ? 'Enable video' : 'Disable video'}\n </button>\n `\n : null}\n ${this.showVolume\n ? html`\n <div class=\"volume-control\" part=\"slider\">\n <label class=\"volume-label\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"\n />\n </svg>\n Volume: ${this.volume}%\n </label>\n <input\n type=\"range\"\n class=\"volume-slider\"\n min=\"0\"\n max=\"100\"\n .value=${String(this.volume)}\n @input=${this.handleVolumeChange}\n aria-label=\"Participant volume\"\n />\n </div>\n `\n : null}\n ${this.showPin\n ? html`\n <button\n class=\"action-button ${this.isPinned ? 'active' : ''}\"\n part=\"action-button\"\n @click=${this.handleTogglePin}\n aria-label=\"${this.isPinned ? 'Unpin' : 'Pin'}\"\n aria-pressed=\"${this.isPinned}\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z\" />\n </svg>\n ${this.isPinned ? 'Unpin' : 'Pin'}\n </button>\n `\n : null}\n ${this.canRemove\n ? html`\n <button\n class=\"action-button danger\"\n part=\"action-button\"\n @click=${this.handleRemove}\n aria-label=\"Remove participant\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n />\n </svg>\n Remove\n </button>\n `\n : null}\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-participant-controls': ParticipantControlsComponent;\n }\n}\n"],"names":["ParticipantControlsComponent","LitElement","changedProperties","nameSub","name","audioSub","muted","videoSub","sub","error","_a","e","input","html","css","__decorateClass","property","state","customElement"],"mappings":"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"}
1
+ {"version":3,"file":"participant-controls.js","sources":["../../src/components/participant-controls.ts"],"sourcesContent":["/**\n * Participant Controls Component\n *\n * Individual participant control panel with actions like mute, remove,\n * volume control, and pin/spotlight.\n *\n * @example\n * ```html\n * <sw-participant-controls\n * .participant=${participant}\n * .capabilities=${['memberMuteAudio', 'memberRemove']}\n * ></sw-participant-controls>\n * ```\n *\n * @fires sw-participant-mute-audio - Fired to toggle a participant's audio. Detail: `{ participantId: string, muted: boolean }`\n * @fires sw-participant-mute-video - Fired to toggle a participant's video. Detail: `{ participantId: string, muted: boolean }`\n * @fires sw-participant-remove - Fired to remove a participant. Detail: `{ participantId: string }`\n * @fires sw-participant-volume - Fired when a participant's volume changes. Detail: `{ participantId: string, volume: number }`\n * @fires sw-participant-pin - Fired to pin/unpin a participant. Detail: `{ participantId: string, pinned: boolean }`\n *\n * @cssprop [--sw-color-primary=#044cf6] - Primary brand color\n * @cssprop [--sw-color-primary-hover=#0339c4] - Primary color on hover\n * @cssprop [--sw-color-success=#10b981] - Success/positive color\n * @cssprop [--sw-color-danger=#ef4444] - Danger/destructive color\n * @cssprop [--sw-color-danger-hover=#dc2626] - Danger color on hover\n * @cssprop [--sw-color-warning=#f59e0b] - Warning color\n * @cssprop [--sw-color-text=#1f2937] - Primary text color\n * @cssprop [--sw-color-text-muted=#6b7280] - Secondary/muted text color\n * @cssprop [--sw-color-text-inverse=#ffffff] - Inverse text color for active buttons\n * @cssprop [--sw-color-background=#ffffff] - Component background color\n * @cssprop [--sw-color-background-hover=#f3f4f6] - Background color on hover\n * @cssprop [--sw-color-background-active=#e5e7eb] - Background color on active/press\n * @cssprop [--sw-color-border=#e5e7eb] - Border color\n * @cssprop [--sw-font-family=-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif] - Font family\n * @cssprop [--sw-font-size-xs=11px] - Extra-small font size\n * @cssprop [--sw-font-size-sm=12px] - Small font size\n * @cssprop [--sw-font-size-base=14px] - Base font size\n * @cssprop [--sw-space-1=4px] - Smallest spacing unit\n * @cssprop [--sw-space-2=8px] - Small spacing unit\n * @cssprop [--sw-space-3=12px] - Medium spacing unit\n * @cssprop [--sw-space-4=16px] - Large spacing unit\n * @cssprop [--sw-border-radius=8px] - Border radius for containers and buttons\n */\n\nimport { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport { Subscription } from 'rxjs';\nimport type { Observable } from 'rxjs';\n\n/**\n * Participant interface for controls component\n */\nexport interface ControlParticipant {\n id: string;\n name$?: Observable<string | undefined>;\n audioMuted$?: Observable<boolean | undefined>;\n videoMuted$?: Observable<boolean | undefined>;\n mute?(): Promise<void>;\n unmute?(): Promise<void>;\n muteVideo?(): Promise<void>;\n unmuteVideo?(): Promise<void>;\n remove?(): Promise<void>;\n}\n\n@customElement('sw-participant-controls')\nexport class ParticipantControlsComponent extends LitElement {\n static styles = css`\n :host {\n /* CSS Custom Properties for theming */\n --sw-color-primary: #044cf6;\n --sw-color-primary-hover: #0339c4;\n --sw-color-success: #10b981;\n --sw-color-danger: #ef4444;\n --sw-color-danger-hover: #dc2626;\n --sw-color-warning: #f59e0b;\n --sw-color-text: #1f2937;\n --sw-color-text-muted: #6b7280;\n --sw-color-text-inverse: #ffffff;\n --sw-color-background: #ffffff;\n --sw-color-background-hover: #f3f4f6;\n --sw-color-background-active: #e5e7eb;\n --sw-color-border: #e5e7eb;\n --sw-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n --sw-font-size-xs: 11px;\n --sw-font-size-sm: 12px;\n --sw-font-size-base: 14px;\n --sw-space-1: 4px;\n --sw-space-2: 8px;\n --sw-space-3: 12px;\n --sw-space-4: 16px;\n --sw-border-radius: 8px;\n\n display: block;\n font-family: var(--sw-font-family);\n }\n\n /* Dark mode support */\n :host([data-theme='dark']) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n --sw-color-border: #374151;\n }\n\n @media (prefers-color-scheme: dark) {\n :host(:not([data-theme='light'])) {\n --sw-color-text: #f9fafb;\n --sw-color-text-muted: #9ca3af;\n --sw-color-background: #1f2937;\n --sw-color-background-hover: #374151;\n --sw-color-background-active: #4b5563;\n --sw-color-border: #374151;\n }\n }\n\n .container {\n display: flex;\n flex-direction: column;\n gap: var(--sw-space-2);\n padding: var(--sw-space-3);\n background: var(--sw-color-background);\n border: 1px solid var(--sw-color-border);\n border-radius: var(--sw-border-radius);\n min-width: 200px;\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n }\n\n .header {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n padding-bottom: var(--sw-space-2);\n border-bottom: 1px solid var(--sw-color-border);\n }\n\n .participant-name {\n flex: 1;\n font-size: var(--sw-font-size-base);\n font-weight: 500;\n color: var(--sw-color-text);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .actions {\n display: flex;\n flex-direction: column;\n gap: var(--sw-space-2);\n }\n\n .action-button {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n padding: var(--sw-space-2) var(--sw-space-3);\n background: var(--sw-color-background-hover);\n border: none;\n border-radius: var(--sw-border-radius);\n color: var(--sw-color-text);\n font-family: var(--sw-font-family);\n font-size: var(--sw-font-size-sm);\n cursor: pointer;\n transition: background-color 0.15s ease;\n text-align: left;\n }\n\n .action-button:hover {\n background: var(--sw-color-background-active);\n }\n\n .action-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .action-button.active {\n background: var(--sw-color-danger);\n color: var(--sw-color-text-inverse);\n }\n\n .action-button.active:hover {\n background: var(--sw-color-danger-hover);\n }\n\n .action-button.danger {\n color: var(--sw-color-danger);\n }\n\n .action-button.danger:hover {\n background: var(--sw-color-danger);\n color: var(--sw-color-text-inverse);\n }\n\n .action-button svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n }\n\n .volume-control {\n display: flex;\n flex-direction: column;\n gap: var(--sw-space-1);\n }\n\n .volume-label {\n display: flex;\n align-items: center;\n gap: var(--sw-space-2);\n font-size: var(--sw-font-size-sm);\n color: var(--sw-color-text-muted);\n }\n\n .volume-label svg {\n width: 14px;\n height: 14px;\n }\n\n .volume-slider {\n width: 100%;\n height: 4px;\n -webkit-appearance: none;\n appearance: none;\n background: var(--sw-color-border);\n border-radius: 2px;\n outline: none;\n cursor: pointer;\n }\n\n .volume-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 14px;\n height: 14px;\n background: var(--sw-color-primary);\n border-radius: 50%;\n cursor: pointer;\n transition: transform 0.1s ease;\n }\n\n .volume-slider::-webkit-slider-thumb:hover {\n transform: scale(1.2);\n }\n\n .volume-slider::-moz-range-thumb {\n width: 14px;\n height: 14px;\n background: var(--sw-color-primary);\n border-radius: 50%;\n border: none;\n cursor: pointer;\n }\n\n .no-actions {\n color: var(--sw-color-text-muted);\n font-size: var(--sw-font-size-sm);\n text-align: center;\n padding: var(--sw-space-2);\n }\n `;\n\n /**\n * Participant object to control\n */\n @property({ attribute: false })\n participant: ControlParticipant | null = null;\n\n /**\n * Available capabilities (actions user can perform)\n */\n @property({ attribute: false })\n capabilities: string[] = [];\n\n /**\n * Whether to show volume slider\n */\n @property({ type: Boolean, attribute: 'show-volume' })\n showVolume: boolean = false;\n\n /**\n * Whether to show pin/spotlight button\n */\n @property({ type: Boolean, attribute: 'show-pin' })\n showPin: boolean = false;\n\n /**\n * Current volume (0-100)\n */\n @state()\n private volume: number = 100;\n\n /**\n * Participant name\n */\n @state()\n private participantName: string = 'Participant';\n\n /**\n * Audio mute state\n */\n @state()\n private audioMuted: boolean = false;\n\n /**\n * Video mute state\n */\n @state()\n private videoMuted: boolean = false;\n\n /**\n * Is pinned/spotlighted\n */\n @state()\n private isPinned: boolean = false;\n\n /**\n * RxJS subscriptions for cleanup\n */\n private subscriptions: Subscription[] = [];\n\n connectedCallback() {\n super.connectedCallback();\n this.subscribeToParticipant();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.cleanup();\n }\n\n updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('participant')) {\n this.cleanup();\n this.subscribeToParticipant();\n }\n }\n\n private subscribeToParticipant() {\n if (!this.participant) return;\n\n if (this.participant.name$) {\n const nameSub = this.participant.name$.subscribe((name) => {\n if (name !== undefined) this.participantName = name;\n });\n this.subscriptions.push(nameSub);\n }\n\n if (this.participant.audioMuted$) {\n const audioSub = this.participant.audioMuted$.subscribe((muted) => {\n if (muted !== undefined) this.audioMuted = muted;\n });\n this.subscriptions.push(audioSub);\n }\n\n if (this.participant.videoMuted$) {\n const videoSub = this.participant.videoMuted$.subscribe((muted) => {\n if (muted !== undefined) this.videoMuted = muted;\n });\n this.subscriptions.push(videoSub);\n }\n }\n\n private cleanup() {\n this.subscriptions.forEach((sub) => sub.unsubscribe());\n this.subscriptions = [];\n }\n\n private get canMuteAudio(): boolean {\n return this.capabilities.includes('memberMuteAudio');\n }\n\n private get canMuteVideo(): boolean {\n return this.capabilities.includes('memberMuteVideo');\n }\n\n private get canRemove(): boolean {\n return this.capabilities.includes('memberRemove');\n }\n\n private get hasAnyCapability(): boolean {\n return (\n this.canMuteAudio || this.canMuteVideo || this.canRemove || this.showVolume || this.showPin\n );\n }\n\n private async handleToggleAudioMute() {\n if (!this.participant) return;\n\n try {\n if (this.audioMuted && this.participant.unmute) {\n await this.participant.unmute();\n } else if (!this.audioMuted && this.participant.mute) {\n await this.participant.mute();\n }\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-mute-audio', {\n detail: { participant: this.participant, muted: !this.audioMuted },\n bubbles: true,\n composed: true\n })\n );\n } catch (error) {\n console.error('Failed to toggle audio mute:', error);\n }\n }\n\n private async handleToggleVideoMute() {\n if (!this.participant) return;\n\n try {\n if (this.videoMuted && this.participant.unmuteVideo) {\n await this.participant.unmuteVideo();\n } else if (!this.videoMuted && this.participant.muteVideo) {\n await this.participant.muteVideo();\n }\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-mute-video', {\n detail: { participant: this.participant, muted: !this.videoMuted },\n bubbles: true,\n composed: true\n })\n );\n } catch (error) {\n console.error('Failed to toggle video mute:', error);\n }\n }\n\n private async handleRemove() {\n if (!this.participant?.remove) return;\n\n try {\n await this.participant.remove();\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-remove', {\n detail: { participant: this.participant },\n bubbles: true,\n composed: true\n })\n );\n } catch (error) {\n console.error('Failed to remove participant:', error);\n }\n }\n\n private handleVolumeChange(e: Event) {\n const input = e.target as HTMLInputElement;\n this.volume = parseInt(input.value, 10);\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-volume', {\n detail: { participant: this.participant, volume: this.volume },\n bubbles: true,\n composed: true\n })\n );\n }\n\n private handleTogglePin() {\n this.isPinned = !this.isPinned;\n\n this.dispatchEvent(\n new CustomEvent('sw-participant-pin', {\n detail: { participant: this.participant, pinned: this.isPinned },\n bubbles: true,\n composed: true\n })\n );\n }\n\n render() {\n return html`\n <div class=\"container\" part=\"container\">\n <div class=\"header\">\n <span class=\"participant-name\">${this.participantName}</span>\n </div>\n\n <div class=\"actions\">\n ${!this.hasAnyCapability\n ? html`<div class=\"no-actions\">No actions available</div>`\n : null}\n ${this.canMuteAudio\n ? html`\n <button\n class=\"action-button ${this.audioMuted ? 'active' : ''}\"\n part=\"action-button\"\n @click=${this.handleToggleAudioMute}\n aria-label=\"${this.audioMuted ? 'Unmute audio' : 'Mute audio'}\"\n >\n ${this.audioMuted\n ? html`<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z\"\n />\n </svg>`\n : html`<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z\"\n />\n </svg>`}\n ${this.audioMuted ? 'Unmute' : 'Mute'}\n </button>\n `\n : null}\n ${this.canMuteVideo\n ? html`\n <button\n class=\"action-button ${this.videoMuted ? 'active' : ''}\"\n part=\"action-button\"\n @click=${this.handleToggleVideoMute}\n aria-label=\"${this.videoMuted ? 'Unmute video' : 'Mute video'}\"\n >\n ${this.videoMuted\n ? html`<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M21 6.5l-4 4V7c0-.55-.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.21 0 .39-.08.54-.18L19.73 21 21 19.73 3.27 2z\"\n />\n </svg>`\n : html`<svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n d=\"M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z\"\n />\n </svg>`}\n ${this.videoMuted ? 'Enable video' : 'Disable video'}\n </button>\n `\n : null}\n ${this.showVolume\n ? html`\n <div class=\"volume-control\" part=\"slider\">\n <label class=\"volume-label\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z\"\n />\n </svg>\n Volume: ${this.volume}%\n </label>\n <input\n type=\"range\"\n class=\"volume-slider\"\n min=\"0\"\n max=\"100\"\n .value=${String(this.volume)}\n @input=${this.handleVolumeChange}\n aria-label=\"Participant volume\"\n />\n </div>\n `\n : null}\n ${this.showPin\n ? html`\n <button\n class=\"action-button ${this.isPinned ? 'active' : ''}\"\n part=\"action-button\"\n @click=${this.handleTogglePin}\n aria-label=\"${this.isPinned ? 'Unpin' : 'Pin'}\"\n aria-pressed=\"${this.isPinned}\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z\" />\n </svg>\n ${this.isPinned ? 'Unpin' : 'Pin'}\n </button>\n `\n : null}\n ${this.canRemove\n ? html`\n <button\n class=\"action-button danger\"\n part=\"action-button\"\n @click=${this.handleRemove}\n aria-label=\"Remove participant\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\n />\n </svg>\n Remove\n </button>\n `\n : null}\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'sw-participant-controls': ParticipantControlsComponent;\n }\n}\n"],"names":["ParticipantControlsComponent","LitElement","changedProperties","nameSub","name","audioSub","muted","videoSub","sub","error","_a","e","input","html","css","__decorateClass","property","state","customElement"],"mappings":"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"}
@@ -10,6 +10,10 @@
10
10
  * <self-media mirror=${true}></self-media>
11
11
  * </participants>
12
12
  * ```
13
+ *
14
+ * @fires sw-participant-mute-audio - Fired to toggle a participant's audio. Detail: `{ participantId: string, muted: boolean }`
15
+ * @fires sw-participant-mute-video - Fired to toggle a participant's video. Detail: `{ participantId: string, muted: boolean }`
16
+ * @fires sw-participant-remove - Fired to remove a participant. Detail: `{ participantId: string }`
13
17
  */
14
18
  import { LitElement } from 'lit';
15
19
  import type { Call } from '../types/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"participants.d.ts","sourceRoot":"","sources":["../../src/components/participants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C,OAAO,KAAK,EAAE,IAAI,EAA4B,MAAM,mBAAmB,CAAC;AAIxE,qBACa,YAAa,SAAQ,UAAU;IAC1C,MAAM,CAAC,MAAM,0BAqHX;IAEF;;OAEG;IAGH,OAAO,CAAC,KAAK,CAAC,CAAO;IAErB;;OAEG;IACH,IACI,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,SAAS,EAI/B;IACD,IAAI,IAAI,IAAI,IAAI,GAAG,SAAS,CAE3B;IAED;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAqB;IAE/C;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAqB;IAE/C;;OAEG;IAEH,OAAO,CAAC,WAAW,CAAuB;IAE1C;;OAEG;IACH,OAAO,CAAC,aAAa,CAAsB;IAE3C;;OAEG;IACH,iBAAiB;IAKjB;;OAEG;IACH,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAUhE;;OAEG;IACH,oBAAoB;IAKpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,WAAW;IAKnB;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;YACW,gBAAgB;IAwB9B;;OAEG;YACW,gBAAgB;IAwB9B;;OAEG;YACW,aAAa;IAoB3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA0D1B;;OAEG;IACH,OAAO,CAAC,aAAa;IA0CrB;;OAEG;IACH,MAAM;CAMP;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,iBAAiB,EAAE,YAAY,CAAC;KACjC;CACF"}
1
+ {"version":3,"file":"participants.d.ts","sourceRoot":"","sources":["../../src/components/participants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C,OAAO,KAAK,EAAE,IAAI,EAA4B,MAAM,mBAAmB,CAAC;AAIxE,qBACa,YAAa,SAAQ,UAAU;IAC1C,MAAM,CAAC,MAAM,0BAqHX;IAEF;;OAEG;IAGH,OAAO,CAAC,KAAK,CAAC,CAAO;IAErB;;OAEG;IACH,IACI,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,SAAS,EAI/B;IACD,IAAI,IAAI,IAAI,IAAI,GAAG,SAAS,CAE3B;IAED;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAqB;IAE/C;;OAEG;IACH,OAAO,CAAC,kBAAkB,CAAqB;IAE/C;;OAEG;IAEH,OAAO,CAAC,WAAW,CAAuB;IAE1C;;OAEG;IACH,OAAO,CAAC,aAAa,CAAsB;IAE3C;;OAEG;IACH,iBAAiB;IAKjB;;OAEG;IACH,SAAS,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAUhE;;OAEG;IACH,oBAAoB;IAKpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,WAAW;IAKnB;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;YACW,gBAAgB;IAwB9B;;OAEG;YACW,gBAAgB;IAwB9B;;OAEG;YACW,aAAa;IAoB3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA0D1B;;OAEG;IACH,OAAO,CAAC,aAAa;IA0CrB;;OAEG;IACH,MAAM;CAMP;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,iBAAiB,EAAE,YAAY,CAAC;KACjC;CACF"}