infaira-canvas 0.1.9

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.
@@ -0,0 +1,127 @@
1
+ # ICan Widget
2
+
3
+ A widget for the **InfAIra Canvas (ICan)**, scaffolded with
4
+ [`infaira-canvas`](https://www.npmjs.com/package/infaira-canvas).
5
+
6
+ > Update this title and description with your widget’s name once you’ve had a
7
+ > chance to read through the file map below.
8
+
9
+ ---
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ npm install # install dependencies (already done by init)
15
+ npm run dev # local dev harness on http://localhost:8080
16
+ npm run build # produces dist/main.js (this is what you upload)
17
+ ```
18
+
19
+ When you’re happy with the result, upload `bundle.json` + `dist/main.js` to your
20
+ ICan portal’s **Widget Library** page (or via the API).
21
+
22
+ > Anything you see in `npm run dev` will look identical in the portal — the
23
+ > harness mirrors the portal’s component library.
24
+
25
+ ---
26
+
27
+ ## What goes where
28
+
29
+ | Path | What it does |
30
+ |-----------------------------------|----------------------------------------------------------------------|
31
+ | **`src/index.tsx`** | Your widget — edit this. Register it via `registerWidget(...)`. |
32
+ | **`src/styles.scss`** | Theme-aware SCSS. Use `--ican-*` CSS vars, never hardcoded colors. |
33
+ | **`src/ican.ts`** | Helper that exposes `registerWidget` + `IContextProvider` types. |
34
+ | `bundle.json` | Widget metadata (id, name, version, layout). Uploaded with the build. |
35
+ | `dist/main.js` | Webpack output. This is what the portal loads at runtime. |
36
+ | `index.html` | Dev harness — loads `dist/main.js` + the ICan component mock. |
37
+ | `ui.html` | Alternate harness for UI/display preview. |
38
+ | `resources/ican-components.js` | Local mock of `ican/components` — mirrors portal behaviour. |
39
+ | `resources/infaira-*.png`, favicon | Branding assets used by the dev harness. |
40
+ | `ican.d.ts` | TypeScript types for `ican/components`. Don’t edit; comes from server.|
41
+ | `designer.d.ts` | TypeScript types for the designer surface. |
42
+ | `webpack.config.js` | Build config. Externalises React + ICanComponents. |
43
+ | `tsconfig.json` | TypeScript config. |
44
+ | `package.json` | npm scripts + deps for this widget. |
45
+ | `localization.json` | i18n strings (key → translation). |
46
+ | `site.webmanifest` | PWA manifest used by the dev harness. |
47
+ | `sample-bundle.json` | Example reference for `bundle.json`. |
48
+ | `sample-localization.json` | Example reference for `localization.json`. |
49
+
50
+ ### Documentation
51
+
52
+ | Doc | Read when… |
53
+ |-----------------------------------------------------------|---------------------------------------------------------|
54
+ | **[ICan-Widget-Development-Guide.md](./ICan-Widget-Development-Guide.md)** | …you’re fetching data, adding a settings panel, using roles/localisation, or need multi-instance state patterns. |
55
+ | **[ICan-Widget-Theming-Guide.md](./ICan-Widget-Theming-Guide.md)** | …you’re writing CSS — full reference of `--ican-*` vars across the four themes. |
56
+ | **[ICan-Customizing-Components.md](./ICan-Customizing-Components.md)** | …you’re passing `style` / `className` to ICan components. |
57
+ | **[ICan-Widget-Styling-Patterns.md](./ICan-Widget-Styling-Patterns.md)** | …you’re deciding how to approach CSS isolation, class name collisions, or glass theme support. |
58
+
59
+ ---
60
+
61
+ ## The Three Customization Patterns
62
+
63
+ 1. **ICan component + your `style` prop** — quick inline overrides:
64
+ `<Card style={{ background: 'var(--ican-accent-dim)' }}>`
65
+ 2. **Build your own elements** — raw `<div>` + SCSS using ICan CSS vars.
66
+ 3. **ICan component + `className` AND `style`** — `className` for structural rules
67
+ in SCSS, `style` for one-off tweaks.
68
+
69
+ See [`ICan-Customizing-Components.md`](./ICan-Customizing-Components.md) for the
70
+ full breakdown including merge order and when to use which layer.
71
+
72
+ ---
73
+
74
+ ## Theming Quick Rules
75
+
76
+ - **Never hardcode a color.** Always go through `var(--ican-*)` so all four
77
+ themes (Dark / Light / Glass-Dark / Glass-Light) work.
78
+ - For glass-aware surfaces use `backdrop-filter: var(--ican-backdrop-filter)` —
79
+ it no-ops on Dark/Light but frosts on glass themes.
80
+ - `--ican-card-bg`, `--ican-primary-text`, `--ican-border`, `--ican-accent`,
81
+ `--ican-success`, `--ican-error`, `--ican-warning` cover ~90 % of widgets.
82
+
83
+ Full variable reference and four-theme matrix:
84
+ [`ICan-Widget-Theming-Guide.md`](./ICan-Widget-Theming-Guide.md).
85
+
86
+ ---
87
+
88
+ ## Common Tasks
89
+
90
+ **Add a new ICan component to your widget:**
91
+ ```tsx
92
+ import { Card, Button, StatCard } from 'ican/components';
93
+ ```
94
+
95
+ **Register the widget so the portal picks it up:**
96
+ ```ts
97
+ registerWidget({
98
+ id: 'your-widget-id',
99
+ widget: YourComponent,
100
+ configs: { layout: { w: 6, h: 5, minW: 4, minH: 4 } },
101
+ });
102
+ ```
103
+
104
+ **Read context provided by the portal (auth, theme, etc.):**
105
+ ```tsx
106
+ const Widget: React.FC<{ icanContext?: IContextProvider }> = ({ icanContext }) => {
107
+ // icanContext.executeAction('Model', 'Query', {...}) etc.
108
+ };
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Troubleshooting
114
+
115
+ | Symptom | Likely cause |
116
+ |----------------------------------------------------|---------------------------------------------------------------------------|
117
+ | Widget loads in dev but not in portal | Make sure you uploaded the **latest `dist/main.js`** + matching `bundle.json`. |
118
+ | `style={{...}}` works in dev but not in portal | Should never happen now — file an issue if it does. |
119
+ | Hardcoded colors look wrong in another theme | Replace with `var(--ican-*)` per the theming guide. |
120
+ | TypeScript errors importing from `ican/components` | Run `npm install` to refresh — types come from `ican.d.ts`. |
121
+
122
+ ---
123
+
124
+ ## Need help?
125
+
126
+ Re-run `npx infaira-canvas init` to scaffold a fresh widget, or read the docs
127
+ linked above. Both MDs are also bundled with every freshly initialised widget.
@@ -0,0 +1,468 @@
1
+ declare module "widget-designer/components" {
2
+
3
+
4
+ export type IWidgetMode = 'preview' | 'live' | 'thumbnail';
5
+ export type PropertyPanelType = 'general' | 'properties' | 'fields' | 'templates';
6
+
7
+ export interface IWidgetProps {
8
+ description?: string;
9
+ name?: string;
10
+ height?: number;
11
+ }
12
+
13
+
14
+ export interface IWidgetTemplateProps<T = any> {
15
+ icanContext?: IContextProvider;
16
+ designer: IWDDesignModeProps;
17
+ uiProps?: T;
18
+ initialSource: string;
19
+ updateWidgetHeight?: (height: number) => void
20
+ }
21
+ export type TemplateRenderer = (props: IWidgetTemplateProps) => JSX.Element;
22
+ export interface ILayoutProps {
23
+ w?: number,
24
+ h?: number,
25
+ minW?: number,
26
+ minH?: number,
27
+ maxH?: number,
28
+ maxW?: number
29
+ }
30
+ export type TargetDataStructure = 'value' | 'dictionary' | 'value-array' | 'dictionary-array';
31
+
32
+ export interface IWidgetTemplate {
33
+ template: TemplateRenderer;
34
+ toolbar?: TemplateRenderer;
35
+ id: string;
36
+ name: string,
37
+ description: string;
38
+ icon?: IconProp | JSX.Element,
39
+ canDragDropFields?: boolean,
40
+ layout?: ILayoutProps,
41
+ complexity?: 'easy' | 'medium' | 'hard' | 'advanced',
42
+ preLoader?: IWidgetPreloader,
43
+ expectedSchema?: TargetDataStructure,
44
+ autoHieght?: boolean,
45
+ getHeight?: () => number,
46
+ defaultProps?: any,
47
+ key?: string // for user templates
48
+ isTemplate?: boolean,
49
+ moduleId?: string
50
+ }
51
+ export type IUIPropertyDefinitionMap = {
52
+ [key: string]: (
53
+ IUIPropertyDefinition<IBaseProperty>
54
+ | { [subKey: string]: IUIPropertyDefinition<IBaseProperty> }
55
+ )
56
+ };
57
+
58
+ export type SourceBrowser = (icanContext: IContextProvider, template: IWidgetTemplate, onCancel: () => void, onSelected: (source: IModelPreview | IActionPreview) => Promise<boolean>) => React.ReactElement;
59
+
60
+ export interface IErrorProps {
61
+ id: string,
62
+ error: string,
63
+ type: 'property' | 'general',
64
+ containerId?: string,
65
+ fieldId?: string,
66
+ fieldClassName?: string,
67
+ highLightOnAdd?: boolean
68
+ }
69
+ export interface IWDDesignModeProps {
70
+ mode: IWidgetMode;
71
+ widgetName: string;
72
+ selectedPanel?: PropertyPanelType
73
+ onSelect: (ref: any, props: any, data: any, id?: string) => void;
74
+ selectedItem: any;
75
+ setUIProps: (uiProps: any) => void;
76
+ setWidgetProperties: (props: IWidgetProps) => void;
77
+ sourceBrowser: SourceBrowser;
78
+ addErrors: (errors: IErrorProps[]) => void
79
+ removeErrors: (ids: string[]) => void
80
+ removeAllErrors: () => void
81
+ }
82
+
83
+
84
+ export type PropertyPath = string | number | Array<string | number>;
85
+
86
+
87
+ export interface IBaseProperty {
88
+ }
89
+
90
+ export interface IPropertyEditorProps<T> {
91
+ uiProps: T;
92
+ onChange: (newProps: T) => void;
93
+ onChangeAny: (path: PropertyPath, value: any) => void;
94
+ icanContext: IContextProvider,
95
+ data?: any
96
+ parentUIProps: any;
97
+ designer: IWDDesignModeProps;
98
+ }
99
+
100
+ export interface IUIPropertyDefinition<T> {
101
+ defaults: T;
102
+ render: (props: IPropertyEditorProps<T>, icanContext?: IContextProvider, data?: any, containerId?: string) => JSX.Element;
103
+ }
104
+ export interface ISelectableItemUIProp {
105
+ id: PropertyPath;
106
+ value: IBaseProperty;
107
+ definition: IUIPropertyDefinition<IBaseProperty>; //IUIPropertyDefinition<any?> ?
108
+ }
109
+
110
+ export interface ISelectableItemProps {
111
+ designer: IWDDesignModeProps;
112
+ props: ISelectableItemUIProp[];
113
+ style?: React.CSSProperties;
114
+ onChange?: (props: any) => void;
115
+ data?: any;
116
+ className?: string;
117
+ showAtRuntime?: boolean;
118
+ elementId?: string;
119
+ preventLosingFocusOnNextClick?: boolean;
120
+ }
121
+ export const SelectableItem: React.FunctionComponent<ISelectableItemProps>
122
+
123
+ export function SimpleTextProperty(title: string, props: string, showCategoryTitle?: boolean): IUIPropertyDefinition<string>
124
+
125
+ export function selectUIProps(props: any, propDefs: any, ...keys: string[]): ISelectableItemUIProp[]
126
+
127
+
128
+ export interface IWDFilterPanelProps {
129
+ designer: IWDDesignModeProps,
130
+ icanContext: IContextProvider,
131
+ uiProps?: any,
132
+ containerRef: React.MutableRefObject<any>
133
+ data: { [key: string]: any }
134
+ onChangeFilters: (data: any) => void,
135
+ onClearFilters: () => void,
136
+ onOpenPanel?: () => void,
137
+ onClosePanel?: () => void
138
+ }
139
+ export const WidgetDesignerFilterPanel: React.FunctionComponent<IWDFilterPanelProps>
140
+
141
+ export interface IWidgetWrapperWithSidebarProps {
142
+ className: string;
143
+ designer?: IWDDesignModeProps;
144
+ data: any;
145
+ uiProps: any;
146
+ propDef: IUIPropertyDefinitionMap;
147
+ defaultTitle?: string;
148
+ elementId?: string;
149
+ filterSection?: () => React.ReactElement;
150
+ containerRef?: any;
151
+ }
152
+ export const WidgetWrapperWithTitleBar: React.FunctionComponent<IWidgetWrapperWithSidebarProps>
153
+
154
+
155
+ export interface ISerializedUIField {
156
+ id: string
157
+ name: string
158
+ type: string;
159
+ props: any;
160
+ isInputField?: boolean,
161
+ metaType?: TCollectionAttributeMetaTypeName
162
+ }
163
+
164
+ export interface IUseDropArgs {
165
+ /**
166
+ * designer props
167
+ */
168
+ designer?: IWDDesignModeProps,
169
+ /**
170
+ * ui props from the widget
171
+ */
172
+ uiProps: any,
173
+ /**
174
+ * path for the dropable fields (in uiProps)
175
+ */
176
+ propPath?: PropertyPath,
177
+ /**
178
+ * callback on props change
179
+ */
180
+ onChangeFields?: (newFields: ISerializedUIField[]) => void,
181
+ /**
182
+ * all the data from data sources , form fields and filters and etc.
183
+ * sample structure
184
+ * {
185
+ * // if the widget has form fields
186
+ * form: {
187
+ * input1: value1,
188
+ * input2: value2,
189
+ * input3: value3,
190
+ * },
191
+ * // if the widget has a filter panel
192
+ * filters: {
193
+ * input1: value1,
194
+ * input2: value2,
195
+ * },
196
+ * // data from data sources
197
+ * data: {
198
+ * source1: <data from the source>
199
+ * }
200
+ * }
201
+ */
202
+ data?: { [key: string]: any }
203
+ /**
204
+ * key of the data
205
+ * default is `form`
206
+ *
207
+ * this will be used to get and set data
208
+ */
209
+ dataKey?: string
210
+ /**
211
+ * call back when the data is changed
212
+ */
213
+ onChangeData?: (data: { [key: string]: any }) => void,
214
+ /**
215
+ * ican context
216
+ */
217
+ icanContext: IContextProvider,
218
+ disabled?: boolean
219
+ }
220
+
221
+ export interface IFieldContainerProps extends IUseDropArgs { }
222
+ export const FieldContainer: React.FunctionComponent<IFieldContainerProps>
223
+
224
+ export interface IRedirectUrl {
225
+ url: string,
226
+ target?: string
227
+ }
228
+
229
+ interface IWidgetProps {
230
+ id: string,
231
+ key: string,
232
+ isTemplate: boolean,
233
+ sourceUrl: string
234
+ templateType?: string,
235
+ templateKey?: string
236
+ templateId?: string,
237
+ }
238
+ export interface IRedirectWidget {
239
+ widget: IWidgetProps,
240
+ params?: { [key: string]: any }
241
+ }
242
+
243
+ export interface ILucyAction {
244
+ modelName: string;
245
+ action: string;
246
+ disableParamters?: boolean,
247
+ params: { [key: string]: any };
248
+ dialogTitle?: string,
249
+ executionMsg?: string,
250
+ postExecution?: {
251
+ success?: {
252
+ msg?: string,
253
+ action?: IAction,
254
+ goToHome?: boolean,
255
+ buttonTitle?: string,
256
+ reloadWidget?: boolean
257
+ },
258
+ error?: {
259
+ msg?: string,
260
+ action?: IAction,
261
+ goToHome?: boolean,
262
+ buttonTitle?: string,
263
+ reloadWidget?: boolean
264
+
265
+ }
266
+ }
267
+ }
268
+
269
+ export type IActionType = 'open-url' | 'open-widget' | 'execute-action'
270
+ export interface IAction {
271
+ type: IActionType,
272
+ redirectUrl?: IRedirectUrl
273
+ redirectWidget?: IRedirectWidget;
274
+ lucyAction?: ILucyAction
275
+ }
276
+ export interface IActionButtonProps {
277
+ designer?: any
278
+ icanContext: IContextProvider
279
+ title: string,
280
+ action: IAction,
281
+ styles?: React.CSSProperties
282
+ icon?: string,
283
+ iconPosition?: 'left' | 'right'
284
+ iconStyles?: React.CSSProperties,
285
+ data?: any;
286
+ customParams?: any;
287
+ onReloadWidget: () => void;
288
+ }
289
+ export const ActionButton: React.FunctionComponent<IActionButtonProps>
290
+
291
+ export interface IActionRenderComponentProps {
292
+ designer: IWDDesignModeProps
293
+ icanContext: IContextProvider
294
+ action: IAction
295
+ data: any,
296
+ onClose: () => void,
297
+ onReloadWidget: () => void,
298
+ customParams?: any,
299
+ }
300
+
301
+ export const ActionRenderComponent: React.FC<IActionRenderComponentProps>
302
+
303
+ export function AutoCompleteTextProperty(title: string, props: string, options: () => string[]): IUIPropertyDefinition<string>
304
+ export function ActionProperty(title: string, props: IAction): IUIPropertyDefinition<IAction>
305
+ export type BoolObject = { [key: string]: boolean };
306
+ export function BooleanGroupProperty(title: string, props: BoolObject, labels: any): IUIPropertyDefinition<BoolObject>
307
+ export function BooleanProperty(title: string, props?: boolean, showCategoryTitle?: boolean): IUIPropertyDefinition<boolean>
308
+
309
+ export interface IButtonPropertyProps extends IBaseProperty {
310
+ label: string,
311
+ onClick: IAction,
312
+ color?: string,
313
+ backgroundColor?: string,
314
+ icon?: {
315
+ icon: string
316
+ position?: 'left' | 'right',
317
+ color?: string,
318
+ size?: string
319
+ }
320
+ }
321
+
322
+ export interface IActionEditorLinkSettings {
323
+ disableUrl?: boolean;
324
+ disableWidget?: boolean;
325
+ disableAction?: boolean;
326
+ disableActionParameters?: boolean;
327
+ }
328
+ export function ButtonProperty(props: IButtonPropertyProps, title?: string, actionEditorSettings?: IActionEditorLinkSettings): IUIPropertyDefinition<IButtonPropertyProps>
329
+
330
+ export interface IColorProperty extends IBaseProperty {
331
+ color: string;
332
+ }
333
+ export function ColorProperty(title: string, props: IColorProperty): IUIPropertyDefinition<IColorProperty>
334
+ export interface IBackgroundProps {
335
+ image?: {
336
+ image: string,
337
+ position?: string,
338
+ size?: string,
339
+ },
340
+ color?: string
341
+ }
342
+
343
+ export interface ITextProps {
344
+ color?: string,
345
+ fontSize?: string,
346
+ align?: string,
347
+ }
348
+ export interface IContainerPropertyProps extends IBaseProperty {
349
+ background?: IBackgroundProps
350
+ text?: ITextProps
351
+ onClick?: IAction
352
+ removeShadow?: boolean
353
+ }
354
+ export function ContainerProperty(defaultProps: IContainerPropertyProps, title?: string): IUIPropertyDefinition<IContainerPropertyProps>
355
+
356
+ export interface IDataSourceParameter {
357
+ type: string;
358
+ id: string;
359
+ description: string;
360
+ example: string;
361
+ value: string;
362
+ }
363
+
364
+ export interface IDataSource {
365
+ id: string;
366
+ model: string;
367
+ action: string;
368
+ parameters: IDataSourceParameter[];
369
+ description: string;
370
+ icon?: string;
371
+ schema?: any;
372
+ type: 'model-action' | 'custom-json' | 'model-collection' | 'external-database',
373
+ json?: any // custom json
374
+ name?: string // source name - used for custom json and queries(in future)
375
+ grouping?: any[]
376
+ filter?: any
377
+ modelKey?: string,
378
+ databaseId?: string
379
+ }
380
+ export interface IDataSourceBinding {
381
+ source: string;
382
+ path: string;
383
+ }
384
+
385
+ export function DataSourceProperty(title: string, source: IDataSourceBinding, sources: IDataSource[], context: IContextProvider, designer: IWDDesignModeProps, target: TargetDataStructure): IUIPropertyDefinition<IDataSourceBinding>
386
+
387
+ export interface IOptions {
388
+ showCategoryTitle?: boolean
389
+ data: Array<{ id: string, name: string }>,
390
+ selected?: string
391
+ }
392
+ export const DropdownProperty: (title: string, props: string, options: IOptions) => IUIPropertyDefinition<string>
393
+
394
+ export function HTMLCodeProperty(title: string, defaultProps: string): IUIPropertyDefinition<string>
395
+
396
+ export interface IIconProperty extends IBaseProperty {
397
+ icon: string,
398
+ color?: string
399
+ size?: string,
400
+ }
401
+ export function IconProperty(props: IIconProperty, title?: string): IUIPropertyDefinition<IIconProperty>
402
+
403
+ export function ModelProperty(title: string, source: string, sources: IDataSource[], context: IContextProvider, structure?: TargetDataStructure): IUIPropertyDefinition<string>
404
+
405
+ export function MultipleImagesProperty(title: string, props: string[], showCategoryTitle?: boolean): IUIPropertyDefinition<string[]>
406
+ export function RichTextProperty(title: string, props: string, showCategoryTitle?: boolean): IUIPropertyDefinition<string>
407
+ export function SimpleDateProperty(title: string, props: Date, showCategoryTitle?: boolean): IUIPropertyDefinition<Date>
408
+ export function SimpleNumberProperty(title: string, props: number): IUIPropertyDefinition<number>
409
+
410
+ export interface ITextFieldProperty extends IBaseProperty {
411
+ text: string;
412
+ fontSize: string;
413
+ textColor: string;
414
+ backgroundColor: string;
415
+ }
416
+
417
+ export interface ITextFieldPropertySettings {
418
+ isTextHidden?: boolean,
419
+ isBackgroundColorHidden?: boolean,
420
+ isFontSizeHidden?: boolean,
421
+ isTextColorVisible?: boolean,
422
+ useCustomColorPicker: boolean,
423
+ }
424
+
425
+ export function TextFieldProperty(title: string, props: ITextFieldProperty, settings?: ITextFieldPropertySettings): IUIPropertyDefinition<ITextFieldProperty>
426
+
427
+ export interface ITextProperty extends IBaseProperty {
428
+ text?: string,
429
+ color?: string
430
+ size?: string,
431
+ align?: string
432
+ }
433
+ export function TextProperty(defaultProps: ITextProperty, title?: string): IUIPropertyDefinition<ITextProperty>
434
+
435
+ export function VerticalAlignmentProperty(widgetProps: string): IUIPropertyDefinition<string>
436
+
437
+
438
+
439
+ export function extractContainerProperties(props: IContainerPropertyProps): {
440
+ styles: React.CSSProperties;
441
+ onClick: IAction;
442
+ }
443
+
444
+ export function extractTextProperties(props: ITextProperty, defaultText?: string): {
445
+ text: string;
446
+ styles: React.CSSProperties;
447
+ }
448
+
449
+ export function hasValue(value: any, allowZero?: boolean, allowNegative?: boolean): boolean
450
+ export function tryParseJSON(x: string, def?: any): any
451
+ export function generateUUID(): string
452
+
453
+ export type DebounceFunction = (...args: any[]) => void;
454
+ export type DebounceOptions = {
455
+ isImmediate: boolean,
456
+ }
457
+ export function debounce<F extends DebounceFunction>(func: F, waitMilliseconds?: number, options?: DebounceOptions): F
458
+ export const getUrlFriendlyString: (string: string, removeSlashes?: boolean) => string
459
+ export function loadInitialData(props: IWidgetTemplateProps): {
460
+ isLoading: boolean;
461
+ initialData: any;
462
+ }
463
+ export function extractContextData(props: IWidgetTemplateProps, initialData: any, currentContextData: any): {
464
+ [key: string]: any;
465
+ }
466
+ export function formatNumber(n: number | string, decimals?: number): string
467
+
468
+ }