Coverage for src / lexigram / admin / ui / columns / column / config.py: 39%

56 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-11 17:07 +0800

1""" 

2Column configuration methods for fluent API. 

3""" 

4 

5from __future__ import annotations 

6 

7from typing import Any, Self 

8 

9 

10class ColumnConfigMixin: 

11 """Mixin class containing all column configuration methods.""" 

12 

13 def sortable(self, sortable: bool = True) -> Self: 

14 """Make column sortable in the DataTable. 

15 

16 When enabled, clicking the column header will toggle between 

17 ascending and descending sort order. A sort indicator (↑/↓) 

18 will be displayed in the header. 

19 

20 Args: 

21 sortable: Whether to enable sorting (default: True) 

22 

23 Returns: 

24 Self for method chaining 

25 

26 Example: 

27 >>> # Enable sorting 

28 >>> TextColumn("name").sortable() 

29 >>> 

30 >>> # Disable sorting 

31 >>> TextColumn("id").sortable(False) 

32 >>> 

33 >>> # Chain with other methods 

34 >>> TextColumn("email").sortable().searchable().copyable() 

35 """ 

36 self._sortable = sortable 

37 return self 

38 

39 def searchable(self, searchable: bool = True) -> Self: 

40 """Include column in global search. 

41 

42 When enabled, this column's values will be included in the 

43 DataTable's global search functionality. 

44 

45 Args: 

46 searchable: Whether to include in search (default: True) 

47 

48 Returns: 

49 Self for method chaining 

50 

51 Example: 

52 >>> TextColumn("name").searchable() 

53 >>> TextColumn("email").searchable().sortable() 

54 """ 

55 self._searchable = searchable 

56 return self 

57 

58 def toggleable(self, toggleable: bool = True) -> Self: 

59 """Allow showing/hiding column in UI. 

60 

61 When enabled, users can toggle column visibility through 

62 the DataTable's column visibility controls. 

63 

64 Args: 

65 toggleable: Whether column can be toggled (default: True) 

66 

67 Returns: 

68 Self for method chaining 

69 

70 Example: 

71 >>> TextColumn("description").toggleable() 

72 >>> TextColumn("id").toggleable(False) # Always visible 

73 """ 

74 self._toggleable = toggleable 

75 return self 

76 

77 def copyable(self, copyable: bool = True) -> Self: 

78 """Add click-to-copy functionality. 

79 

80 When enabled, clicking the cell will copy its value to the 

81 clipboard. A visual indicator will show on hover. 

82 

83 Args: 

84 copyable: Whether to enable click-to-copy (default: True) 

85 

86 Returns: 

87 Self for method chaining 

88 

89 Example: 

90 >>> TextColumn("api_key").copyable() 

91 >>> TextColumn("email").copyable().searchable() 

92 """ 

93 self._copyable = copyable 

94 return self 

95 

96 def filterable(self, filter_instance: Any = True) -> Self: 

97 """Mark column as filterable with optional filter configuration. 

98 

99 Can accept either: 

100 - True (default): Mark as filterable with default filter behavior 

101 - Filter instance: Specific filter configuration (SelectFilter, RangeFilter, etc.) 

102 

103 Args: 

104 filter_instance: Boolean or Filter instance (SelectFilter, RangeFilter, ToggleFilter, MultiSelectFilter) 

105 

106 Returns: 

107 Self for method chaining 

108 

109 Example: 

110 >>> from lexigram.admin.ui.filters import SelectFilter, RangeFilter 

111 >>> 

112 >>> # Simple boolean 

113 >>> TextColumn("status").filterable() 

114 >>> 

115 >>> # With SelectFilter 

116 >>> BadgeColumn("species").filterable(SelectFilter( 

117 ... options={"dog": "Dogs", "cat": "Cats"}, 

118 ... label="Species" 

119 ... )) 

120 >>> 

121 >>> # With RangeFilter 

122 >>> DateColumn("birth_date").filterable(RangeFilter( 

123 ... label="Birth Date Range" 

124 ... )) 

125 """ 

126 if filter_instance is True: 

127 self._filterable = True 

128 self._filter_instance = None 

129 else: 

130 self._filterable = True 

131 self._filter_instance = filter_instance 

132 # Store filter instance with column name if not explicitly set 

133 if hasattr(filter_instance, "name"): 

134 # Check for "unnamed_field" (Field class default) or empty name 

135 current_name = filter_instance.name 

136 if not current_name or current_name == "unnamed_field": 

137 filter_instance.name = self.name # type: ignore[attr-defined] 

138 return self 

139 

140 def exportable(self, exportable: bool = True) -> Self: 

141 """Include column in data exports. 

142 

143 When enabled, this column will be included when exporting 

144 DataTable data to CSV, Excel, or other formats. 

145 

146 Args: 

147 exportable: Whether to include in exports (default: True) 

148 

149 Returns: 

150 Self for method chaining 

151 

152 Example: 

153 >>> TextColumn("name").exportable() 

154 >>> ImageColumn("avatar").exportable(False) # Skip images 

155 """ 

156 self._exportable = exportable 

157 return self 

158 

159 def limit(self, chars: int) -> Self: 

160 """Truncate text to specified character limit. 

161 

162 Text longer than the limit will be truncated with an ellipsis (...). 

163 

164 Args: 

165 chars: Maximum number of characters to display 

166 

167 Returns: 

168 Self for method chaining 

169 

170 Example: 

171 >>> TextColumn("description").limit(100) 

172 >>> TextColumn("email").limit(50).copyable() 

173 """ 

174 self._limit = chars 

175 return self 

176 

177 def wrap(self, wrap: bool = True) -> Self: 

178 """Enable word wrapping for long content. 

179 

180 When enabled, long text will wrap to multiple lines instead 

181 of being truncated or overflowing. 

182 

183 Args: 

184 wrap: Whether to enable word wrapping (default: True) 

185 

186 Returns: 

187 Self for method chaining 

188 

189 Example: 

190 >>> TextColumn("description").wrap() 

191 >>> TextColumn("notes").wrap().limit(200) 

192 """ 

193 self._wrap = wrap 

194 return self 

195 

196 def width(self, pixels: int | str) -> Self: 

197 """Set column width. 

198 

199 Accepts either a numeric value (treated as `rem` units) or a 

200 string with explicit units (e.g. '200px' or '10%'). 

201 

202 Args: 

203 pixels: Width in rem when numeric, or an explicit CSS width string 

204 

205 Returns: 

206 Self for method chaining 

207 

208 Example: 

209 >>> TextColumn("id").width(4) # 4rem 

210 >>> TextColumn("name").width('200px') # 200px 

211 """ 

212 self._width = pixels 

213 return self 

214 

215 def grow(self, grow: bool = True) -> Self: 

216 """Control whether this column is allowed to grow (fluid). 

217 

218 When grow is True (default), and no explicit width is set, the 

219 column will receive a Tailwind `w-full`/`min-w-0` treatment so 

220 it can expand to fill available space. When False, the column 

221 will be sized tightly to its content. 

222 """ 

223 self._grow = grow 

224 return self 

225 

226 def tooltip(self, text: str) -> Self: 

227 """Add tooltip to column header. 

228 

229 Displays helpful information when hovering over the column header. 

230 

231 Args: 

232 text: Tooltip text to display 

233 

234 Returns: 

235 Self for method chaining 

236 

237 Example: 

238 >>> TextColumn("api_key").tooltip("Click to copy API key") 

239 >>> DateColumn("created_at").tooltip("Account creation date") 

240 """ 

241 self._tooltip = text 

242 return self 

243 

244 def align_left(self) -> Self: 

245 """Align content to the left. 

246 

247 Returns: 

248 Self for method chaining 

249 

250 Example: 

251 >>> TextColumn("name").align_left() 

252 """ 

253 self._alignment = "left" 

254 return self 

255 

256 def align_center(self) -> Self: 

257 """Align content to the center. 

258 

259 Returns: 

260 Self for method chaining 

261 

262 Example: 

263 >>> TextColumn("status").align_center() 

264 """ 

265 self._alignment = "center" 

266 return self 

267 

268 def align_right(self) -> Self: 

269 """Align content to the right. 

270 

271 Commonly used for numeric columns. 

272 

273 Returns: 

274 Self for method chaining 

275 

276 Example: 

277 >>> CurrencyColumn("price").align_right() 

278 >>> TextColumn("count").align_right().sortable() 

279 """ 

280 self._alignment = "right" 

281 return self 

282 

283 def pinned(self, position: str = "left") -> Self: 

284 """Pin column to the side. 

285 

286 Pinned columns remain visible when horizontally scrolling 

287 the DataTable. 

288 

289 Args: 

290 position: 'left' or 'right' (default: 'left') 

291 

292 Returns: 

293 Self for method chaining 

294 """ 

295 self._pinned = position 

296 return self