@hidemikimura/chit-ui 0.2.0 → 0.3.0

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.
@@ -8,8 +8,14 @@ export const panelStyles = css`
8
8
  flex-direction: column;
9
9
  width: var(--chit-panel-width);
10
10
  height: var(--chit-panel-height);
11
- max-width: calc(100vw - var(--chit-panel-offset-x) * 2);
12
- max-height: calc(100vh - var(--chit-panel-offset-y) * 2);
11
+ /*
12
+ * The room left between the panel's own corner and the far edge. Doubling
13
+ * the offset instead would assume the panel is centred between two equal
14
+ * gaps, which stops being true the moment it is dragged: moving it up
15
+ * would have made it shorter on the way.
16
+ */
17
+ max-width: calc(100vw - var(--chit-panel-offset-x) - 8px);
18
+ max-height: calc(100vh - var(--chit-panel-offset-y) - 8px);
13
19
  overflow: hidden;
14
20
  border-radius: var(--chit-panel-radius);
15
21
  background-color: var(--chit-color-bg);
@@ -82,6 +88,21 @@ export const panelStyles = css`
82
88
  * and space-between pushes the image to one edge and the title to the
83
89
  * middle, away from each other.
84
90
  */
91
+ [part~='header'][data-draggable] {
92
+ cursor: grab;
93
+ touch-action: none;
94
+ user-select: none;
95
+ }
96
+
97
+ [part~='header'][data-draggable]:active {
98
+ cursor: grabbing;
99
+ }
100
+
101
+ [part~='header'][data-draggable]:focus-visible {
102
+ outline: 2px solid var(--chit-color-accent);
103
+ outline-offset: -2px;
104
+ }
105
+
85
106
  [part~='header-title'] {
86
107
  display: inline-flex;
87
108
  min-width: 0;
@@ -23,6 +23,7 @@ const CLOSED_PC = {
23
23
  position: 'bottom-right',
24
24
  offset: { x: 24, y: 24 },
25
25
  radius: 30,
26
+ draggable: false,
26
27
  image: null,
27
28
  label: null,
28
29
  component: null,
@@ -72,6 +73,9 @@ export const defaultTheme = {
72
73
  offset: { x: 24, y: 24 },
73
74
  radius: 16,
74
75
  launcher: /** @type {const} */ ('hidden'),
76
+ // Off by default: a widget that slides around under the thumb on a first
77
+ // touch would be startling, and most pages want it where they put it.
78
+ draggable: false,
75
79
  header: { visible: true, title: null, logo: null, home: false },
76
80
  background: { image: null },
77
81
  colors: {
@@ -95,6 +99,18 @@ export const defaultTheme = {
95
99
  // enough to show who is talking, and a tail is a strong look to impose on
96
100
  // a widget that has to sit in someone else's page.
97
101
  bubble: { radius: 14, tail: /** @type {const} */ ('none') },
102
+ // The wait for an answer. `auto` is opt-in: a widget that starts showing
103
+ // a wait on its own would be guessing that every submit is followed by a
104
+ // round trip, which is not true of a form or a scenario branch.
105
+ loading: {
106
+ auto: false,
107
+ // A spinner rather than the typing dots: the same three dots would say
108
+ // "someone is writing to you", which is not what a wait on a server is.
109
+ // `'dots'` is there for the cases where it really is a person.
110
+ style: /** @type {const} */ ('spinner'),
111
+ text: null,
112
+ timeout: 0,
113
+ },
98
114
  // Who is talking. Nothing by default: a widget that invents a name and a
99
115
  // face for the consumer's support desk would be guessing.
100
116
  speaker: {
package/src/types.js CHANGED
@@ -52,12 +52,25 @@
52
52
  * @property {number} [radius]
53
53
  * @property {string | null} [image] Icon image URL; null clears the default.
54
54
  * @property {string | null} [label]
55
+ * @property {boolean} [draggable] Let the reader move it around the viewport.
55
56
  * @property {(new () => HTMLElement) | string | null} [component] A component that draws the whole launcher.
56
57
  * @property {Record<string, unknown>} [props] Properties written to that component.
57
58
  * @property {{ background?: string, text?: string, shadow?: string }} [colors]
58
59
  * @property {Animation & { idle?: 'none' | 'pulse' | 'bounce' }} [animation]
59
60
  */
60
61
 
62
+ /**
63
+ * The wait between sending and the answer arriving.
64
+ *
65
+ * @typedef {'dots' | 'spinner' | 'text'} LoadingStyle
66
+ *
67
+ * @typedef {Object} LoadingTheme
68
+ * @property {boolean} [auto] Show it from `chat-submit` until the next message from the other side.
69
+ * @property {LoadingStyle} [style] What it looks like.
70
+ * @property {string | null} [text] Wording beside the animation, or on its own for `'text'`.
71
+ * @property {number} [timeout] ms after which it gives up on its own; 0 leaves it to the consumer.
72
+ */
73
+
61
74
  /**
62
75
  * Who is talking, as the theme describes them. A message that carries its own
63
76
  * `name` or `avatar` wins; `null` on the message hides what the theme set.
@@ -94,11 +107,13 @@
94
107
  * @property {Offset} [offset]
95
108
  * @property {number} [radius]
96
109
  * @property {'hidden' | 'visible'} [launcher] Keep the launcher visible while open.
110
+ * @property {boolean} [draggable] Let the reader move the panel by its title bar.
97
111
  * @property {{ visible?: boolean, title?: string | null, logo?: string | null, home?: boolean }} [header] The title bar: whether it is there at all, its text, its image, and whether it offers a way back to the start.
98
112
  * @property {{ image?: string | null }} [background]
99
113
  * @property {OpenColors} [colors]
100
114
  * @property {{ radius?: number, tail?: BubbleTail }} [bubble] Bubble corner radius, and where the tail points from.
101
115
  * @property {{ assistant?: SpeakerTheme, user?: SpeakerTheme }} [speaker] Default name and icon per side.
116
+ * @property {LoadingTheme} [loading] The wait for an answer.
102
117
  * @property {Animation & { scroll?: 'smooth' | 'instant' }} [animation]
103
118
  * @property {{ maxRows?: number, placeholder?: string | null, attach?: boolean, accept?: string, multiple?: boolean }} [input] Composer: rows, placeholder, and the attach button.
104
119
  */
@@ -122,6 +137,7 @@
122
137
  * @property {Position} position
123
138
  * @property {{ x: number, y: number }} offset
124
139
  * @property {number} radius
140
+ * @property {boolean} draggable
125
141
  * @property {string | null} image
126
142
  * @property {string | null} label
127
143
  * @property {(new () => HTMLElement) | string | null} component
@@ -136,11 +152,13 @@
136
152
  * @property {{ x: number, y: number }} offset
137
153
  * @property {number} radius
138
154
  * @property {'hidden' | 'visible'} launcher
155
+ * @property {boolean} draggable
139
156
  * @property {{ visible: boolean, title: string | null, logo: string | null, home: boolean }} header
140
157
  * @property {{ image: string | null }} background
141
158
  * @property {Required<OpenColors>} colors
142
159
  * @property {{ radius: number, tail: BubbleTail }} bubble
143
160
  * @property {{ assistant: Required<SpeakerTheme>, user: Required<SpeakerTheme> }} speaker
161
+ * @property {Required<LoadingTheme>} loading
144
162
  * @property {{ enter: Effect, exit: Effect, duration: number, scroll: 'smooth' | 'instant' }} animation
145
163
  * @property {{ maxRows: number, placeholder: string | null, attach: boolean, accept: string, multiple: boolean }} input
146
164
  *