qlik-script-editor
Version:
A React component library for Qlik Sense script editing with syntax highlighting, autocomplete, and theme support
1 lines • 36 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../node_modules/clsx/dist/clsx.mjs","../src/utils/index.ts","../src/components/QlikScriptEditor/QlikScriptEditor.tsx","../src/components/ThemeProvider/ThemeContext.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/components/ThemeProvider/useTheme.ts","../src/components/Button/Button.tsx","../src/components/Card/Card.tsx","../src/hooks/useCounter.ts","../src/hooks/useQlikScript.ts"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import { type ClassValue, clsx } from 'clsx'\n\n/**\n * Utility function to merge class names conditionally\n * Uses clsx for conditional class names and handles conflicts\n */\nexport function cn(...inputs: ClassValue[]): string {\n return clsx(inputs)\n}\n\n/**\n * Debounce function that delays the execution of a function\n * @param func - The function to debounce\n * @param wait - The delay in milliseconds\n * @returns The debounced function\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: NodeJS.Timeout | null = null\n\n return function executedFunction(...args: Parameters<T>) {\n const later = () => {\n timeout = null\n func(...args)\n }\n\n if (timeout) clearTimeout(timeout)\n timeout = setTimeout(later, wait)\n }\n}\n\n/**\n * Generate a unique ID string\n * @param prefix - Optional prefix for the ID\n * @returns A unique ID string\n */\nexport function generateId(prefix?: string): string {\n const id = Math.random().toString(36).substr(2, 9)\n return prefix ? `${prefix}-${id}` : id\n}\n\n/**\n * Check if a value is not null or undefined\n * @param value - The value to check\n * @returns True if the value is not null or undefined\n */\nexport function isNotNullish<T>(value: T | null | undefined): value is T {\n return value !== null && value !== undefined\n}\n\n/**\n * Omit keys from an object\n * @param obj - The object to omit keys from\n * @param keys - The keys to omit\n * @returns A new object without the specified keys\n */\nexport function omit<T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n keys: K[]\n): Omit<T, K> {\n const result = { ...obj }\n keys.forEach((key) => delete result[key])\n return result\n}\n\n/**\n * Pick keys from an object\n * @param obj - The object to pick keys from\n * @param keys - The keys to pick\n * @returns A new object with only the specified keys\n */\nexport function pick<T extends Record<string, unknown>, K extends keyof T>(\n obj: T,\n keys: K[]\n): Pick<T, K> {\n const result = {} as Pick<T, K>\n keys.forEach((key) => {\n if (key in obj) {\n result[key] = obj[key]\n }\n })\n return result\n}","import React, { useRef, useEffect, useState, useCallback } from 'react'\nimport { cn } from '@/utils'\n\nexport interface QlikScriptEditorProps {\n /**\n * Initial script content\n */\n initialScript?: string\n \n /**\n * Callback when script content changes\n */\n onChange?: (value: string) => void\n \n /**\n * Available Qlik variables for autocomplete\n */\n variables?: string[]\n \n /**\n * Editor theme\n */\n theme?: 'light' | 'dark'\n \n /**\n * Editor height\n */\n height?: string\n \n /**\n * Editor width\n */\n width?: string\n \n /**\n * Whether the editor is read-only\n */\n readOnly?: boolean\n \n /**\n * Additional CSS class\n */\n className?: string\n \n /**\n * Placeholder text when editor is empty\n */\n placeholder?: string\n}\n\n/**\n * A Qlik Script Editor component with syntax highlighting and autocomplete\n */\nexport const QlikScriptEditor: React.FC<QlikScriptEditorProps> = ({\n initialScript = '',\n onChange,\n variables = [],\n theme = 'light',\n height = '400px',\n width = '100%',\n readOnly = false,\n className,\n placeholder = 'Enter your Qlik script here...'\n}) => {\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n const [script, setScript] = useState(initialScript)\n const [suggestions, setSuggestions] = useState<string[]>([])\n const [showSuggestions, setShowSuggestions] = useState(false)\n const [cursorPosition, setCursorPosition] = useState(0)\n\n // Qlik keywords for syntax highlighting\n const qlikKeywords = [\n 'LOAD', 'FROM', 'SELECT', 'WHERE', 'GROUP BY', 'ORDER BY', 'LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN',\n 'OUTER JOIN', 'CONCATENATE', 'MAPPING', 'QUALIFY', 'UNQUALIFY', 'DROP', 'STORE', 'LET', 'SET',\n 'CALL', 'SUB', 'END SUB', 'FOR', 'NEXT', 'DO', 'LOOP', 'WHILE', 'WEND', 'IF', 'THEN', 'ELSE',\n 'ELSEIF', 'END IF', 'SWITCH', 'CASE', 'DEFAULT', 'END SWITCH', 'EXIT SCRIPT', 'TRACE'\n ]\n\n // Qlik functions for autocomplete\n const qlikFunctions = [\n 'Sum', 'Count', 'Avg', 'Min', 'Max', 'Date', 'Today', 'Now', 'Year', 'Month', 'Day',\n 'Hour', 'Minute', 'Second', 'If', 'Alt', 'Match', 'ApplyMap', 'Lookup', 'Peek', 'Previous',\n 'Exists', 'FieldValue', 'FieldIndex', 'NoOfRows', 'RowNo', 'RecNo', 'Len', 'Left', 'Right',\n 'Mid', 'Trim', 'Upper', 'Lower', 'Replace', 'SubField', 'PurgeChar', 'KeepChar', 'Chr', 'Ord'\n ]\n\n const handleScriptChange = useCallback((value: string) => {\n setScript(value)\n onChange?.(value)\n }, [onChange])\n\n const handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const value = e.target.value\n const position = e.target.selectionStart || 0\n \n handleScriptChange(value)\n setCursorPosition(position)\n \n // Simple autocomplete logic\n const currentWord = getCurrentWord(value, position)\n if (currentWord.length > 0) {\n const matchingSuggestions = [\n ...qlikKeywords.filter(keyword => \n keyword.toLowerCase().startsWith(currentWord.toLowerCase())\n ),\n ...qlikFunctions.filter(func => \n func.toLowerCase().startsWith(currentWord.toLowerCase())\n ),\n ...variables.filter(variable => \n variable.toLowerCase().startsWith(currentWord.toLowerCase())\n )\n ].slice(0, 10)\n \n setSuggestions(matchingSuggestions)\n setShowSuggestions(matchingSuggestions.length > 0)\n } else {\n setShowSuggestions(false)\n }\n }\n\n const getCurrentWord = (text: string, position: number): string => {\n const beforeCursor = text.slice(0, position)\n const afterCursor = text.slice(position)\n \n const wordStart = beforeCursor.search(/\\w+$/)\n const wordEnd = afterCursor.search(/\\W/)\n \n if (wordStart === -1) return ''\n \n const start = wordStart\n const end = position + (wordEnd === -1 ? afterCursor.length : wordEnd)\n \n return text.slice(start, end)\n }\n\n const applySuggestion = (suggestion: string) => {\n if (!textareaRef.current) return\n \n const textarea = textareaRef.current\n const currentWord = getCurrentWord(script, cursorPosition)\n const wordStart = script.slice(0, cursorPosition).lastIndexOf(currentWord)\n \n if (wordStart !== -1) {\n const newScript = \n script.slice(0, wordStart) + \n suggestion + \n script.slice(wordStart + currentWord.length)\n \n handleScriptChange(newScript)\n setShowSuggestions(false)\n \n // Focus back to textarea\n setTimeout(() => {\n textarea.focus()\n const newPosition = wordStart + suggestion.length\n textarea.setSelectionRange(newPosition, newPosition)\n }, 0)\n }\n }\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n // Handle Tab for indentation\n if (e.key === 'Tab') {\n e.preventDefault()\n const textarea = e.currentTarget\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n const value = textarea.value\n\n // Insert tab character\n const newValue = value.substring(0, start) + ' ' + value.substring(end)\n handleScriptChange(newValue)\n\n // Move cursor\n setTimeout(() => {\n textarea.selectionStart = textarea.selectionEnd = start + 2\n }, 0)\n }\n \n // Close suggestions on Escape\n if (e.key === 'Escape') {\n setShowSuggestions(false)\n }\n }\n\n useEffect(() => {\n if (initialScript !== script) {\n setScript(initialScript)\n }\n }, [initialScript, script])\n\n return (\n <div className={cn('qlik-script-editor', className)} style={{ width }}>\n <div \n className={cn(\n 'qlik-script-editor__container',\n `qlik-script-editor--${theme}`,\n {\n 'qlik-script-editor--readonly': readOnly\n }\n )}\n style={{ height }}\n >\n <textarea\n ref={textareaRef}\n value={script}\n onChange={handleTextareaChange}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n readOnly={readOnly}\n className=\"qlik-script-editor__textarea\"\n spellCheck={false}\n />\n \n {showSuggestions && suggestions.length > 0 && (\n <div className=\"qlik-script-editor__suggestions\">\n {suggestions.map((suggestion) => (\n <button\n key={suggestion}\n className=\"qlik-script-editor__suggestion\"\n onClick={() => applySuggestion(suggestion)}\n type=\"button\"\n >\n {suggestion}\n </button>\n ))}\n </div>\n )}\n </div>\n </div>\n )\n}","import { createContext } from 'react'\n\nexport type Theme = 'light' | 'dark' | 'system'\n\nexport interface ThemeContextType {\n theme: Theme\n actualTheme: 'light' | 'dark'\n setTheme: (theme: Theme) => void\n}\n\nexport const ThemeContext = createContext<ThemeContextType | undefined>(undefined)","import React, { useEffect, useState } from 'react'\nimport { Theme, ThemeContext } from './ThemeContext'\n\ninterface ThemeProviderProps {\n /**\n * Default theme\n */\n defaultTheme?: Theme\n \n /**\n * Local storage key for theme persistence\n */\n storageKey?: string\n \n /**\n * Children components\n */\n children: React.ReactNode\n}\n\n/**\n * Theme provider for managing light/dark theme state\n */\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({\n defaultTheme = 'system',\n storageKey = 'qlik-script-editor-theme',\n children\n}) => {\n const [theme, setThemeState] = useState<Theme>(defaultTheme)\n const [actualTheme, setActualTheme] = useState<'light' | 'dark'>('light')\n\n useEffect(() => {\n // Load theme from localStorage\n const stored = localStorage.getItem(storageKey) as Theme\n if (stored) {\n setThemeState(stored)\n }\n }, [storageKey])\n\n useEffect(() => {\n // Update actual theme based on theme setting\n if (theme === 'system') {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n setActualTheme(mediaQuery.matches ? 'dark' : 'light')\n \n const handleChange = (e: MediaQueryListEvent) => {\n setActualTheme(e.matches ? 'dark' : 'light')\n }\n \n mediaQuery.addEventListener('change', handleChange)\n return () => mediaQuery.removeEventListener('change', handleChange)\n } else {\n setActualTheme(theme)\n }\n }, [theme])\n\n const setTheme = (newTheme: Theme) => {\n setThemeState(newTheme)\n localStorage.setItem(storageKey, newTheme)\n }\n\n const value = {\n theme,\n actualTheme,\n setTheme\n }\n\n return (\n <ThemeContext.Provider value={value}>\n <div className={`qlik-script-editor-theme qlik-script-editor-theme--${actualTheme}`}>\n {children}\n </div>\n </ThemeContext.Provider>\n )\n}\n\n","import { useContext } from 'react'\nimport { ThemeContext } from './ThemeContext'\n\n/**\n * Hook to access theme context\n */\nexport const useTheme = () => {\n const context = useContext(ThemeContext)\n if (!context) {\n throw new Error('useTheme must be used within a ThemeProvider')\n }\n return context\n}","import React from 'react'\nimport { cn } from '@/utils'\nimport './Button.css'\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * The button variant\n * @default 'primary'\n */\n variant?: 'primary' | 'secondary' | 'outline' | 'ghost'\n \n /**\n * The button size\n * @default 'md'\n */\n size?: 'sm' | 'md' | 'lg'\n \n /**\n * Whether the button is in a loading state\n * @default false\n */\n loading?: boolean\n \n /**\n * Icon to display before the button text\n */\n leftIcon?: React.ReactNode\n \n /**\n * Icon to display after the button text\n */\n rightIcon?: React.ReactNode\n \n /**\n * Whether the button should take the full width of its container\n * @default false\n */\n fullWidth?: boolean\n \n /**\n * The button content\n */\n children?: React.ReactNode\n}\n\n/**\n * A versatile button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and accessibility features.\n */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = 'primary',\n size = 'md',\n loading = false,\n leftIcon,\n rightIcon,\n fullWidth = false,\n className,\n disabled,\n children,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading\n\n return (\n <button\n ref={ref}\n className={cn(\n 'rc-button',\n `rc-button--${variant}`,\n `rc-button--${size}`,\n {\n 'rc-button--loading': loading,\n 'rc-button--full-width': fullWidth,\n },\n className\n )}\n disabled={isDisabled}\n {...props}\n >\n {loading && (\n <span className=\"rc-button__spinner\" aria-hidden=\"true\">\n <svg\n className=\"rc-button__spinner-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <circle\n className=\"rc-button__spinner-circle\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"rc-button__spinner-path\"\n fill=\"currentColor\"\n d=\"m12 2a10 10 0 0 1 10 10h-4a6 6 0 0 0-6-6V2z\"\n />\n </svg>\n </span>\n )}\n \n {leftIcon && !loading && (\n <span className=\"rc-button__icon rc-button__icon--left\" aria-hidden=\"true\">\n {leftIcon}\n </span>\n )}\n \n {children && (\n <span className=\"rc-button__content\">\n {children}\n </span>\n )}\n \n {rightIcon && !loading && (\n <span className=\"rc-button__icon rc-button__icon--right\" aria-hidden=\"true\">\n {rightIcon}\n </span>\n )}\n </button>\n )\n }\n)\n\nButton.displayName = 'Button'","import React from 'react'\n\nexport interface CardProps {\n /**\n * Card content\n */\n children: React.ReactNode\n /**\n * Card title\n */\n title?: string\n /**\n * Card padding\n */\n padding?: 'sm' | 'md' | 'lg'\n /**\n * Enable shadow\n */\n shadow?: boolean\n /**\n * Additional CSS classes\n */\n className?: string\n}\n\n/**\n * A flexible card component for containing content\n */\nexport const Card: React.FC<CardProps> = ({\n children,\n title,\n padding = 'md',\n shadow = true,\n className = '',\n}) => {\n const baseClasses = 'bg-white rounded-lg border border-gray-200'\n \n const paddingClasses = {\n sm: 'p-4',\n md: 'p-6',\n lg: 'p-8'\n }\n \n const shadowClasses = shadow ? 'shadow-sm hover:shadow-md transition-shadow' : ''\n \n const classes = [\n baseClasses,\n paddingClasses[padding],\n shadowClasses,\n className\n ].filter(Boolean).join(' ')\n\n return (\n <div className={classes}>\n {title && (\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-medium text-gray-900\">{title}</h3>\n </div>\n )}\n {children}\n </div>\n )\n}","import { useState, useCallback } from 'react'\n\nexport interface UseCounterOptions {\n /**\n * The initial count value\n * @default 0\n */\n initialValue?: number\n \n /**\n * The minimum allowed value\n */\n min?: number\n \n /**\n * The maximum allowed value\n */\n max?: number\n \n /**\n * The step value for increment/decrement\n * @default 1\n */\n step?: number\n}\n\nexport interface UseCounterReturn {\n /** The current count value */\n count: number\n \n /** Increment the count by the step value */\n increment: () => void\n \n /** Decrement the count by the step value */\n decrement: () => void\n \n /** Reset the count to the initial value */\n reset: () => void\n \n /** Set the count to a specific value */\n set: (value: number) => void\n \n /** Whether the count is at the minimum value */\n isAtMin: boolean\n \n /** Whether the count is at the maximum value */\n isAtMax: boolean\n}\n\n/**\n * A hook for managing counter state with optional min/max constraints\n * \n * @param options - Configuration options for the counter\n * @returns Counter state and controls\n * \n * @example\n * ```tsx\n * const { count, increment, decrement, reset } = useCounter({\n * initialValue: 0,\n * min: 0,\n * max: 10,\n * step: 1\n * })\n * ```\n */\nexport function useCounter(options: UseCounterOptions = {}): UseCounterReturn {\n const {\n initialValue = 0,\n min = -Infinity,\n max = Infinity,\n step = 1,\n } = options\n\n const [count, setCount] = useState(initialValue)\n\n const increment = useCallback(() => {\n setCount((prev) => Math.min(prev + step, max))\n }, [step, max])\n\n const decrement = useCallback(() => {\n setCount((prev) => Math.max(prev - step, min))\n }, [step, min])\n\n const reset = useCallback(() => {\n setCount(initialValue)\n }, [initialValue])\n\n const set = useCallback(\n (value: number) => {\n setCount(Math.max(min, Math.min(max, value)))\n },\n [min, max]\n )\n\n const isAtMin = count <= min\n const isAtMax = count >= max\n\n return {\n count,\n increment,\n decrement,\n reset,\n set,\n isAtMin,\n isAtMax,\n }\n}","import { useState, useCallback, useMemo } from 'react'\n\nexport interface UseQlikScriptOptions {\n /**\n * Initial script content\n */\n initialScript?: string\n \n /**\n * Callback when script content changes\n */\n onChange?: (script: string) => void\n \n /**\n * Available variables for validation\n */\n variables?: string[]\n \n /**\n * Enable syntax validation\n */\n enableValidation?: boolean\n}\n\nexport interface QlikScriptError {\n line: number\n column: number\n message: string\n type: 'syntax' | 'warning' | 'info'\n}\n\nexport interface UseQlikScriptReturn {\n /** Current script content */\n script: string\n \n /** Set script content */\n setScript: (script: string) => void\n \n /** Clear script content */\n clearScript: () => void\n \n /** Script validation errors */\n errors: QlikScriptError[]\n \n /** Whether script has any errors */\n hasErrors: boolean\n \n /** Format script with proper indentation */\n formatScript: () => void\n \n /** Get script statistics */\n getScriptStats: () => {\n lines: number\n characters: number\n words: number\n tables: number\n variables: string[]\n }\n}\n\n/**\n * Hook for managing Qlik script state and validation\n */\nexport function useQlikScript(options: UseQlikScriptOptions = {}): UseQlikScriptReturn {\n const {\n initialScript = '',\n onChange,\n variables = [],\n enableValidation = true\n } = options\n\n const [script, setScriptState] = useState(initialScript)\n\n const setScript = useCallback((newScript: string) => {\n setScriptState(newScript)\n onChange?.(newScript)\n }, [onChange])\n\n const clearScript = useCallback(() => {\n setScript('')\n }, [setScript])\n\n // Basic syntax validation\n const errors = useMemo((): QlikScriptError[] => {\n if (!enableValidation || !script.trim()) return []\n\n const errors: QlikScriptError[] = []\n const lines = script.split('\\n')\n\n lines.forEach((line, index) => {\n const trimmedLine = line.trim()\n if (!trimmedLine) return\n\n // Check for missing semicolons on certain statements\n if (\n trimmedLine.match(/^(LOAD|SELECT|LET|SET|STORE|DROP)\\s+/i) &&\n !trimmedLine.endsWith(';') &&\n !trimmedLine.endsWith(':') &&\n !trimmedLine.includes('FROM') // FROM statements might be multi-line\n ) {\n errors.push({\n line: index + 1,\n column: line.length,\n message: 'Missing semicolon',\n type: 'warning'\n })\n }\n\n // Check for unmatched parentheses\n const openParens = (line.match(/\\(/g) || []).length\n const closeParens = (line.match(/\\)/g) || []).length\n if (openParens !== closeParens) {\n errors.push({\n line: index + 1,\n column: line.indexOf('(') + 1,\n message: 'Unmatched parentheses',\n type: 'syntax'\n })\n }\n\n // Check for undefined variables\n const variableMatches = line.match(/\\$\\([^)]+\\)/g)\n if (variableMatches) {\n variableMatches.forEach(match => {\n const varName = match.slice(2, -1)\n if (!variables.includes(varName)) {\n errors.push({\n line: index + 1,\n column: line.indexOf(match) + 1,\n message: `Undefined variable: ${varName}`,\n type: 'warning'\n })\n }\n })\n }\n })\n\n return errors\n }, [script, variables, enableValidation])\n\n const hasErrors = useMemo(() => \n errors.some(error => error.type === 'syntax')\n , [errors])\n\n const formatScript = useCallback(() => {\n const lines = script.split('\\n')\n let indentLevel = 0\n const indentSize = 2\n\n const formattedLines = lines.map(line => {\n const trimmed = line.trim()\n if (!trimmed) return ''\n\n // Decrease indent for END statements\n if (trimmed.match(/^(END\\s+(IF|SUB|SWITCH))/i)) {\n indentLevel = Math.max(0, indentLevel - 1)\n }\n\n const indentedLine = ' '.repeat(indentLevel * indentSize) + trimmed\n\n // Increase indent for control structures\n if (\n trimmed.match(/^(IF\\s+|FOR\\s+|DO\\s*$|WHILE\\s+|SUB\\s+|SWITCH\\s+)/i) ||\n trimmed.match(/^(THEN\\s*$|ELSE\\s*$)/i)\n ) {\n indentLevel++\n }\n\n return indentedLine\n })\n\n setScript(formattedLines.join('\\n'))\n }, [script, setScript])\n\n const getScriptStats = useCallback(() => {\n const lines = script.split('\\n').filter(line => line.trim()).length\n const characters = script.length\n const words = script.split(/\\s+/).filter(word => word).length\n \n // Count LOAD/SELECT statements as tables\n const tableMatches = script.match(/^(LOAD|SELECT)\\s+/gmi) || []\n const tables = tableMatches.length\n \n // Extract variable definitions\n const variableMatches = script.match(/^(LET|SET)\\s+([^=\\s]+)/gmi) || []\n const scriptVariables = variableMatches.map(match => {\n const parts = match.split(/\\s+/)\n return parts[1] || ''\n }).filter(Boolean)\n\n return {\n lines,\n characters,\n words,\n tables,\n variables: scriptVariables\n }\n }, [script])\n\n return {\n script,\n setScript,\n clearScript,\n errors,\n hasErrors,\n formatScript,\n getScriptStats\n }\n}"],"names":["r","e","t","f","n","o","clsx","cn","inputs","debounce","func","wait","timeout","args","later","generateId","prefix","id","isNotNullish","value","omit","obj","keys","result","key","pick","QlikScriptEditor","initialScript","onChange","variables","theme","height","width","readOnly","className","placeholder","textareaRef","useRef","script","setScript","useState","suggestions","setSuggestions","showSuggestions","setShowSuggestions","cursorPosition","setCursorPosition","qlikKeywords","qlikFunctions","handleScriptChange","useCallback","handleTextareaChange","position","currentWord","getCurrentWord","matchingSuggestions","keyword","variable","text","beforeCursor","afterCursor","wordStart","wordEnd","start","end","applySuggestion","suggestion","textarea","newScript","newPosition","handleKeyDown","newValue","useEffect","jsx","jsxs","ThemeContext","createContext","ThemeProvider","defaultTheme","storageKey","children","setThemeState","actualTheme","setActualTheme","stored","mediaQuery","handleChange","newTheme","useTheme","context","useContext","Button","React","variant","size","loading","leftIcon","rightIcon","fullWidth","disabled","props","ref","isDisabled","Card","title","padding","shadow","baseClasses","paddingClasses","shadowClasses","classes","useCounter","options","initialValue","min","max","step","count","setCount","increment","prev","decrement","reset","set","isAtMin","isAtMax","useQlikScript","enableValidation","setScriptState","clearScript","errors","useMemo","line","index","trimmedLine","openParens","closeParens","variableMatches","match","varName","hasErrors","error","formatScript","lines","indentLevel","indentSize","formattedLines","trimmed","indentedLine","getScriptStats","characters","words","word","tables","scriptVariables"],"mappings":"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","x_google_ignoreList":[0]}